c调用c++函数(包括类成员函数)的方法

声明:引用两处博客

http://blog.csdn.net/lg1259156776/

http://blog.csdn.net/caspiansea/article/details/9676153


精要一揽

C调用C++,使用extern “C”则是告诉编译器依照C的方式来编译封装接口,当然接口函数里面的C++语法还是按C++方式编译。

使用extern “C” 主要是因为C编译器编译函数时不带参数的类型信息,只包含函数的符号名字。如

int foo( float x )

C编译器会将此函数编译成类似_foo的符号,C连接器只要找到了调用函数的符号,就认为连接成功。 
而C++编译器为了实现函数重载,会在编译时带上函数的参数信息。如它可以把上面的函数编译成类似于foo_float这样的符号。

源文件在编译器下生成的目标文件中导出符号的规则问题

另外,具体的关于源文件在编译器下生成的目标文件中导出符号的规则问题,可以参看我的另一篇博文《[【读书笔记】程序员的自我修养总结(三)][2]》,具体的内容我粘贴如下:

由于全局符号在链接过程是全局可见的,所以如果编写的库文件中和当前的目标文件中有相同的符号名,那么就会发生冲突。为了防止类似的符号名冲突,UNIX下的C语言规定,C语言源代码文件中的所有全局变量和函数经过编译以后,相对应的符号名前面加上下划线,“_”。但如果是一个大型的软件,由不同的部门来开发,他们之间的命名规范如果不严格,则可能导致冲突,于是C++这样的语言使用了Namespace来解决不同模块下的符号冲突。

C++的符号修饰机制指的是C++语言支持不同参数类型的函数拥有一样的名字,即函数重载。实际上他们在编译的时候会进行一个函数签名,包含函数名,参数类型,所在类和命名空间等信息。

而名称修饰机制,也被用来防止静态变量,不同的编译器可能名称修饰方法不同,函数签名可能对应不同的修饰名称,由于不同的编译器采用不同的名字修饰方法,必然导致由不同编译器编译产生的目标文件无法正常相互链接。

“extern C”用法

C++中为了与C兼容,在符号管理上有一个用来声明或定义一个C的符号的“extern C”关键字用法:

extern “C”{
    int func(int);
    int var;
}

C++ 编译器会将extern “C”的大括号内部的代码当作C语言来处理。VC++平台会将C语言的符号进行修饰,即大括号中的func和var修饰后的符号为_func和_var,而C++部分的则按照C++的那一套进行修饰。

而下面的一段代码常常用来解决C/C++两种源码编译形式:

#ifdef __cplusplus
extern "C"{
#endif

void *memset(void *, int, size_t);

#ifdef __cplusplus
}
#endif

如果当前参与编译的是C++代码,memset会在extern “C”中被声明,按照C代码进行符号修饰;而如果是C代码,直接声明即可。(C语言不支持extern “C”,而__cplusplus这个宏是C++编译器默认定义的,如果是C++编译器参与的编译,则就默认定义了该宏。)。这段代码几乎在所有的系统头文件中都被利用。

C调用C++库

调用C++函数库,一般不能直接调用,需要将C++库转换成C接口输出,方可以使用C调用

将 C++ 函数声明为“extern “C””(在你的 C++ 代码里做这个声明),然后调用它(在你的 C 或者 C++ 代码里调用)。例如:

// C++ code:
extern "C" void f(int);
void f(int i)
{
     // ...
}

然后,你可以在C文件中这样使用 f():

/* C code: */
void f(int);
void cc(int i)
{
    f(i);
   /* ... */
    }

当然,这招只适用于非成员函数。如果你想要在 C 里调用成员函数(包括虚函数),则需要提供一个简单的包装(wrapper)。例如:

// C++ code:
class C
{
       // ...
       virtual double f(int);
};

extern "C" double call_C_f(C* p, int i) // wrapper function
{
       return p->f(i);
}

然后,你就可以这样调用 C::f():

/* C code: */
double call_C_f(struct C* p, int i);

void ccc(struct C* p, int i)
{
       double d = call_C_f(p,i);
       /* ... */
}

如果你想在 C 里调用重载函数,则必须提供不同名字的包装,这样才能被 C 代码调用。例如:

// C++ code:
void f(int);
void f(double);

extern "C" void f_i(int i) { f(i); }
extern "C" void f_d(double d) { f(d); }

为什么这样做的目的很明显,因为C++为了实现复杂的重载函数功能,在目标文件生成的过程中,会将函数名与参数返回值等类型一起导出到函数符号中去,从而实现的重载函数之间的区分,所以声明为C的时候,必须要提供不同的函数名才行。

参数struct C* p从哪里来,即怎么在C中定义C++对象,其实上面只是说了思想,真实的c中使用C++类需要把原来的类都封装一下:

