Вызов функции C ++, имеющей std :: vectors в качестве входных и выходных параметров от Julia

Я новичок в Джулии, и я пытаюсь получить доступ к моему коду C ++ от Джулии. Точнее, я пытаюсь вызвать функцию C ++ от Джулии, используя Cxx. Входными и выходными параметрами функции C ++ являются std :: vectors, смотрите функцию compute_sum в следующем примере:

#include <vector>
#include <iostream>

std::vector< int > compute_sum( const std::vector< std::vector<int> >& input )
{
std::vector< int > resut( input.size() , 0 );
for ( std::size_t i = 0 ; i != input.size() ; ++i )
{
for ( std::size_t j = 0 ; j != input[i].size() ; ++j )
{
resut[i] += input[i][j];
}
}
return resut;
}

void simple_function( int i )
{
std::cout << "The numbers is : " << i << std::endl;
}

Предполагая, что эта функция хранится как code.cpp, я компилирую ее в общий объект code.so, используя:

 g++ -shared -fPIC code.cpp -o code.so

и в результате я получаю файл code.so

Имея это, я запускаю Джулию в той же папке, что и code.so. Моя версия Юлии — 0.6.2. Затем я импортирую Cxx и файл code.so, используя:

julia> using Cxx
julia> const path_to_lib = pwd()
julia> addHeaderDir(path_to_lib, kind=C_System)
julia> Libdl.dlopen(path_to_lib * "/code.so", Libdl.RTLD_GLOBAL)
Ptr{Void} @0x00000000044bda30
julia> cxxinclude("code.cpp")

Чтобы проверить, успешен ли процесс, я вызываю функцию simple_function и получаю правильные результаты:

julia> @cxx simple_function(1234)
The numbers is : 1234

Затем я хочу вызвать функцию compute_sum. Для этого мне нужно как-то создать или преобразовать вектор Джулии в C ++ std :: vector< std :: vector>. Я пытаюсь следующее:

julia> cxx" std::vector< std::vector<int> > a;"true
julia> icxx" a.push_back( std::vector<int>(1,2) ); "julia> icxx" a.push_back( std::vector<int>(1,3) ); "julia> icxx" a.push_back( std::vector<int>(1,4) ); "julia> icxx" a.size(); "0x0000000000000003

Поэтому я предполагаю, что вектор создан правильно. Затем я пытаюсь вызвать функцию с ним, но мне не удается:

julia> @cxx compute_sum(a)
ERROR: UndefVarError: a not defined
julia> @cxx compute_sum("a")
ERROR: Got bad type information while compiling Cxx.CppNNS{Tuple{:compute_sum}} (got String for argument 1)
julia> icxx " compute_sum(a);"ERROR: syntax: extra token """ after end of expression

Может ли кто-нибудь помочь мне, пожалуйста, со следующими вопросами:

  1. Как вызвать функцию compute_sum у Юлии? Я счастлив использовать любую технику (не обязательно Cxx), которая работает и работает достаточно быстро.
  2. Как преобразовать результат compute_sum в массив Julia?

Большое спасибо!

Pawel

1

Решение

Хитрость заключается в том, чтобы использовать icxx строковый макрос, это позволяет интерполировать переменные Джулии, используя $, Вот полный пример:

using Cxx

cxx"""#include <iostream>
#include <vector>

