20

AI老铁 · 2020年07月29日

海思AI芯片(Hi3519A/3559A)方案学习(二十一)extern "C"和C/C++混合编程

本系列为华为海思海思AI芯片(Hi3519A/3559A)方案学习系列之二十一,系列文章请关注海思AI芯片方案学习
作者:ltshan139

前言

在重构sample sdk代码,并编译成静态或动态库来提供给上层调用的API时,不可避免会遇到c++调用c或c调用c++的问题。看我博客的网友也提到了这个问题,所以藉这个机会来把C和C++混合编程(即相互调用)的问题透彻的弄清楚。

问题背景

C和C++直接相互调用 之所以会出问题的原因是, C++里面有函数重载的概念,从而导致编译出来的函数名会带上参数类型,而C编译出来的函数名简单的就是其本身。

下面定义了两个简单的c、c++头文件和实现文件

/*test_c.h*/
#ifndef TEST_C
#define TEST_C
 
int add(int a, int b);
 
#endif
/*test_c.c*/
#include "test_c.h"
 
int add(int a, int b)
{
    int c;
    c = a + b;
    return c;
}
/*test_c++.h*/
#ifndef TEST_C_PLUSPLUS
#define TEST_C_PLUSPLUS
 
int substract(int a, int b);
 
#endif
/*test_c++.cpp*/
#include "test_c++.h"
 
int substract(int a, int b)
{
    int c;
    c = a - b;
    return c;
}

分别使用两个命令生成obj文件。

gcc -c test_c.c
gcc -c test_c++.cpp

然后使用命令: nm test_c.o | grep add 和 nm test_c++.o | grep substract 得到如下结果:

add 和 _Z9substractii。 这个结果进一步验证了前面所说的,即C和C++编译出来的函数名会不一样。

C调用C++

添加一个C测试文件main.c,并调用add和substract API。

/*main.c*/
#include <stdio.h>
#include "test_c.h"
#include "test_c++.h"
 
int main(void)
{
    int x = 3;
    int y = 5;
 
    printf("add = %d \n", add(x, y));
    printf("sub = %d \n", substract(x, y));
 
    return 0;
}

使用命令如下来将它们编译成一个可执行文件test_c:

gcc test_c.c test_c++.cpp main.c -o test_c

但这个时候会报链接错误: main.c:(.text+0x43): undefined reference to 'substract' 。明明我们在test_c++.cpp里面定义了substract(),但是main.c不识别。 这也说明了 c直接调用c++代码出现错误。

解决办法就是引入 extern "C"。 关于它有两个重要注意点:

1)extern "C"是c++语法,只能被c++编译器认识,其目的就是告诉C++编译器,其被extern "C"所包含的函数得以C方式(即简单函数名)去链接。

2) 为了避免C编译器来编译它来产生编译错误, 保险起见,会将 extern "C" {和}用#ifdef __cplusplus... #endif修饰起来。这里很容易犯得一个错误就是 cplusplus前面是两根下划线"_"。

回到上面得错误,我们得在test_c++.cpp编译时,得用c方式去来生成substract函数。所以test_cpp.h更新如下,并用__cplusplus保护起来,避免main.c编译时报错。

/*test_c++.h*/
#ifndef TEST_C_PLUSPLUS
#define TEST_C_PLUSPLUS
 
#ifdef __cplusplus
extern "C" {
#endif
 
int substract(int a, int b);
 
#ifdef __cplusplus
}
#endif
 
#endif

再次编译链接: gcc test_c.c test_c++.cpp main.c -o test_c没有问题,并执行./test_c 结果也完全正确。

C++调用C
添加一个C++测试文件main.cpp来调用add和substract。

/*main.cpp*/
#include <stdio.h>
#include "test_c.h"
#include "test_c++.h"
 
int main(void)
{
    int x = 3;
    int y = 5;
 
    printf("add = %d \n", add(x, y));
    printf("sub = %d \n", substract(x, y));
 
    return 0;
}

使用类似命令来编译链接: gcc test_c.c test_c++.cpp main.cpp -o test_cpp,结果也出现main.cpp里面找不到定义在test_c.c里面得add函数。

同样地,必须在test_c.h里面添加extern ”C“来告诉main.cpp编译链接时得以c方式来寻找add函数。同时使用__cplusplus修饰避免 test_c.c编译失败。

/*test_c.h*/
#ifndef TEST_C
#define TEST_C
 
#ifdef __cplusplus
extern "C" {
#endif
 
int add(int a, int b);
 
#ifdef __cplusplus
}
#endif
#endif

使用编译链接命令:gcc test_c.c test_c++.cpp main.cpp -o test_cpp 没有问题,并执行./test_cpp 结果也完全正确。

结论

无论是C++调用C api 还是C调用C++ API, 必须先在API所对应得头文件对API进行 extern "C"声明,并用#ifdef __cplusplus进行保护。



海思AI芯片系列文章



更多海思AI芯片方案学习笔记欢迎关注海思AI芯片方案学习

推荐阅读
关注数
867
内容数
40
海思AI芯片(Hi3519A/3559A)方案学习系列笔记,欢迎关注。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息