基本思想是,写一个 wrapper文件,把 C++类封装起来,对外只提供C语言的接口,和 C++i相关的都在  wrapper的实现文件里实现。

1. apple.h

[cpp]  view plain  copy
 print ?
  1.    
  2. #ifndef __APPLE_H__  
  3. #define __APPLE_H__  
  4. class Apple  
  5. {  
  6. public:  
  7.     enum  
  8.     {  
  9.         APPLE_COLOR_RED,  
  10.         APPLE_COLOR_BLUE,  
  11.         APPLE_COLOR_GREEN,  
  12.     };  
  13.   
  14.     Apple();  
  15.     int GetColor(void);  
  16.     void SetColor(int color);  
  17. private:  
  18.     int m_nColor;  
  19. };  
  20. #endif   
apple.cpp:

[cpp]  view plain  copy
 print ?
  1. #include "apple.h"  
  2. Apple::Apple():m_nColor(APPLE_COLOR_RED)  
  3. {  
  4. }  
  5.   
  6. void Apple::SetColor(int color)  
  7. {  
  8.     m_nColor = color;  
  9. }  
  10.   
  11. int Apple::GetColor(void)  
  12. {  
  13.     return m_nColor;  
  14. }  

2. AppleWrapper.h

[cpp]  view plain  copy
 print ?
  1. #ifndef _APPLE_WRAPPER_H__  
  2. #define _APPLE_WRAPPER_H_  
  3. struct tagApple;  
  4. #ifdef __cplusplus  
  5. extern "C" {  
  6. #endif  
  7. struct tagApple *GetInstance(void);  
  8. void ReleaseInstance(struct tagApple **ppInstance);  
  9. extern void SetColor(struct tagApple *pApple, int color);  
  10. extern int GetColor(struct tagApple *pApple);  
  11. #ifdef __cplusplus  
  12. };  
  13. #endif  
  14. #endif  

AppleWrapper.cpp

[cpp]  view plain  copy
 print ?
  1. #include "AppleWrapper.h"  
  2. #include "apple.h"  
  3.   
  4. #ifdef __cplusplus  
  5. extern "C" {  
  6. #endif  
  7. struct tagApple  
  8. {  
  9.     Apple apple;  
  10. };  
  11. struct tagApple *GetInstance(void)  
  12. {  
  13.     return new struct tagApple;  
  14. }  
  15. void ReleaseInstance(struct tagApple **ppInstance)  
  16. {  
  17.     delete *ppInstance;  
  18.     *ppInstance = 0;  
  19.       
  20. }  
  21. void SetColor(struct tagApple *pApple, int color)  
  22. {  
  23.     pApple->apple.SetColor(color);  
  24. }  
  25.   
  26. int GetColor(struct tagApple *pApple)  
  27. {  
  28.     return pApple->apple.GetColor();  
  29. }  
  30. #ifdef __cplusplus  
  31. };  
  32. #endif  

3. test.c

[cpp]  view plain  copy
 print ?
  1. #include "AppleWrapper.h"  
  2. #include <assert.h>  
  3.   
  4. int main(void)  
  5. {  
  6.     struct tagApple * pApple;  
  7.     pApple= GetInstance();  
  8.     SetColor(pApple, 1);  
  9.     int color = GetColor(pApple);  
  10.     printf("color = %d\n", color);  
  11.     ReleaseInstance(&pApple);  
  12.     assert(pApple == 0);  
  13.     return 0;  
  14. }  

可以用 GCC编译:

[plain]  view plain  copy
 print ?
  1. g++ -c apple.cpp  
  2. g++ -c apple.cpp  AppleWrapper.cpp  
  3. gcc test.c -o test AppleWrapper.o apple.o -lstdc++  

其实,  wrapper里的 struct 完全可以不要,定义一个  handle更好:

[html]  view plain  copy
 print ?
  1. #ifndef _APPLE_WRAPPER_H__  
  2. #define _APPLE_WRAPPER_H_  
  3. #ifdef __cplusplus  
  4. extern "C" {  
  5. #endif  
  6. int  GetInstance(int *handle);  
  7. void ReleaseInstance(int *handle);  
  8. extern void SetColor(int handle, int color);  
  9. extern int GetColor(int handle);  
  10. #ifdef __cplusplus  
  11. };  
  12. #endif  
  13. #endif  

