惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
D
Docker
J
Java Code Geeks
腾讯CDC
Blog — PlanetScale
Blog — PlanetScale
G
Google Developers Blog
M
MIT News - Artificial intelligence
L
LangChain Blog
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
博客园 - Franky
GbyAI
GbyAI
Hugging Face - Blog
Hugging Face - Blog
aimingoo的专栏
aimingoo的专栏
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
N
Netflix TechBlog - Medium
B
Blog RSS Feed
Y
Y Combinator Blog
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News

Пусть этот камень будет более крепким, чем человек

【MLIR】Transform Dialect【0】保持计算结构 【MLIR】跟着Toy语言学习MLIR【6】支持复合结构类型 【MLIR】跟着Toy语言学习MLIR【5】降级到LLVM及CodeGen 【MLIR】跟着Toy语言学习MLIR【4】部分降级到低层方言 【MLIR】跟着Toy语言学习MLIR【3】通过接口实现通用转换 【MLIR】跟着Toy语言学习MLIR【2】pattern匹配和重写 【MLIR】跟着Toy语言学习MLIR【1】Toy语言和 Toy Dialect 【琐记】你的爱好能坚持多少年 【琐记】烟火与尘埃 【Triton】Triton实现矩阵乘 【LLM推理加速】FlashAttention 【LLM推理加速】PagedAttention 【LLM推理加速】Online Softmax LLM基础知识【1】 Transformer模型 【AI编译】LayerGroup Tiling Tile的疑惑和思考 【AI编译】深度优先的Tile调度,万事大吉? 【AI编译】多级流水线Tile调度策略 【CUDA C++】GPU内存使用【3】 【AI编译】Cache缓存地址映射 【CUDA C++】GPU存储【2】 【CUDA C++】GPU基本介绍【1】 【00】0序章-不受欢迎的来客 【转载】我来了——持续低熵 【Halide】调度优化【2】 【感想】写作进度报告5 【Halide】调度优化【1】 【转载】北大中文男足战报2 【BYOC】TVM切分子图 【转载】北大中文男足战报1
C语言工程调用Cpp库解决方案
Post author: XianMu@Пусть этот камень будет более крепким, чем ч · 2024-05-14 · via Пусть этот камень будет более крепким, чем человек

本文为 C 语言工程调用 C++ 库的解决方案。

应用场景: 需要 C++ 程序编译成的库提供函数接口,来解决 C 语言工程的需求。

想要快速解决问题,直接看 三、通用解决方法

人的出场顺序真的很重要,很多人如果换一个时间认识,换一个时间共处,一切都将是不一样的场景,不一样的结局。所以,人生有无限种可能,我的人生,是现在这一种。感谢大家恰到好处的出现,组成我最好的一种可能。
------ 大家好啊 我是 暮冬 Z 羡慕

C++ 库由自己编写,可以决定头文件书写的位置

#include "hello.h"
#include "iostream" 
using namespace std;
void sayHello(){
    cout<< " # iostream: i am saying hello !" << endl;
    printf(" # c: i am saying hello !\n");
}

创建 hello.cpp 文件,实现 sayHello () 功能,分别用 C++ 标准库和 C 标准库的输入输出功能打印 hello!

①需要注意的是 “将 C++ 标准库放在该 cpp 文件中”,原因后续指出。

#include "stdio.h"  
void sayHello();

创建头文件 hello.h 这里可以添加 C 语言标准库,但是不要把 C++ 标准库放在这里。

以上 hello.h 和 hello.cpp 模拟了 C++ 库。为了使 C 语言工程能够调用该库,需要增加一个中间层

#include "helloWapper.h"
void Wapper_sayHello(){
        sayHello();
}

创建中间层 helloWapper.cpp , 对想要使用的 C++ 库函数进行封装,即: 通过 Wapper_sayHello () 调用 sayHello ()

#include "hello.h"
#ifdef __cplusplus
extern "C"{
#endif
    void Wapper_sayHello();
#ifdef __cplusplus
}
#endif

创建中间层头文件 helloWapper.h,暴露 Wapper_sayHello () 接口。中间出现的 extern "C" {} 是告诉 G++ 编译器,对中间的函数按照 C 语言的方式进行编译。

然后将上述两个 CPP 文件编译成 静态库 ,使用的 CMakeLists.txt 文件如下:

cmake_minimum_required (VERSION 3.5)
project (demo)
include_directories (${PROJECT_SOURCE_DIR}/include ) 
set (test_call_LIST ${PROJECT_SOURCE_DIR}/src/hello.cpp
                    ${PROJECT_SOURCE_DIR}/src/helloWapper.cpp)  
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 
add_library(hello STATIC ${test_call_LIST}) 

