如何从 C 调用 C 函数?

新手上路,请多包涵

我知道这个。

从 C++ 调用 C 函数:

如果我的应用程序是用 C++ 编写的,并且我必须从用 C 编写的库中调用函数。那么我会使用

//main.cpp

extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.

这不会破坏名称 C_library_function 并且链接器会在其输入 *.lib 文件中找到相同的名称,问题就解决了。

从 C 调用 C++ 函数???

但是在这里我扩展了一个用 C 编写的大型应用程序,我需要使用一个用 C++ 编写的库。 C++ 的名称修改在这里造成了麻烦。链接器正在抱怨未解析的符号。好吧,我不能在我的 C 项目上使用 C++ 编译器,因为那会破坏很多其他东西。出路是什么?

顺便说一句,我正在使用 MSVC

原文由 claws 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 961
2 个回答

您需要创建一个 C API 来公开 C++ 代码的功能。基本上,您将需要编写声明为 extern “C” 并且具有包装 C++ 库的纯 C API(例如,不使用类)的 C++ 代码。然后使用您创建的纯 C 包装库。

您的 C API 可以选择遵循面向对象的风格,即使 C 不是面向对象的。前任:

 // *.h file
// ...
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

typedef void* mylibrary_mytype_t;

EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);

#undef EXTERNC
// ...

 // *.cpp file
mylibrary_mytype_t mylibrary_mytype_init() {
  return new MyType;
}

void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
   MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
   delete typed_ptr;
}

void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
   MyType* typed_self = static_cast<MyType*>(untyped_self);
   typed_self->doIt(param);
}

原文由 Michael Aaron Safyan 发布,翻译遵循 CC BY-SA 4.0 许可协议

我会通过以下方式做到这一点:

(如果使用 MSVC,请忽略 GCC 编译命令)

假设我有一个名为 AAA 的 C++ 类,在文件 aaa.h、aaa.cpp 中定义,并且 AAA 类有一个名为 sayHi(const char *name) 的方法,我想为 C 代码启用该方法。

AAA级 的C++代码——纯C++,我不修改:

aaa.h

 #ifndef AAA_H
#define AAA_H

class AAA {
    public:
        AAA();
        void sayHi(const char *name);
};

#endif

aaa.cpp

 #include <iostream>

#include "aaa.h"

AAA::AAA() {
}

void AAA::sayHi(const char *name) {
    std::cout << "Hi " << name << std::endl;
}

按照 C++ 的常规编译此类。该代码“不知道”它将被 C 代码使用。使用命令:

 g++ -fpic -shared aaa.cpp -o libaaa.so


现在,同样在 C++ 中,创建一个 C 连接器:

在文件 aaa_c_connector.h、aaa_c_connector.cpp 中定义它。此连接器将定义一个名为 AAA_sayHi(cosnt char *name) 的 C 函数,它将使用 AAA 的实例并调用其方法:

aaa_c_connector.h

 #ifndef AAA_C_CONNECTOR_H
#define AAA_C_CONNECTOR_H

#ifdef __cplusplus
extern "C" {
#endif

void AAA_sayHi(const char *name);

#ifdef __cplusplus
}
#endif

#endif

aaa_c_connector.cpp

 #include <cstdlib>

#include "aaa_c_connector.h"
#include "aaa.h"

#ifdef __cplusplus
extern "C" {
#endif

// Inside this "extern C" block, I can implement functions in C++, which will externally
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),

static AAA *AAA_instance = NULL;

void lazyAAA() {
    if (AAA_instance == NULL) {
        AAA_instance = new AAA();
    }
}

void AAA_sayHi(const char *name) {
    lazyAAA();
    AAA_instance->sayHi(name);
}

#ifdef __cplusplus
}
#endif

再次使用常规 C++ 编译命令对其进行编译:

 g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so


现在我有一个共享库 (libaaa_c_connector.so),它实现了 C 函数 AAA_sayHi(const char *name) 。我现在可以创建一个 C 主文件并一起编译它:

main.c

 #include "aaa_c_connector.h"

int main() {
    AAA_sayHi("David");
    AAA_sayHi("James");

    return 0;
}

使用 C 编译命令对其进行编译:

 gcc main.c -L. -laaa_c_connector -o c_aaa

我需要将 LD_LIBRARY_PATH 设置为包含 $PWD,如果我运行可执行文件 ./c_aaa ,我会得到我期望的输出:

 Hi David
Hi James

编辑:

在某些 linux 发行版上,最后一个编译命令可能还需要 -laaa-lstdc++ 。感谢@AlaaM。为了引起注意

原文由 SomethingSomething 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题