python调用C接口

编写c接口代码

//capi.c
#include <stdio.h>
#include <stdlib.h>

char* foo(char* in)
{
  printf("foo function input: %s\n", in);
  return "OK";
}

char* exam(char *in)
{
  printf("exam function input: %s\n",in);
  return "OK";
}

编译c接口代码:

# gcc -o libcapi.so -shared -fPIC capi.c

编译后在本地目录下生成libcapi.so为C接口代码的动态库 编辑python代码

#pycall.py
import ctypes

libcapi = ctypes.cdll.LoadLibrary("./libcapi.so")
libcapi.foo("good")
libcapi.exam("good")
print "success!"

调用Python测试代码

# python pycall.py
foo function input: good
exam function input: good
success!

测试成功!

python调用C++接口

编辑C++接口代码

//cppapi.cpp
#include <iostream>

std::string foo(std::string in)
{
  std::cout << "foo function input: " << in << std::endl;
  return "OK";
}

char* exam(char *in)
{
  std::cout << "exam function input: " << in << std::endl;
  return (char*)"OK";
}

编译C++接口代码

# g++ -o libcppapi.so -shared -fPIC  cppapi.cpp

编写python测试脚步

#pycall.py
import ctypes

libcppapi = ctypes.cdll.LoadLibrary("./libcppapi.so")
libcppapi.foo("good")
libcppapi.exam("good")
print "success!"

执行脚步测试:

# python pycall.py
Traceback (most recent call last):
  File "pycall.py", line 5, in <module>
    libcppapi.foo("good")
  File "/usr/lib/python2.7/ctypes/__init__.py", line 378, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib/python2.7/ctypes/__init__.py", line 383, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: ./libcppapi.so: undefined symbol: foo

c++动态库中找不到foo方法,是因为c和c++的函数签名不一致,因为c++支持重载,所以按c++的方式是找不到同名的c函数的,先查看C接口libcapi.so动态库符号表

# nm -D  libcapi.so
0000000000201038 B __bss_start
                w __cxa_finalize
0000000000201038 D _edata
0000000000201040 B _end
000000000000072d T exam
000000000000075c T _fini
0000000000000700 T foo
                w __gmon_start__
0000000000000598 T _init
                w _ITM_deregisterTMCloneTable
                w _ITM_registerTMCloneTable
                w _Jv_RegisterClasses
                U printf

C库中可以找到foo函数符号和exam函数符号,再看看C++动态库libcppapi.so 的符号表

# nm -D libcppapi.so 0000000000202088 B __bss_start
                 U __cxa_atexit
                 w __cxa_finalize
0000000000202088 D _edata
0000000000202090 B _end
0000000000000dc8 T _fini
                 w __gmon_start__
                 U __gxx_personality_v0
0000000000000a58 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U __stack_chk_fail
                 U _Unwind_Resume
0000000000000c50 T _Z3fooSs
0000000000000d17 T _Z4examPc
                 U _ZNSaIcEC1Ev
                 U _ZNSaIcED1Ev
                 U _ZNSolsEPFRSoS_E
                 U _ZNSsC1EPKcRKSaIcE
                 U _ZNSt8ios_base4InitC1Ev
                 U _ZNSt8ios_base4InitD1Ev
                 U _ZSt4cout
                 U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
                 U _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E
                 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

找到foo和exam函数的符号表变成了_Z3fooSs和_Z4examPc,因此使用C++写的库函数在对外提供Python调用的接口是需要转换成C接口,需要对源代码做适当的处理.

//cpp2capi.cpp
#include <iostream>

//Macro definition EXPORT_C_API is true export c api, otherwise cpp api
#define EXPORT_C_API true

//notify EXPORT_C_API when compiled
#if EXPORT_C_API
#warning("libcpp2capi export c api")
#else
#warning("libcpp2capi export cpp api")
#endif


#if EXPORT_C_API
namespace cppapi{
#endif

//cpp api follow
//----------------------------------------------------

std::string foo(std::string in)
{
  std::cout << "foo function input: " << in << std::endl;
  return "OK";
}

char* exam(char *in)
{
  std::cout << "exam function input: " << in << std::endl;
  return (char*)"OK";
}
//--------------------------------------------------
//cpp api above


#if EXPORT_C_API
}

