std::array или std::vector из указателя
У меня есть массив данных в массиве C++/CLI, который я могу передать нативной функции, используя pin_ptr<T>
пока проблем нет. Теперь, однако, мне нужно передать массив в функцию C++/STL, которая ожидает контейнер, такой как std::array
или же std::vector
,
Самый простой способ сделать это (что я сделал первым), это скопировать элемент за элементом.
Второй самый простой способ - позвонить std::copy()
Смотрите ответ на этот вопрос: конвертируйте System::array в std:: vector.
Однако я хочу пропустить весь шаг копирования и просто использовать указатель. Видеть как std::array
требует аргумента шаблона, чтобы определить его длину, я не могу создать его во время выполнения (но, пожалуйста, исправьте меня, если я ошибаюсь). Есть ли способ создать вектор или контейнер STL другого типа без ненужного копирования данных?
2 ответа
Начиная с C++20 существует способ использования контейнеров C++ с управляемыми массивами в C++/cli, который позволяет избежать копирования данных: std::span
В C++20 можно использовать std::span
чтобы обернуть управляемый массив C++/cli. Затем его можно использовать со стандартными контейнерными алгоритмами.
К сожалению, Microsoft не поддерживает C++/cli за пределами C++17. Следовательно, нужно сначала передать указатель и длину функции в другом исходном файле, который скомпилирован с использованием C++ latest, при компиляции исходного файла вызывающего абонента с использованием более ранней версии C++17/cli. К счастью, ABI совместимы. Это легко настроить для каждого файла на странице свойств Visual Studio 2019.
Вот пример кода, который создает небольшой управляемый array<double>
затем вызывает функцию, которая обертывает управляемые данные с помощью std::span
затем сортирует с std::sort
// file1.cpp compile with /cli
#include <iostream>
using namespace System;
void sortme(double *p, int len);
int main()
{
array<double>^ v = gcnew array<double> {1.0, 3.0, 2.0, 4.0};
pin_ptr<double> pin=&v[0];
int len=v->Length;
sortme(pin, len);
for (int i = 0; i < len; i++)
std::cout << v[i] << "\n"; // prints sorted array
}
// file2.cpp compile with c++latest
#include <span>
#include <algorithm>
void sortme(double *p, int len)
{
std::span data_clr(p, len);
std::sort(data_clr.begin(), data_clr.end());
}
Нет, это невозможно сделать без копирования, в любом случае со стандартными контейнерами.
Если вы все еще в порядке с копированием, вы должны посмотреть на std::vector
конструктор, потому что я думаю, что самый простой способ будет сделать, например,
std::vector<T>(your_pointer, your_pointer + number_of_elements)
Если вы определенно хотите избежать копирования, то на самом деле не так сложно написать простую обертку вокруг указателя, включая простые итераторы, необходимые для итерации (я полагаю, это должен быть стандартный контейнер).
Просто для удовольствия, и, поскольку у меня было немного времени, я создал именно такую обертку. Включает индексирование и итераторы. Нет проверки границ.
См. https://gist.github.com/pileon/c21cfba496e6c352dd81
Пример программы, использующей его:
#include <iostream>
#include "pointer_container.h"
int main()
{
int a[20];
std::iota(a, a + 20, 0); // Initialize array
{
std::cout << "From array : ";
for (const auto item : a)
{
std::cout << item << ' ';
}
std::cout << '\n';
}
pointer_container<int> c(a, 20);
{
std::cout << "From container: ";
for (const auto item : c)
{
std::cout << item << ' ';
}
std::cout << '\n';
}
}
Ожидаемый результат от программы:
Из массива: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Из контейнера: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Поскольку std::array является просто оболочкой, вы можете привести обычный массив к указателю на std::array. Это не подходит для других контейнеров, конечно.
#include <array>
#include <iostream>
void test(std::array<int, 10>* pia)
{
std::cout << (*pia)[0] << std::endl;
}
int main()
{
int ix[10]{ 0 };
test((std::array<int, 10> *) ix);
}