所有文件结构如下:
文件结构图

在工程根目录输入

mkdir build & cd build
cmake .. 
make

bin 文件夹下就会出现 封装好的库 libhello.a

创建 mian.c 来模拟 C 语言工程:

#include "stdio.h"
#include "helloWapper.h"  
int main(){
    Wapper_sayHello();
    return 0;
}

A:通过命令行进行编译运行

回到工程根目录,编译 main.c , 运行:

cd ..
gcc -o Hello ./src/main.c -I./include -L./bin -lhello -lstdc++
./Hello

B:通过 Cmake 编译运行

在工程根目录,将 CMakeList.txt 内容替换为:

cmake_minimum_required (VERSION 3.5)
project (demo)
include_directories (${PROJECT_SOURCE_DIR}/include)  
find_library(Hello_LIB hello HINTS ${PROJECT_SOURCE_DIR}/bin) 
set (hello_List ${PROJECT_SOURCE_DIR}/src/main.c)
add_executable (Hello ${hello_List})  
target_link_libraries (Hello ${Hello_LIB} stdc++)
cd build && cmake ..
make
./Hello

A B 两种方式均输出结果: 至此 C 语言工程能够成功调用 C++ 库

输出结果

以上 A B 两种方式中均出现 stdc++ ,一般以 libstdc++.so 的方式存在,是 C++ 标准库。

可以看下图 “G++ and GCC”

 “G++ and GCC”

GCC 在编译时不会自动链接 C++ 标准库, 因此 hello.cpp 用到的 类似 "iostream" 等 C++ 标准库需要手动链接,否则会出现以下错误:

找不到C++标准库错误

# C++ 库为第三方库,无法编辑的情况

在《一、C 库可以编辑的情况》 中提到 【①需要注意的是 “ 将 C 标准库放在该 cpp 文件中”】 是因为 GCC 编译不仅找不到 C 标准库 stdc ,也找不到 C++ 标准库的头文件。如果在 hello.h 中引用 “iostream", 那么用 GCC 编译 C 语言工程时,会报找不到头文件错误。

#include "stdio.h"  
#include "iostream"  
void sayHello();

但是当我们想用的 C 库为第三方库,而它在头文件里引用了大量 C 标准库的情况下,该如何处理呢?

再看图 “G++ and GCC”,里面指明 G 编译器能够编译 C 和 C 文件,且能够自动链接 C 标准库。所以在这种情况下,只需要在编译 C 语言工程的时候,指定 G 为编译器(编译 C 文件默认使用的是 GCC 编译器)就可以了。

下面是与《一、C++ 库可以编辑的情况》相似的总体流程(有修改的地方会有注释):

#include "hello.h"  
using namespace std;
void sayHello(){
    cout<< " # iostream: i am saying hello !" << endl;
    printf(" # c: i am saying hello !\n");
}

创建 hello.cpp 文件,实现 sayHello () 功能,分别用 C++ 标准库和 C 标准库的输入输出功能打印 hello!

#include "stdio.h" 
#include "iostream"  
void sayHello();

创建头文件 hello.h
以上 hello.h 和 hello.cpp 模拟了 C++ 库。为了使 C 语言工程能够调用该库,需要增加一个中间层:

#include "helloWapper.h"
void Wapper_sayHello(){
        sayHello();
}

创建中间层 helloWapper.cpp , 对想要使用的 C++ 库函数进行封装,即: 通过 Wapper_sayHello () 调用 sayHello ()

#include "hello.h"
#ifdef __cplusplus
extern "C"{
#endif
    void Wapper_sayHello();
#ifdef __cplusplus
}
#endif

创建中间层头文件 helloWapper.h,暴露 Wapper_sayHello () 接口。中间出现的 extern "C" {} 是告诉 G++ 编译器,对中间的函数按照 C 语言的方式进行编译。
然后将上述两个 CPP 文件编译成 静态库 ,使用的 CMakeLists.txt 文件如下:

cmake_minimum_required (VERSION 3.5)
project (demo)
include_directories (${PROJECT_SOURCE_DIR}/include ) 
set (test_call_LIST ${PROJECT_SOURCE_DIR}/src/hello.cpp
                    ${PROJECT_SOURCE_DIR}/src/helloWapper.cpp)  
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 
add_library(hello STATIC ${test_call_LIST}) 

文件结构还是图:文件结构图

在工程根目录输入

mkdir build & cd build
cmake .. 
make

bin 文件夹下就会出现 封装好的库 libhello.a
创建 mian.c 来模拟 C 语言工程:

#include "stdio.h"
#include "helloWapper.h"  
int main(){
    Wapper_sayHello();
    return 0;
}

