【问题标题】:Building a shared object from a single .cpp从单个 .cpp 构建共享对象
【发布时间】:2023-03-18 03:49:01
【问题描述】:

我正在尝试从cpp 文件构建一个共享对象库,该文件是一组简单的函数。我想用ctypes与python交互。

假设我有cpp 文件:

#include "print.h"
#include <vector>
#include <iostream>
#include <dlfcn.h>

void print_array(const std::vector<std::vector<float>> &A){
  for(size_t i = 0; i < A.size(); i++) {
    for(size_t j = 0; j < A[0].size(); j++) {
      std::cout << A[i][j] << "\n";
    }
  }
}

和头文件

#ifndef ADD_H
#define ADD_H
#include <vector>

void print_array(const std::vector<std::vector<float>> &A);

#endif

我尝试构建

g++ -fpic -c print.cpp -o print.o
g++ -shared -o print.so print.o

然后在python中

from cytpes import cdll
print_lib = cdll.LoadLibrary("print.so")

一行

print_lib.print_array()

产量

AttributeError: ./print.so: undefined symbol: print_array

nm -D print.so

给出输出

0000000000201060 B __bss_start
                 U __cxa_atexit
                 w __cxa_finalize
0000000000201060 D _edata
0000000000201068 B _end
0000000000000c14 T _fini
                 w __gmon_start__
0000000000000898 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000a50 T _Z11print_arrayRKSt6vectorIS_IfSaIfEESaIS1_EE
0000000000000bcc W _ZNKSt6vectorIfSaIfEE4sizeEv
0000000000000bf2 W _ZNKSt6vectorIfSaIfEEixEm
0000000000000b6a W _ZNKSt6vectorIS_IfSaIfEESaIS1_EE4sizeEv
0000000000000ba2 W _ZNKSt6vectorIS_IfSaIfEESaIS1_EEixEm
                 U _ZNSolsEf
                 U _ZNSt8ios_base4InitC1Ev
                 U _ZNSt8ios_base4InitD1Ev
                 U _ZSt4cout
                 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

我在编译步骤中根本做错了什么?

【问题讨论】:

  • 注意它是怎么说的,undefined symbol: print_arrayprint.so 中的符号似乎是 _Z11print_arrayRKSt6vectorIS_IfSaIfEESaIS1_EE
  • 你被c++ ABI击中了。见stackoverflow.com/questions/1615813/…

标签: python c++ shared-libraries


【解决方案1】:

您正在调用 C++ 函数,而 ctypes 需要 C 函数链接:

  • 如您所见,C 函数具有不同的(即没有)名称修饰
  • C 函数不能有 C++ 参数(std 对象,如 std::vector、引用等)

要解决您的问题,请在您的标头中声明一个干净的 C 样式 API,如果您打算在 C++ 中实现函数并使用 g++ 作为编译器/链接器,则必须在函数之前添加 extern "C" 声明头文件中的声明,或作为声明周围的块:

extern "C" void print_array(...);

这将防止 C++ 名称损坏。

在接口函数中使用向量和其他 C++ 类型会导致 ABI 问题,即。 e. “它不会工作”,即使所有链接和编译看起来都很干净。

在您的函数中使用具有 ctypes 支持的数据类型的 C 函数(请参阅 Python docs on ctypes)作为接口,您可以随意在 C++ 中实现,但随后将您的 C 函数声明包装在 extern "C" 块中以防止名称混淆.

请务必参考 ctypes 的文档以了解如何正确使用结构、联合、引用等作为函数参数,因为有许多陷阱和问题需要考虑。

【讨论】:

  • 同意,我将坚持使用数组,因为我不需要动态结构。
  • @mdoubez 你在说什么?将 C++ 函数标记为 extern "C" 绝对没问题。 extern "C" 告诉编译器不要对函数执行名称修改,而是发出一个与 C 兼容的符号。
  • 它还修改了调用约定,“C”没有参数 std::vector& 的调用约定。您不能向 C 编译器公开相同的函数。
  • 传入一个numpy数组作为float *var传递。然后我在内部函数中转换为 c++ 类型。这种方法对我很有效!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-25
  • 1970-01-01
  • 2012-07-02
  • 1970-01-01
  • 2013-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多