[html]  view plain  copy
 print ?
  1. #include "AppleWrapper.h"  
  2. #include "apple.h"  
  3. #include <vector>  
  4.   
  5. #ifdef __cplusplus  
  6. extern "C" {  
  7. #endif  
  8.   
  9. static std::vector<Apple *> g_appleVector;  
  10.   
  11. int GetInstance(int * handle)  
  12. {  
  13.     g_appleVector[0] =  new Apple;  
  14.     *handle = 0;  
  15.     return 1;  
  16. }  
  17. void ReleaseInstance(int *handle)  
  18. {  
  19.     delete g_appleVector[*handle];  
  20.     *handle = -1;  
  21.       
  22. }  
  23. void SetColor(int handle, int color)  
  24. {  
  25.     g_appleVector[handle]->SetColor(color);  
  26. }  
  27.   
  28. int GetColor(int handle)  
  29. {  
  30.     return g_appleVector[handle]->GetColor();  
  31. }  
  32. #ifdef __cplusplus  
  33. };  
  34. #endif  

  • 10
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在 Python 中调用 C 语言的成员函数,通常需要使用 Cython 或 ctypes 库。 使用 Cython 的方法: 1. 安装 Cython:在命令行中输入 `pip install cython` 即可安装。 2. 编写 Cython 代码:在 Python 中创建一个 .pyx 文件,然后使用 Cython 语法编写代码。例如,下面是示例代码: ```python cdef extern from "my_c_library.h": cdef cppclass MyCClass: MyCClass() int my_c_function(int x, int y) cdef class PyMyCClass: cdef MyCClass *thisptr # hold a C++ instance which we're wrapping def __cinit__(self): self.thisptr = new MyCClass() def my_c_function(self, x, y): return self.thisptr.my_c_function(x, y) ``` 3. 编译 Cython 代码:使用 Cython 编译器将 .pyx 文件转换为 C/C++ 代码。例如,在命令行中输入 `cython my_cython_code.pyx` 即可生成名为 `my_cython_code.c` 的 C 代码文件。 4. 编译生成的 C 代码:使用 C 编译器将 C 代码文件编译为可执行文件或动态链接库。 5. 在 Python 中使用生成的可执行文件或动态链接库:使用 Python 的 ctypes 库或其他方法加载可执行文件或动态链接库,然后就可以在 Python 中使用 C 语言的成员函数了。 使用 ctypes 的方法: 1. 安装 ctypes:ctypes 是 Python 的标准库,因此不需要安装。 2 ### 回答2: 在Python中,可以通过ctypes模块来调用C语言成员函数。 首先,需要使用ctypes模块导入C语言的动态链接库文件,使用`ctypes.CDLL()`函数进行加载。 接下来,使用`ctypes.Structure`创建一个与C语言结构相对应的Python,确保Python的成员变量与C语言中的变量一一对应。 然后,使用`ctypes.CFUNCTYPE()`函数创建一个函数型,用于定义C语言成员函数。 接下来,使用`ctypes.cast()`函数将Python对象转换为指向C对象的指针。 通过`ctypes.POINTER()`函数创建一个指针型,指向C语言的对象。 最后,使用创建的指针调用C语言成员函数。 以下是一个示例代码: ```python import ctypes # 加载C语言动态链接库文件 c_lib = ctypes.CDLL("path/to/library.so") # 创建Python,与C语言结构相对应 class MyStruct(ctypes.Structure): _fields_ = [ ("data", ctypes.c_int), ("next", ctypes.POINTER(MyStruct)) ] # 定义C语言成员函数型 MyFuncType = ctypes.CFUNCTYPE(None, ctypes.POINTER(MyStruct)) # 将Python对象转换为指向C对象的指针 obj = MyStruct() c_obj = ctypes.cast(obj, ctypes.POINTER(MyStruct)) # 调用C语言成员函数 c_func = MyFuncType(c_lib.my_function) c_func(c_obj) ``` 需要注意的是,此方法只能调用已被编译为动态链接库的C语言成员函数。同时,确保PythonC语言的成员变量一一对应。 ### 回答3: Python可以通过ctypes模块调用C的成员函数。 首先,在Python中导入ctypes模块: import ctypes 然后,使用ctypes.CDLL或ctypes.WinDLL函数加载C库: lib = ctypes.CDLL("C库路径") 或 lib = ctypes.WinDLL("C库路径") 接下来,定义C的结构体型,可以使用ctypes.Structure来定义一个与C对应的结构体型: class CClass(ctypes.Structure): _fields_ = [("成员变量名1", ctypes.c_type1), ("成员变量名2", ctypes.c_type2), ... ("成员变量名n", ctypes.c_typen)] 然后,使用ctypes.POINTER函数定义一个指向C结构体的指针型: CClassPtr = ctypes.POINTER(CClass) 接着,通过ctypes调用C成员函数: 1. 先定义函数原型,并设置返回值型和参数型: lib.function_name.restype = 返回值型 lib.function_name.argtypes = 参数型列表 2. 然后,通过指针调用函数: result = lib.function_name(参数列表) 注意:参数型列表的顺序应该与C成员函数的参数顺序保持一致。 此外,还可以通过ctypes调用C的成员变量: 1. 首先,通过ctypes创建一个C对象: obj = CClass() 2. 然后,通过对象的成员变量名访问和修改成员变量的值: obj.成员变量名 = 新的值 以上就是使用ctypes模块在Python中调用C成员函数方法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值