Подтвердить что ты не робот

Вызов С++ (не C) из Common Lisp?

Мне интересно, есть ли способ вызвать код С++ из Common Lisp (желательно портативно, а если нет, желательно в SBCL, а если нет, то Clozure, CLisp или ECL).

С++ будет вызываться внутри циклов для численного вычисления, поэтому было бы неплохо, если бы звонки были быстрыми.

CFFI, похоже, не поддерживает это:

"Эта концепция может быть обобщена на Другие языки; в момент письменной форме, только поддержка CFFI C довольно полная, но поддержка С++ над которым работали".

(глава 4 руководства)

В руководстве SBCL также не упоминается С++; это на самом деле говорит

В этой главе описываются функции SBCL интерфейс к программам и библиотекам C (и, поскольку C-интерфейсы являются своего рода lingua franca мира Unix, другим программам и библиотекам в вообще.)

В С++-коде используется OO и перегрузка операторов, поэтому его действительно нужно скомпилировать с помощью g++.

И насколько я знаю, у меня может быть функция С++ main() и писать оболочки для функций C, но не наоборот - это правда?

В любом случае... Есть ли способ сделать это?

Спасибо!

4b9b3361

Ответ 1

О, подождите!

Кажется, что существует трюк, который я могу использовать!

Я пишу обертку в С++, объявляя функции-оболочки extern "C":

#include "lib.h"

extern "C" int lib_operate (int i, double *x) {
...
}

Заголовочный файл lib.h, который может быть вызван как из C, так и из С++, это:

#if __cplusplus
extern "C" {
#endif

int lib_operate (int i, double *x);

#if __cplusplus
}
#endif

Затем скомпилируйте с помощью:

g++ -c lib.cpp
gcc -c prog.c
gcc lib.o prog.o -lstdc++ -o prog

Кажется, это работает на примере игрушек!: -)

Итак, в Common Lisp я бы назвал оболочку после загрузки libstdС++.

В любом случае, спасибо за ваши ответы!

Ответ 2

После компиляции большинство функций С++ фактически сводятся к регулярным вызовам функций C. Из-за перегрузки функций и других функций компиляторы С++ используют name mangling, чтобы различать похожие функции. Учитывая полезность дампа объекта и достаточные знания о вашем компиляторе на С++, вы можете вызвать код С++ непосредственно из внешнего мира.

Сказав это, вам может быть проще написать C-совместимый уровень между Lisp и вашим кодом на С++. Вы сделали бы это с помощью extern "C" следующим образом:

extern "C" Foo *new_Foo(int x)
{
    return new Foo(x);
}

Это означает, что функция new_Foo() выполняет соглашение о вызове C, чтобы вы могли вызывать ее из внешних источников.

Ответ 3

Основным отличием при вызове функций С++ вместо функций C, помимо имени, являются "скрытые" функции, такие как this указатели, которые неявно передаются в функции-члены. Среда выполнения C ничего не знает об этих, неявных преобразованиях типов и других интересных функциях С++, поэтому, если вы намереваетесь вызывать С++ через интерфейс C, вам, возможно, придется подделать эти функции.

Предполагая, что вы можете удерживать хотя бы void * для объекта, который собираетесь называть, и требуемых данных, вы можете деградировать следующий вызов С++

matrix->multiply(avector);

для вызова C, если вы создаете функцию обертки C:

extern "C"
void matrix_multiply(void *cpp_matrix, void *cpp_vector) {
  reinterpret_cast<matrix_type *>(cpp_matrix)->multiply(reinterpret_cast<vector_type *>(cpp_vector);
}

Очевидно, что функция matrix_multiply будет находиться в исходном коде на С++ и скомпилирована как таковая, но она открывает интерфейс C во внешний мир. Пока вы можете взаимодействовать с непрозрачными указателями, вы в порядке с прокладками перевода выше.

По общему признанию, это не обязательно самое элегантное решение для такой проблемы, но я использовал его в прошлом в таких ситуациях, как ваш.

Другим вариантом было бы сделать вызовы С++ напрямую, рассматривая их как вызовы C с дополнительными параметрами и предоставляя всю необходимую информацию самостоятельно, но это очень быстро переводит вас в область кода, специфичного для компилятора. В принципе, вы все равно будете удерживать непрозрачные указатели на объекты С++, но вам придется выработать искаженное имя функции, которую вы хотите вызвать. После того, как у вас есть это имя функции, вам нужно будет указать этот указатель (который неявный в С++ и полу-имплицитный в приведенном выше примере) и правильные параметры, а затем вызвать функцию. Это может быть сделано, но, как уже упоминалось, вы глубоко вникаете в сферу компилятора и даже специфическое поведение для компилятора.

Ответ 4

В зависимости от вашего С++ ABI вашей оболочке (lib_operate выше) может потребоваться каким-то образом обработать любые исключения С++, которые могут возникнуть. Если ваш ABI выполняет обработку исключений табличного диска, необработанные исключения просто вызовут процесс (Lisp). Если вместо этого происходит динамическая регистрация, вы даже не заметите, что что-то пошло не так. В любом случае, это плохо.

Или, если у вас есть гарантия отсутствия броска для обернутого кода, вы можете игнорировать все это.

Ответ 5

Вы можете использовать cl-cxx, как писать pybind11 для python.

пример в c++ 'std> = c++ 14', скомпилированный как разделяемая библиотека:

#include <string>

#include "clcxx/clcxx.hpp"

class xx {
 public:
  xx(int xx, int yy) : y(yy), x(xx) {}
  std::string greet() { return "Hello, World"; }

  int y;
  int x;
};

std::string greet() { return "Hello, World"; }
int Int(int x) { return x + 100; }
float Float(float y) { return y + 100.34; }
auto gr(std::complex<float> x) { return x; }
std::string hi(char* s) { return std::string("hi, " + std::string(s)); }

void ref_class(xx& x) { x.y = 1000000; }

CLCXX_PACKAGE TEST(clcxx::Package& pack) {
  pack.defun("hi", &hi);
  pack.defun("test-int", &Int);
  pack.defun("greet", &greet);
  pack.defun("test-float", &Float);
  pack.defun("test-complex", &gr);
  pack.defun("ref-class", &ref_class);
  pack.defclass<xx, false>("xx")
      .member("y", &xx::y)
      .defmethod("greet-from-class", &xx::greet)
      .constructor<int, int>();
}

использование в lisp:

(cffi:use-foreign-library my-lib)
(cxx:init)
(cxx:add-package "TEST" "TEST")
(test:greet)
(setf my-class (test:creat-xx2 10 20))
(test:y.get myclass)

Это позаботится обо всех конверсиях, extern "C",... для вас.