赞
踩
C++不是兼容C语言的吗?
是的,如果你只是自己开发程序,只用一种语言的话,就没有必要进行封装。如果你开发的是一个C++的.so库,你希望将这个库提供给其他语言使用的话,就涉及到不同语言的相互调用,大家都知道C语言的库是最容易与其他语言交互的。
比如mysql是用C++编写的,但还会提供C语言的调用接口(.h头文件),但是这个头文件与C++所用的头文件是不一样的,因为C语言处理不 了Class这些东西。
这个文章我们只讲如何在C语言中调用C++代码。
基本方法是,写一个 wrapper文件,把 C++类封装起来,对外只提供C语言的接口,和 C++相关的实现在 wrapper的实现文件里实现一遍。
提供一个C语言专用的头文件,里面只提供完成程序功能最基本的C语言类型指针和函数,所有的功能都通过此头文件对应的函数实现。
所有的定义放在extern "C" { }代码块中包裹起来。
看下面的示例:
假定有一个类定义 ,是用C++定义的。
apple.h
- #ifndef __APPLE_H__
- #define __APPLE_H__
-
-
- enum class LogLevel {
- Trace /// Most detailed output
- ,Debug
- ,Info
- ,Warn
- ,Error
- ,Fatal /// Least detailed output
- ,Current /// no-op, value indicates current level should be retained
- };
-
-
- class Apple
- {
- public:
- Apple();
- int GetColor(void);
- void SetColor(int color);
-
- private:
- int m_nColor;
- };
- #endif

apple.cpp
- #include "apple.h"
-
- Apple::Apple() : m_nColor(0)
- {
- }
-
- void Apple::SetColor(int color)
- {
- m_nColor = color;
- }
-
- int Apple::GetColor(void)
- {
- return m_nColor;
- }
此时如果直接用C++写main函数是这样的:
- #include "apple.h"
- #include <iostream>
-
- // main for cpp
- int main(void)
- {
- Apple a;
- a.SetColor(1);
- printf("color = %d\n", a.GetColor());
- return 0;
- }
编译生成mainpp程序
- g++ -g -c apple.cpp -o apple.o
- g++ -g main.cpp -o mainpp apple.o
但是如果将apple.cpp编译成一个库提供给C语言开发者,C语言肯定是无法调用 的。
所以就要有一个wrapper。
apple_c.h
- #ifndef _APPLE_C_H_
- #define _APPLE_C_H_
-
-
- #if defined _WIN32 || defined WIN32 || defined _WINDOWS || defined __CYGWIN__
- #ifdef _DLL
- #ifdef __GNUC__
- #define DLL_PUBLIC __attribute__ ((dllexport))
- #else
- #define DLL_PUBLIC __declspec(dllexport)
- #endif
- #else
- #ifdef __GNUC__
- #define DLL_PUBLIC __attribute__ ((dllimport))
- #else
- #define DLL_PUBLIC __declspec(dllimport)
- #endif
- #endif
- #define DLL_LOCAL
- #else
- #if __GNUC__ >= 4
- #define DLL_PUBLIC __attribute__ ((visibility ("default")))
- #define DLL_LOCAL __attribute__ ((visibility ("hidden")))
- #else
- #define DLL_PUBLIC
- #define DLL_LOCAL
- #endif
- #endif
-
-
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
-
- typedef enum {
- LogLevelTrace = 0, /// Most detailed output
- LogLevelDebug,
- LogLevelInfo,
- LogLevelWarn,
- LogLevelError,
- LogLevelFatal /// Least detailed output
- } AppLogLevel;
-
- typedef void* ApplePtr; //指针类型定义
- DLL_PUBLIC ApplePtr GetInstance(void);
- DLL_PUBLIC void ReleaseInstance(ApplePtr ptr);
- DLL_PUBLIC void SetColor(ApplePtr ptr, int color);
- DLL_PUBLIC int GetColor(ApplePtr ptr);
-
- #ifdef __cplusplus
- };
- #endif
-
- #endif

apple_c.cpp
- #include "apple_c.h"
- #include "apple.h"
- #include <exception>
- #include <iostream>
-
-
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
-
-
- DLL_PUBLIC ApplePtr GetInstance(void)
- {
- return new Apple;
- }
-
- DLL_PUBLIC void ReleaseInstance(ApplePtr ptr)
- {
- try {
- auto *client = static_cast<Apple*>(ptr);
- delete client;
- ptr = nullptr;
- } catch (std::exception &e) {
- std::cout<< e.what() << std::endl;
- }
- }
-
- DLL_PUBLIC void SetColor(ApplePtr ptr, int color)
- {
- if (ptr == nullptr) {
- return ;
- }
- auto *pApple = static_cast<Apple*>(ptr);
- pApple->SetColor(color);
- }
-
- DLL_PUBLIC int GetColor(ApplePtr ptr)
- {
- if (ptr == nullptr) {
- return -1;
- }
- auto *pApple = static_cast<Apple*>(ptr);
- return pApple->GetColor();
- }
-
-
-
-
- #ifdef __cplusplus
- };
- #endif

main.c的写法:
- #include "apple_c.h"
- #include <stdio.h>
-
- int main(void)
- {
- ApplePtr pApple;
- pApple = GetInstance();
- SetColor(pApple, 1);
-
- printf("color = %d\n", GetColor(pApple));
- ReleaseInstance(pApple);
- //printf("color = %d\n", GetColor(pApple));// pApple released
- return 0;
- }
编译:
apple.cpp生成c++的静态库,再用apple_c.cpp生成一个wrapper的动态库libapple_c.so
再用main.c去调用这个动态库。
- # compile .cpp
- g++ -g -c apple.cpp -o apple.o
- g++ -g -c apple_c.cpp -o apple_c.o
-
- # build cpp static library
- ar rcs libapple.a apple.o
-
- # build c wraper libapple_c.so dynamic library
- g++ -g -fpic -shared -o libapple_c.so apple_c.o -L. -l apple -fvisibility=hidden
-
-
- # build main.c
- gcc -g main.c -o mainc -L. -l apple_c
结构体、类。已经在c++中定义过了,只需要定义对应的void *指针类型即可。
类的成员函数都要转换成普通的函数,一般将类对象指针作为第一个参数传入函数中。
enum类要在c头文件中重新定义
Exception处理。 c语言不支持Exception,所以异常需要在c++对应的wrapper cpp文件中进行处理。否则会在运行时出问题。
https://github.com/opentdf/client-cpp/blob/main/src/lib/include/tdf_client_c.h
https://github.com/opentdf/client-cpp/blob/main/src/lib/include/tdf_client.h
参考知识:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。