如何从C中调用C++函数?

4
我正在编写一个C程序(myapp),它需要使用一个特定的API;这个API是用C++编写的。我之前有过C和C++的经验,但从来没有同时使用过两者,所以我有些困惑。
所以,这个API提供了以下目录,我将其放在一个名为include的文件夹中,与我的makefile处于同一级别:
libmyapi.a
api/api.h

我的主要源文件是src/myapp.c,它包含了使用#include "api/api.h"的API。
我的make命令是(还有一些标志,我没有列出来,因为我认为它们在这里不相关):
gcc  -Linclude -lmyapi -Iinclude  src/myapp.c -o lib/myapp.sp -lrt

我遇到的问题是api.h文件中包含了对命名空间等的引用。例如,在某个地方它有:
namespace MyAPI {
  namespace API {
    typedef SimpleProxyServer SimpleConnection;
  }
}

显然,C编译器不知道这是什么意思。

所以,我假设我需要使用C++编译器进行编译,但后来有人说我不需要,我只需要在代码中添加"extern 'C'",但我真的不太明白。在网上阅读了一些资料后,我还是没有更进一步。

我需要用C++编译吗(例如使用g++)?

我需要给代码添加"extern 'C'"吗?那是什么意思?我只需要这样做吗:

#ifdef __cplusplus
  extern "C" {
    namespace MyAPI {
      namespace API {
        typedef SimpleProxyServer SimpleConnection;
      }
    }
  }
#endif

或者我只是把这些句子包起来

namespace MyAPI {
      namespace API {

然后它们对应的}}是什么意思?

头文件调用其他头文件,所以可能我需要在很多地方做这个。

到目前为止,我尝试了各种变化,都遇到了错误和警告,但我不知道是我包装错了,设置了错误的g++编译器标志,使用了错误的编译器,还是其他原因!如果我知道要使用的方法,至少我可以开始调试。


1
这个回答解决了你的问题吗?extern "C"在C++中有什么作用? - Simon Kraemer
1
请查看此答案中的示例:https://dev59.com/P3NA5IYBdhLWcg3wPLP-#30526795。 我认为它涵盖了你所有的问题。 - Simon Kraemer
1
编写一个全新的“C兼容”接口可能比尝试重塑C++代码更容易。如果库还没有准备好从C中使用,那么选择性地在其上添加extern "C"是不太可能成功的。 - molbdnilo
1
这个回答解决了你的问题吗?如何从C中调用C++函数? - moooeeeep
1
还有相关的内容:https://dev59.com/V3I-5IYBdhLWcg3wBjrl和https://isocpp.org/wiki/faq/mixing-c-and-cpp#call-cpp - moooeeeep
谢谢提供链接。我已经阅读了它们,但仍然不确定我在做什么。我正在遵循下面Mestkon的回答,这似乎接近起作用了。 - Sharon
4个回答

8
你可以编写一个小的C++程序来创建API的C绑定。
考虑到这个API:
namespace MyAPI {
  namespace API {
    typedef SimpleProxyServer SimpleConnection;
  }
}

你可以创建 c_api.h

#ifdef __cplusplus
extern "C" {
#endif

struct api_handle_t;
typedef struct api_handle_t* api_handle;
api_handle myapi_api_create();
void myapi_api_some_function_using_api(api_handle h);
void myapi_api_destroy(api_handle h);

#ifdef __cplusplus
}
#endif

以及 c_api.cpp

#include "c_api.h"
#include <myapi/api/stuff.hpp>

struct api_handle_t
{
    MyAPI::API::SimpleConnection c;
};

api_handle myapi_api_create()
{
    return new api_handle_t;
}

void myapi_api_some_function_using_api(api_handle h)
{ 
    //implement using h
}

void myapi_api_destroy(api_handle h) 
{
    delete h;
}

使用C++编译器将其编译,并在C项目中包含c_api.h文件,链接到使用C++编译器和原始库创建的库。