A:通过命令行进行编译运行
回到工程根目录,编译 main.c , 运行:
编译器使用了 g++ ,可以自动搜索 C 标准库路径及链接 C 标准库,因此不需要再加 “-lstdc++”。

cd ..
g++ -o Hello ./src/main.c -I./include -L./bin -lhello
./Hello

B:通过 Cmake 编译运行
在工程根目录,将 CMakeList.txt 内容替换为:

cmake_minimum_required (VERSION 3.5)
project (demo)
SET(CMAKE_C_COMPILER "g++")  
include_directories (${PROJECT_SOURCE_DIR}/include)  
find_library(Hello_LIB hello HINTS ${PROJECT_SOURCE_DIR}/bin) 
set (hello_List ${PROJECT_SOURCE_DIR}/src/main.c)
add_executable (Hello ${hello_List})
target_link_libraries (Hello ${Hello_LIB})

编译、执行:

cd build && cmake ..
make
./Hello

# 通用方法

可以忽略方法一和二。方法三可以采用 GCC 编译调用了 C 库的 C 语言工程。
解决的方法是: 在 中间层 helloWapper.cpp 中引用 C
库头文件 #include "hello.h", 而不是在 helloWapper.h 中引用

例子:
创建 C++ 库源文件

#include "hello.h"  
using namespace std;
void sayHello(){
    cout<< " # iostream: i am saying hello !" << endl;
    printf(" # c: i am saying hello !\n");
}

创建头文件 hello.h

#include "stdio.h" 
#include "iostream" 
void sayHello();

以上 hello.h 和 hello.cpp 模拟了 C++ 库。为了使 C 语言工程能够调用该库,需要增加一个中间层

其中注意:一定要将 C 库的头文件 "hello.h" 加在 中间层的.cpp 文件中, 而不是放在中间层.h 文件中。这种情况下能够将 "hello.h" 等所有 C 库头文件编译到库当中,防止最后采用 GCC 编译时找不到 g++ 标准库的头文件。

#include "hello.h"  
#include "helloWapper.h"
void Wapper_sayHello(){
        sayHello();
}
#ifdef __cplusplus  
extern "C"{
#endif
    void Wapper_sayHello();
#ifdef __cplusplus
}
#endif

创建中间层头文件 helloWapper.h,暴露 Wapper_sayHello () 接口。中间出现的 extern "C" {} 是告诉 G++ 编译器,对中间的函数按照 C 语言的方式进行编译。

然后将上述两个 CPP 文件编译成 静态库 ,使用的 CMakeLists.txt 文件如下:

cmake_minimum_required (VERSION 3.5)
project (demo)
include_directories (${PROJECT_SOURCE_DIR}/include ) 
set (test_call_LIST ${PROJECT_SOURCE_DIR}/src/hello.cpp
                    ${PROJECT_SOURCE_DIR}/src/helloWapper.cpp)  
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 
add_library(hello STATIC ${test_call_LIST}) 

文件结构还是图:文件结构图

在工程根目录输入

mkdir build & cd build
cmake .. 
make

bin 文件夹下就会出现 封装好的库 libhello.a
创建 mian.c 来模拟 C 语言工程:

#include "stdio.h"
#include "helloWapper.h"  
int main(){
    Wapper_sayHello();
    return 0;
}

A:通过命令行进行编译运行
回到工程根目录,编译 main.c , 运行:
编译器使用了 gcc ,需要添加 C 标准库 “ -lstdc”(标准库的头文件已经在 C++ 库中了)。

cd ..
gcc -o Hello ./src/main.c -I./include -L./bin -lhello -lstdc++
./Hello

B:通过 Cmake 编译运行
在工程根目录,将 CMakeList.txt 内容替换为:

cmake_minimum_required (VERSION 3.5)
project (demo)
include_directories (${PROJECT_SOURCE_DIR}/include)  
find_library(Hello_LIB hello HINTS ${PROJECT_SOURCE_DIR}/bin) 
set (hello_List ${PROJECT_SOURCE_DIR}/src/main.c)
add_executable (Hello ${hello_List})
target_link_libraries (Hello ${Hello_LIB} stdc++)

编译、执行:

cd build && cmake ..
make
./Hello

方法三是最通用的解决方法,不仅仍然可以使用 GCC 编译,而且想要在其他地方调用 C++ 库时,需要的头文件只有 “helloWapper.h” 一个。

# 后记

本博客目前以及可预期的将来都不会支持评论功能。各位大侠如若有指教和问题,可以在我的 github 项目 或随便一个项目下提出 issue,并指明哪一篇博客,我看到一定及时回复!