从Common Lisp调用C++(而不是C)?

Jay*_*Jay 13 c c++ common-lisp

我想知道是否有某种方法可以从Common Lisp调用C++代码(最好是可移植的,如果没有,最好是在SBCL中,如果没有,那么,然后是Clozure,CLisp或ECL).

C++将在内部循环中调用以进行数值计算,因此如果调用速度很快就会很好.

CFFI似乎不支持这个:

"这个概念可以推广到其他语言;在撰写本文时,只有CFFI的C支持相当完整,但正在开发C++支持."

(手册第4章)

SBCL的手册也没有提到C++; 它实际上说

本章描述了SBCL与C程序和库的接口(并且,因为C接口是Unix世界的一种 通用语言,一般来说是其他程序和库.)

C++代码使用OO和运算符重载,所以它确实需要用g ++编译.

据我所知,我可以拥有一个C++ main()函数并为C函数编写包装器,但不是相反 - 这是真的吗?

无论如何......有没有办法做到这一点?

谢谢!

Gre*_*ill 17

编译之后,大多数C++函数实际上归结为常规的C函数调用.由于函数重载和其他功能,C++编译器使用名称修改来区分类似命名的函数.给定一个对象转储实用程序和关于C++编译器的足够知识,您可以直接从外部世界调用C++代码.

尽管如此,您可能会发现在Lisp和C++代码之间编写C兼容层更容易.你会这样使用extern "C"这样:

extern "C" Foo *new_Foo(int x)
{
    return new Foo(x);
}
Run Code Online (Sandbox Code Playgroud)

这使得该new_Foo()函数遵循C调用约定,以便您可以从外部源调用它.

  • 并非所有C++函数都"归结为常规C".除了名称修改之外,成员函数通常使用不同的调用约定.(我相信在x86上的MSVC,`this`参数在寄存器中传递,而C函数中的所有参数都在栈上传递) (3认同)

Tim*_*sch 13

除了名称修改之外,调用C++函数而不是C函数的主要区别在于隐藏传递给成员函数的"隐藏"特性,如此指针.C运行时层对这些,隐式类型转换和其他有趣的C++特性一无所知,因此如果您打算通过C接口调用C++,则可能必须伪造这些功能.

假设您至少可以为要调用的对象及其所需的数据保留void*,则可以降级以下C++调用

matrix->multiply(avector);
Run Code Online (Sandbox Code Playgroud)

如果您创建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);
}
Run Code Online (Sandbox Code Playgroud)

显然,函数matrix_multiply将位于C++源代码中并按原样编译,但它确实向外界公开了C接口.只要您可以与不透明指针进行交互,就可以使用上面的转换填充程序.

不可否认,对于像这样的问题,这不一定是最优雅的解决方案,但我过去在像你这样的情况下使用它.

另一种选择是通过将C++调用视为具有附加参数的C调用并自己提供所有必需信息来直接进行C++调用,但这确实可以非常快速地将您带入特定于编译器的代码领域.基本上,您仍然会持有C++对象的不透明指针,但您必须计算出要调用的函数的错位名称.一旦你有了这个函数名,就必须提供this指针(在C++中是隐式的,在上面的例子中是隐式的)和正确的参数然后调用函数.它可以完成,但如上所述,让您深入了解编译器领域甚至编译器版本特定的行为.


Jay*_*Jay 7

等一下!

似乎有一个技巧我可以使用!

我用C++编写了一个包装器,声明包装函数extern"C":

#include "lib.h"

extern "C" int lib_operate (int i, double *x) {
...
}
Run Code Online (Sandbox Code Playgroud)

头文件lib.h可以从C和C++中调用,它是:

#if __cplusplus
extern "C" {
#endif

int lib_operate (int i, double *x);

#if __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)

然后编译:

g++ -c lib.cpp
gcc -c prog.c
gcc lib.o prog.o -lstdc++ -o prog
Run Code Online (Sandbox Code Playgroud)

似乎适合玩具的例子!:-)

所以,在Common Lisp中,我在加载libstdc ++之后调用包装器.

无论如何,谢谢你的答案!


I.O*_*mar 6

2021 年更新:CL-CXX-JIT处理 lisp 方面的大部分工作。

例子:

(ql:quickload :cxx-jit)
(in-package cxx-jit)
(from '("<cmath>") 'import '("static_cast<double(*)(double)>(std::sin)" . "cpp-sin"))
(cpp-sin 0d0)
(from nil 'import "struct C{ auto hi(){return \"Hello, World\\n\";} auto bye(){return \"Bye\";} };" '("&C::bye" . "bye") '("&C::hi" . "hi") '("[](){static C x; return x;}" . "cc"))
(cc)
(hi *)
(bye **)
Run Code Online (Sandbox Code Playgroud)

您可以使用cl-cxx,就像为 python 编写 pybind11 一样。

在 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>();
}
Run Code Online (Sandbox Code Playgroud)

在 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)
Run Code Online (Sandbox Code Playgroud)

这将处理所有转换,extern "C", ... 为您服务。