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
// 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
#include
#ifdef __cplusplus
extern "C" {
#endif
DLL_PUBLIC ApplePtr GetInstance(void)
{
return new Apple;
}
DLL_PUBLIC void ReleaseInstance(ApplePtr ptr)
{
try {
auto *client = static_cast(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(ptr);
pApple->SetColor(color);
}
DLL_PUBLIC int GetColor(ApplePtr ptr)
{
if (ptr == nullptr) {
return -1;
}
auto *pApple = static_cast(ptr);
return pApple->GetColor();
}
#ifdef __cplusplus
};
#endif
main.c的写法:
#include "apple_c.h"
#include
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
参考知识:
Standard C++ https://isocpp.org/wiki/faq/mixing-c-and-cpp