现在我回到我的原始代码,并将“include c_api.h”添加到其中一个头文件中,然后使用以下命令进行编译:gcc -Linclude -lmyapi -lapiwrapper -shared -DKXVER=3 -fPIC -D_LINUX_X86_64 -Iinclude src/myapp.c -o lib/myapp.so。但是编译时出现错误:error: unknown type name ‘api_handle_t’。所以它找到了h文件,但没有找到新的库。你有什么想法我做错了什么吗? - Sharon
1
嗯,你可能需要编写 typedef struct api_handle_t* api_handle;。C语言很挑剔,如果不在结构体名称前显式地写上“struct”,它就无法解析结构体名称。 - Mestkon
谢谢,但它也不喜欢那样。它说它正在忽略 "typedef"。 - Sharon
1
那么你做错了。如果你缺少typedef的最后一部分(例如typedef struct api_handle_t*),就会出现这样的消息。 - Mestkon
你是对的,我做错了 - 现在已经解决了!非常感谢你的帮助! - Sharon
显示剩余3条评论

5

基本上,你的C++库需要导出一个纯C API。也就是说,它必须提供仅依赖于typedefstructenum、预处理指令/宏(以及我可能忘记提到的一些东西,但它们必须都是有效的C代码)。如果没有这样的接口,你就无法将C代码与C++库链接。

这个纯C API的头文件需要同时可编译C和C++编译器,然而,在用C++编译它时,你必须告诉C++编译器它是一个C接口。这就是为什么你需要将整个API封装在

extern "C" {
    //C API
}

当作为C++编译时,需要使用extern "C"来保证符号能够正确链接。但是,这段代码不是C语言代码,因此您必须将extern "C"从C编译器中隐藏起来。为此,需要添加预处理指令。

#ifdef __cplusplus1
extern "C" {
#endif

    //C API

#ifdef __cplusplus1
}
#endif

如果您无法更改库的头文件,您需要创建一个包装器API,该API提供此纯C API并调用相应的C ++代码。

谢谢你的帮助。我最终使用了Mestkon的解决方案,但我很感激你的时间。 - Sharon

3

如何从C语言中调用C++函数?

需要编写能够在C和C++共同子集中声明有效的调用函数,并在C++中使用C语言链接声明这些函数。

我的问题是api.h文件包含对命名空间的引用

这样的头文件不是在C和C++共同子集中编写的,因此无法从C中使用。您需要编写一个有效的C头文件才能在C中使用它。

我需要使用C++编译吗(即使用g ++)?

如果您的函数定义是用C++编写的,则需要使用C++编译器编译这些C++函数。如果您有C函数调用这些C++函数,则需要使用C编译器编译这些C函数。


一个最简示例:

// C++
#include <iostream>

extern "C" void function_in_cpp() {
    std::cout << "Greetings from C++\n";
}

// C
void function_in_cpp(void);

void function_in_c(void) {
    function_in_cpp();
}

0

不行。你可以在C++程序中使用C函数,但是不能从C中使用C++的东西。当C++被发明时,它允许与C函数兼容和重用,因此它被编写为C的超集,允许C++调用所有C库函数。

但反过来不行。当C被发明时,没有定义C++语言。

唯一的方法是将整个项目转换为C++项目...你需要使用C++编译器(或者如果它们是纯C,则使用C编译器)编译你的C函数,但是对于一个C函数调用C++函数,它必须被编译为C++。你应该这样声明:

extern "C" {

void my_glue_func(type1 param1, type2 param2, ...)
{
    /* ... */
}

} /* extern "C" */

并将整个内容链接为一个C ++程序(调用C ++链接器)

这是因为C不知道任何关于函数重载,类初始化,实例构造函数调用等的信息。因此,即使您可以解开C ++函数的名称以便从C调用它们(最好不要尝试此操作),它们可能会运行未初始化,因此您的程序可能会(大多数情况下)崩溃。

如果您的main()函数恰好是C函数,则没有问题。 C ++是考虑到这种情况而设计的,因此,main()被声明为外部"C"。 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接