extern "C" {
  #include <string.h>

  //convert string to char*
  char* convert(std::string str)
  {
    std::cout << "convert " << str << "..." << std::endl;

    int length = str.length();
    char* p_char = new char[length+1];
    memcpy(p_char, str.c_str(), length);
    strcat(p_char, "\0");
    return p_char;
  }

  char* foo(char* in)
  {
    std::cout << "foo(" << in << ")" << std::endl;
    std::string ret = cppapi::foo(in);

    return convert(ret);
  }

  char* exam(char *in)
  {
    return cppapi::exam(in);
  }

}
#endif


然后编译cpp2capi.cpp:

# g++ -o libcpp2capi.so -shared -fPIC cpp2capi.cpp
cpp2capi.cpp:9:2: warning: #warning ("libcpp2capi export c api") [-Wcpp]
 #warning("libcpp2capi export c api")
  ^
# ls
cpp2capi.cpp  libcpp2capi.so  pycall.py

# python pycall.py
foo(good)
foo function input: good
convert OK...
exam function input: good
success!

调用成功,查看libcpp2capi.so动态库中的符号表

# nm -D libcpp2capi.so
00000000002020d0 B __bss_start
0000000000001089 T convert
                 U __cxa_atexit
                 w __cxa_finalize
00000000002020d0 D _edata
00000000002020d8 B _end
00000000000012ae T exam
0000000000001328 T _fini
0000000000001137 T foo
                 w __gmon_start__
                 U __gxx_personality_v0
0000000000000cf0 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U memcpy
                 w __pthread_key_create
                 U __stack_chk_fail
                 U _Unwind_Resume
0000000000000f70 T _ZN6cppapi3fooESs
0000000000001037 T _ZN6cppapi4examEPc
                 U _Znam
                 U _ZNKSs5c_strEv
                 U _ZNKSs6lengthEv
                 U _ZNSaIcEC1Ev
                 U _ZNSaIcED1Ev
                 U _ZNSolsEPFRSoS_E
                 U _ZNSsC1EPKcRKSaIcE
                 U _ZNSsC1ERKSs
                 U _ZNSsD1Ev
                 U _ZNSt8ios_base4InitC1Ev
                 U _ZNSt8ios_base4InitD1Ev
                 U _ZSt4cout
                 U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
                 U _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E
                 U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

符号表中存在foo和exam函数符号.

当然也可以使用:

# objdump -T libcpp2capi.so

libcpp2capi.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000dd0 l    d  .init	0000000000000000              .init
0000000000000000  w   D  *UND*	0000000000000000              __gmon_start__
0000000000000000  w   D  *UND*	0000000000000000              _Jv_RegisterClasses
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _Znam
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4.21 _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6lengthEv
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZNSt8ios_base4InitC1Ev
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.2.5 __cxa_atexit
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZNSt8ios_base4InitD1Ev
0000000000000000  w   D  *UND*	0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000000000000000  w   D  *UND*	0000000000000000              _ITM_registerTMCloneTable
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4.21 _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev
0000000000000000  w   DF *UND*	0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000000000      DO *UND*	0000000000000000  GLIBCXX_3.4 _ZSt4cout
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.4   __stack_chk_fail
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZNSaIcED1Ev
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZNSolsEPFRSoS_E
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4.21 _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4.21 _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4.21 _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4 _ZNSaIcEC1Ev
0000000000000000      DF *UND*	0000000000000000  CXXABI_1.3  __gxx_personality_v0
0000000000000000      DF *UND*	0000000000000000  GLIBCXX_3.4.21 _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE
0000000000000000      DF *UND*	0000000000000000  GCC_3.0     _Unwind_Resume
0000000000000000      DF *UND*	0000000000000000  GLIBC_2.14  memcpy
0000000000001207 g    DF .text	0000000000000177  Base        foo
000000000000137e g    DF .text	000000000000001a  Base        exam
00000000002020c8 g    D  .bss	0000000000000000  Base        _end
00000000002020c0 g    D  .data	0000000000000000  Base        _edata
0000000000001159 g    DF .text	00000000000000ae  Base        convert
00000000002020c0 g    D  .bss	0000000000000000  Base        __bss_start
0000000000000dd0 g    DF .init	0000000000000000  Base        _init
00000000000013f8 g    DF .fini	0000000000000000  Base        _fini
0000000000001040 g    DF .text	00000000000000c7  Base        _ZN6cppapi3fooENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
0000000000001107 g    DF .text	0000000000000052  Base        _ZN6cppapi4examEPc