基于CMake,交叉编译C、C++

发布于 2023-10-31 11:55
浏览
0收藏

嵌入式开发,尤其SOA(Service Oriented Architecture,面向服务架构)的工程开发中,会碰到这样一种的场景:一部分业务功能使用C开发,且比较成熟,想直接复用。但是,业务层获取的底层服务是C++编写,如果要将两者放到一个进程里,需要将C、C++对应的源码交叉编译生成可执行文件。本文,针对C和C++交叉编译展开聊聊。

1、C与C++区别

首先,我们知道:C是面向过程(Procedure Oriented,简称PO)的编程语言,而C++是面向对象(Object Oriented ,简称OO)的编程语言。C++可以看作C的超集,C++包含了更多的特性。


(一)PO与OO的区别


  • PO优势:效率更高,因为PO语言(eg:C语言)不用实例化对象,消耗的资源更少;
  • PO不足:移植性差,维护成本高。
  • OO优势:复用性高,维护成本低;
  • OO不足:效率低,使用之前,需要先实例化一个对象(Object)。

2、编译原理

不管是C还是C++,这些易于人类阅读的高级语言,终究要进行编译,最终转换成机器可识别的二进制语言

编译一般分为四个步骤预处理->编译->汇编->链接


(一)预处理


预处理由预处理器(Preprocessor)处理,删除注释,引入头文件或者包,将宏定义内容在源文件(*.c、*.cpp等)中进行替换。


(二)编译


汇编阶段,编译器(Compiler)将经过预处理的高级语言翻译成机器可识别的汇编语言。


(三)汇编


由汇编器(Assembler),将汇编语言转换成机器语言(二进制文件),并进行重定位(Relocatable)。再由加载器(Loader)将重定向后的指令数据放到内存指定位置


(四)链接


最终,由链接器(Linker)将多个可重定位的机器代码文件(包括库文件)连接到一起形成可执行文件。


编译过程,如下所示:

基于CMake,交叉编译C、C++ -汽车开发者社区

3、基于CMake,进行C、C++交叉编译

说明:主函数(main.cpp)使用C++编写,并在main()中调用C编写的add()接口。文件结构如下所示:

基于CMake,交叉编译C、C++ -汽车开发者社区

提示:Windows下,获取某个文件夹下的文件结构,可以使用命令:Get-ChildItem . -recurse

(一)文件内容


1、Cal.h文件

 #ifndef _CAL_H_
 #define _CAL_H_
 
     typedef int(*calOpt)(int, int);
 
     #ifdef __cplusplus
     extern "C"
     {
     #endif
 
         extern int add(int x,int y);
 
     #ifdef __cplusplus
     }
     #endif
 
 #endif

如上,为了实现C和C++的混合编程,需要在头文件中,使用extern "C"。extern “C”是C++提供的一个连接交换指定符号,用于告知编译器:这是C编写的函数。extern “C”后面声明的函数,不再使用C++编译的修饰符。因为C++编译后,函数名会增加额外的修饰符,而C语言生成的函数不含额外的修饰符,因此,两者对同一个函数编译后的名称不同,这样,就使得C++无法直接调用C函数。


2、Cal.c文件

 #include "Cal.h"
 
 int add(int x,int y)
 {
     return (x + y);
 }

与Cal.c相同文件夹(src)下的CMakeLists.txt文件内容如下所示:

 # 查找当前目录下的所有源文件,并将名称保存到 CUR_DIR_SRCS 变量
 aux_source_directory(. CUR_DIR_SRCS)
 # 生成链接库
 add_library (Cal ${CUR_DIR_SRCS})
 
 target_include_directories(Cal PUBLIC "../include/")

3、main.cpp文件

 #include <iostream>
 #include "Cal.h"
 
 int main(){
 
     int result;
     calOpt optFunc = add;
     /* 通过函数指针调用add函数 */
     result = optFunc(0x10, 0x20);
 
     std::cout << "result = " << result << std::endl;
     return 0;
 }

与main.cpp相同文件夹下的CMakeLists.txt文件内容如下所示:

 # cmake最低版本要求
 cmake_minimum_required(VERSION 3.10)
 
 # 设置项目名称和版本号
 project(CMake_Pro)
 
 # 设置使用C++标准版本
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED True)
 
 set(ENV{myEnvVar} "80")
 
 MESSAGE(STATUS "myEnvVar: $ENV{myEnvVar}")
 # 添加src子目录
 add_subdirectory(src)
 
 # 编译源码生成目标
 add_executable(CMake_Pro main.cpp)
 # 包含头文件路径
 target_include_directories(CMake_Pro PUBLIC "./include/")
 # 添加链接库
 target_link_libraries(CMake_Pro Cal)

4、运行编译的批处理脚本(*.bat)

 @echo off
 
 set buildDir=build
 
 @REM build的文件夹不存在则创建
 if not exist %buildDir% md %buildDir%
 
 if exist %buildDir% cd %buildDir%
 echo "***************:Execute cmake ***************"
 cmake -G"Unix Makefiles" ../
 echo "***************:Execute make ***************"
 make 
 
 pause

(二)编译


运行CMake_Build.bat脚本,编译过程如下所示:

基于CMake,交叉编译C、C++ -汽车开发者社区

可以看出,如上编译过程中,先将Cal.c文件编译成静态库文件(libCal.a),最终在链接的时候,链接器将其链接成一个完整的可执行文件。这样处理的好处:保留了原有C文件的高效。


运行结果:

基于CMake,交叉编译C、C++ -汽车开发者社区


文章转载自公众号:开心果 Need Car

分类
收藏
回复
举报
回复
相关推荐