std::vector<int> compute_sum(const std::vector<std::vector<int>> &input)
{
std::vector<int> result(input.size(), 0);
for (std::size_t i = 0; i != input.size(); ++i)
{
for (std::size_t j = 0; j != input[i].size(); ++j) // corrected to ++j here
{
result[i] += input[i][j];
}
}
return result;
}
"""
cxx_v = icxx"std::vector<std::vector<int>>{{1,2},{1,2,3}};"println("Input vectors:")
for v in cxx_v
println("  ", collect(v))
end

cxx_sum = icxx"compute_sum($cxx_v);"println("Cxx sums: $(collect(cxx_sum))")

Запуск этого в Джулии должен напечатать:

Input vectors:
Int32[1, 2]
Int32[1, 2, 3]
Cxx sums: Int32[3, 6]

Чтобы сделать это с помощью общей библиотеки, создайте vector.hpp как это:

#include <vector>
std::vector<int> compute_sum(const std::vector<std::vector<int>> &input);

vector.cpp:

#include "vector.hpp"
std::vector<int> compute_sum(const std::vector<std::vector<int>> &input)
{
// same as before
}

Обобщение:

g++ -shared -fPIC -o libvector.so vector.cpp

У Юлии:

using Cxx

const path_to_lib = pwd()
addHeaderDir(path_to_lib, kind=C_System)
Libdl.dlopen(joinpath(path_to_lib, "libvector"), Libdl.RTLD_GLOBAL)
cxxinclude("vector.hpp")

cxx_v = icxx"std::vector<std::vector<int>>{{1,2},{1,2,3}};"println("Input vectors:")
for v in cxx_v
println("  ", collect(v))
end

cxx_sum = icxx"compute_sum($cxx_v);"println("Cxx sums: $(collect(cxx_sum))")
2

Другие решения

Потому что вы готовы работать на Юлию массивы, Я предполагаю, что вы хотели бы работать над матрицы, то есть, массивы, имеющие постоянную длину в размерах. По этой причине я бы посоветовал вам не использовать vectorс vectors, но вместо этого используйте просто vectors. Тогда вы должны помнить, что Юлия использует столбцам массивы, тогда как в C / C ++ расположение памяти рядные основнымы.

Ниже вы можете найти шаблонную версию вашего кода, используя итераторы. Таким образом, вы можете скомпилировать compute_sum для использования в C ++ с вашим любимым std::vectors. Или же вы можете попросить ваш компилятор сгенерировать соответствующий код с указателями, чтобы иметь возможность использовать его с другими языками, такими как Julia.

#include <cstdint>
#include <iterator>
#include <vector>

template <class RandomIt, class OutputIt>
OutputIt compute_sum(const std::uint64_t nrows, RandomIt xbegin, RandomIt xend,
OutputIt rbegin) {
const std::size_t ncols{std::distance(xbegin, xend) / nrows};
typename std::iterator_traits<OutputIt>::value_type sum{0};
for (std::size_t row = 0; row < nrows; row++) {
for (std::size_t col = 0; col < ncols; col++)
sum += xbegin[col * nrows + row];

*rbegin++ = sum;
sum = 0;
}
return rbegin;
}

/* you can use the above code in your C++ applications as follows */
// int main() {
//   std::vector<int> matrix{1, 2, 3, 4, 5,
//                           6, 7, 8, 9}; /* 3x3 matrix in column-major */
//   std::vector<int> result(3);
//   compute_sum(3, std::begin(matrix), std::end(matrix), std::begin(result));
//   return 0;
// }

/* or, ask your compiler to generate code with C linkage (no name mangling) */
extern "C" {
void compute_sum(const std::uint64_t m /* use fixed-size integers */,
const std::uint64_t n /* use fixed-size integers */,
const std::int64_t *xbegin /* use fixed-size integers */,
std::int64_t *rbegin /* use fixed-size integers */) {
compute_sum(m, xbegin, xbegin + m * n, rbegin);
}
}

затем скомпилируйте код как обычно:

g++ -Wall -std=c++11 -O3 -fPIC -shared code.cpp -o code.so

Затем используйте Юлию возможности чтобы вызвать скомпилированный код C:

const libhandle = Libdl.dlopen(joinpath(pwd(), "code.so"))
const funhandle = Libdl.dlsym(libhandle, :compute_sum)

function compute_sum(A::Matrix{Int64})
result = Vector{Int64}(size(A, 1))
ccall(funhandle, Void, (UInt64, UInt64, Ref{Int64}, Ref{Int64}),
size(A, 1), size(A, 2), A, result)
return result
end

result = compute_sum(ones(Int64, 5, 6)) # prints Int64[6, 6, 6, 6, 6]

Надеюсь, это поможет. Ура!

3

Я хотел бы поблагодарить Арду Айтекин и Барта Янссенса за их большую помощь и предложения. Оба решения работают отлично, и я хотел бы отметить их как ответ на мой вопрос, но, кажется, я могу отметить только один ответ …
В следующие дни я проведу тест сравнения скорости, чтобы увидеть, является ли одно решение, основанное на чистом интерфейсе C, более быстрым или нет, чем решение, использующее Cxx. Я обновлю вас, как только это будет готово.

0
По вопросам рекламы [email protected]