Skip to content

Wiki

Functions and Header Files

About 35 min read

Existing English translations were reused. 16 current source blocks remain in Simplified Chinese, without triggering paid translation.

1.function

1.1.Overview

**Purpose:** Encapsulate a frequently used piece of code to reduce duplicate code

A relatively large program is generally divided into several program blocks, with each module implementing a specific function.

1.2.Definition of Function

The definition of a function generally consists of 5 main steps:

1. Return value type

2. Function name

3. Parameter list

4. Function body statements

5. Return expression

Syntax:

返回值类型 函数名 (参数列表)
{

       函数体语句

       return表达式

}

Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.

  • Return type: A function can return a value. In the function definition,
  • Function name: give the function a name
  • Parameter list: the data passed in when using the function
  • Function body statements: the code within the curly braces, the statements that need to be executed inside the function
  • return expression: linked to the return type, after the function finishes executing, it returns the corresponding data

**Example:** Define an addition function to add two numbers together

//函数定义
int add(int num1, int num2)
{
	int sum = num1 + num2;
	return sum;
}

Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.

1.3.Function call

**Function:** Use a defined function

Syntax: 函数名(参数)

Example:

//函数定义
int add(int num1, int num2) //定义中的num1,num2称为形式参数,简称形参
{
	int sum = num1 + num2;
	return sum;
}

int main() {

	int a = 10;
	int b = 10;
	//调用add函数
	int sum = add(a, b);//调用时的a,b称为实际参数,简称实参
	cout << "sum = " << sum << endl;

	a = 100;
	b = 100;

	sum = add(a, b);
	cout << "sum = " << sum << endl;


	return 0;
}

Operation result:

sum = 20
sum = 200

Summary: In a function definition, the parameters inside the parentheses are called formal parameters, and the arguments passed in when the function is called are called actual parameters.

1.4.Pass by value

  • So-called pass-by-value means that when a function is called, the actual argument passes its value to the formal parameter.
  • In pass-by-value, ==if the formal parameter is modified, it will not affect the actual argument==.

Example:

void swap(int num1, int num2)
{
	cout << "交换前:" << endl;
	cout << "num1 = " << num1 << endl;
	cout << "num2 = " << num2 << endl;

	int temp = num1;
	num1 = num2;
	num2 = temp;

	cout << "交换后:" << endl;
	cout << "num1 = " << num1 << endl;
	cout << "num2 = " << num2 << endl;

	//return ; 当函数声明时候,不需要返回值,可以不写return
}

int main() {

	int a = 10;
	int b = 20;

	swap(a, b);

	cout << "mian中的 a = " << a << endl;
	cout << "mian中的 b = " << b << endl;


	return 0;
}

Operation result:

交换前:
num1 = 10
num2 = 20
交换后:
num1 = 20
num2 = 10
mian中的 a = 10
mian中的 b = 20

Summary: In pass-by-value, the formal parameter cannot modify the actual parameter.

1.5.Common styles of functions

There are 4 common function styles.

  1. No parameters, no return
  2. With parameters, no return
  3. No parameters, with return
  4. With parameters, with return

Example:

//函数常见样式
//1、 无参无返
void test01()
{
	//void a = 10; //无类型不可以创建变量,原因无法分配内存
	cout << "this is test01" << endl;
	//test01(); 函数调用
}

//2、 有参无返
void test02(int a)
{
	cout << "this is test02" << endl;
	cout << "a = " << a << endl;
}

//3、无参有返
int test03()
{
	cout << "this is test03 " << endl;
	return 10;
}

//4、有参有返
int test04(int a, int b)
{
	cout << "this is test04 " << endl;
	int sum = a + b;
	return sum;
}

Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.

1.6.Function declaration

作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

  • A function'sdeclaration can appear multiple timesbut a function'sdefinition can appear only once

Example:

//声明可以多次,定义只能一次
//声明
int max(int a, int b);
int max(int a, int b);
//定义
int max(int a, int b)
{
	return a > b ? a : b;
}

int main() {

	int a = 100;
	int b = 200;

	cout << max(a, b) << endl;


	return 0;
}

Operation result:

200

1.7.Splitting functions across files

**Purpose:** Make the code structure clearer

In the previous chapters, we mainly wrote code in src/main.cpp. This is suitable for learning syntax, but once the program gets a bit larger, it becomes very messy.

Writing functions in separate files means splitting the code into:

  1. Header file .h: Write function declarations.
  2. Source file .cpp: Write function definitions.
  3. main.cpp : Call functions.

From C++Development Environment and the First Project onward, this tutorial has been using the same CMake template. At this point, your project is roughly still in this structure:

src/
├── CMakeLists.txt
├── main.cpp
├── lib1/
│   ├── CMakeLists.txt
│   ├── inc/lib1/eigen3_test.hpp
│   └── src/eigen3_test.cpp
└── lib2/
    ├── CMakeLists.txt
    ├── inc/lib2/eigen3_test.hpp
    └── src/eigen3_test.cpp

lib1 and lib2 are two example libraries included with the template. This section organizes them for the first time: delete the lib2 that is not needed for now, change lib1 to our own swap small library, then rename the example header file and source file included with the template to swap.h and swap.cpp respectively, and use them to complete the function file-splitting example in this section.

After the modification, the directory structure becomes:

src/
├── CMakeLists.txt
├── main.cpp
└── swap/
    ├── CMakeLists.txt
    ├── inc/swap/swap.h
    └── src/swap.cpp

In other words:

  1. Delete the lib2 that is temporarily not used in this example.
  2. Rename the lib1 directory to swap.
  3. Rename the inc/lib1 directory to inc/swap and then rename the eigen3_test.hpp within it to swap.h.
  4. Rename src/eigen3_test.cpp to src/swap.cpp.

1.7.1.Organize lib1 and lib2

First deletelib2, this is not needed here.

alt text

  1. Rename the lib1 directory to swap:
  2. Rename the inc/lib1 directory to inc/swap,then rename the eigen3_test.hpp in it to swap.h:
  3. Rename the src/eigen3_test.cpp to src/swap.cpp:

alt text

1.7.2.Modify swap.h

Open:

src/swap/inc/swap/swap.h

Write:

#include<iostream>
using namespace std;

void swap(int a, int b);

alt text

This is the first time we have placed a function declaration separately into a header file.swap.h It is only responsible for telling other source files: there exists in the project a function named swap, which takes two int parameters.

Supplementary note: In real engineering, it is usually not recommended to write using namespace std; in header files, and function names should also avoid being directly called swap, because the standard library contains std::swap. However, here we will first focus on understanding the line of "header file declaration, source file definition, main function call", so this simple teaching code is used temporarily.

1.7.3.Modify swap.cpp

Open:

src/swap/src/swap.cpp

Write:

#include "swap/swap.h"

void swap(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

alt text

Here, #include "swap/swap.h" indicates: swap.cppthe function declaration must be seen before the function definition is provided.

At this point, it does not matter if there are VSCode errors, becauseCMakeListshave not been modified, and secondlyclangdhave also not been refreshed. For now, do not worry about the errors shown by the code hints.

1.7.4.Modify main.cpp

Open:

src/main.cpp

Write:

#include "swap/swap.h"

int main()
{
	int a = 100;
	int b = 200;

	swap(a, b);

	return 0;
}

Running result (compile and link together with the above swap.cpp):

a = 200
b = 100

alt text

main.cppOnly responsible for calling functions, no longer writing swap the specific implementation of the function inside itself.

1.7.5.Modify src/swap/CMakeLists.txt

Open:

src/swap/CMakeLists.txt

This file originally came from lib1, so it still contains traces of lib1 and Eigen examples.

First, change the first line from:

set(PREFIX "lib1")

Change to:

set(PREFIX "swap")

This way, CMake will create one named:

swap_src_lib

library.

swap This example does not require Eigen, so delete the Eigen dependency in the Third-party dependencies block:

find_package(Eigen3 REQUIRED)

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    Eigen3::Eigen
)

After rearrangement,src/swap/CMakeLists.txt it can be written as:

set(PREFIX "swap")

file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
  "${CMAKE_CURRENT_LIST_DIR}/src/*.c"
  "${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)

add_library(${PREFIX}_src_lib
  ${${PREFIX}_SRC_LIST}
)

target_include_directories(${PREFIX}_src_lib
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/inc>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

target_link_libraries(${PREFIX}_src_lib
  PUBLIC
    project_options
  PRIVATE
    project_warnings
)

# ========================
# Third-party dependencies
# ========================

# =======
# Install
# =======

install(TARGETS ${PREFIX}_src_lib
  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/inc/"
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

The most important lines here are:

file(GLOB_RECURSE ${PREFIX}_SRC_LIST CONFIGURE_DEPENDS
  "${CMAKE_CURRENT_LIST_DIR}/src/*.c"
  "${CMAKE_CURRENT_LIST_DIR}/src/*.cpp"
)

它会把 src/swap/src/ 里面的 .cpp 文件收集进来,所以 swap.cpp 会参与编译。

Also:

target_include_directories(${PREFIX}_src_lib
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/inc>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

It adds src/swap/inc/ to the header file search path, so the code can write:

#include "swap/swap.h"

Note: Do not write #include "inc/swap/swap.h". CMake has already passed src/swap/inc/ to the compiler, so when including, continue writing after inc/, that is, swap/swap.h.

The complete CMake syntax will be explained systematically in the CMake project template. For now, it is enough to know that "header files go into inc/, source files go into src/, and the library name is determined by PREFIX".

1.7.6.Modify src/CMakeLists.txt

Open:

src/CMakeLists.txt

Originally, the template contains:

add_subdirectory(lib1)
add_subdirectory(lib2)

Change to:

add_subdirectory(swap)

原来的模板里允许lib1调用lib2,现在lib2没了,所以删掉这部分:

target_link_libraries(lib1_src_lib
  PRIVATE
    lib2_src_lib
)

Delete the one above directly.

So the template also links to:

target_link_libraries(${PROJECT_NAME}
  PRIVATE
    lib1_src_lib
    lib2_src_lib
)

Change to:

target_link_libraries(${PROJECT_NAME}
  PRIVATE
    swap_src_lib
)

Only the above three items need to be modified; all others do not need to be changed. The following introduces the more important points:

After reorganization, src/CMakeLists.txt can be written as:

add_executable(${PROJECT_NAME}
  ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)

target_link_libraries(${PROJECT_NAME}
  PRIVATE
    project_options
    project_warnings
)

add_subdirectory(swap)

target_link_libraries(${PROJECT_NAME}
  PRIVATE
    swap_src_lib
)

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  set_target_properties(${PROJECT_NAME} PROPERTIES
    INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}"
  )
endif()

install(TARGETS ${PROJECT_NAME}
  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

These two points are the most critical:

add_subdirectory(swap)

Meaning: have CMake enter src/swap/ and read src/swap/CMakeLists.txt.

target_link_libraries(${PROJECT_NAME}
  PRIVATE
    swap_src_lib
)

Meaning: the main program calls the swap function, so the main program must link swap_src_lib; otherwise, the linker cannot find the definition of the swap function.

1.7.7.Rebuild and run

修改完 CMake 文件后,先重新 Configure,再 Build 和运行。命令都在工程根目录执行。

Linux:

cmake --preset linux-debug
cmake --build --preset linux-debug
./build/linux-debug/src/cmake_template

Windows,PowerShell:

cmake --preset windows-debug
cmake --build --preset windows-debug
./build/windows-debug/bin/cmake_template.exe

也可以继续用 VSCode 的 CMake Tools 按钮完成相同操作。下面保留 Linux 的操作截图,Windows 选择自己的 windows-debug 预设即可。

Compile

alt text

At this point, if you look at clangd's code hints again, it will no longer report errors.

alt text

Then run:

alt text

Output similar to:

a = 200
b = 100

It can be seen that the function in swap.cpp was indeed called by the main program.

1.7.8.Core Understanding of Writing in Separate Files

In this template, writing functions in separate files is not just about splitting the files apart; CMake must also be made aware of which library these files belong to.

The correspondence is as follows:

File Role
src/swap/inc/swap/swap.h Write function declarations so other .cpp files know how to call them
src/swap/src/swap.cpp Write function definitions to provide the actual implementation
src/swap/CMakeLists.txt Compile swap.cpp into swap_src_lib
src/CMakeLists.txt Link swap_src_lib to the main program
src/main.cpp #include "swap/swap.h" call after swap(a, b)

If it is omitted, the compiler does not know the declaration of the #include "swap/swap.h" function, and the declaration of the swap function.

如果漏了 add_subdirectory(swap),CMake 不会进入 src/swap。

If missed:

target_link_libraries(${PROJECT_NAME}
  PRIVATE
    swap_src_lib
)

The linker cannot find the implementation of the swap function.

2.Organization of header files

We have just placed the declaration of the swap function into swap.h. As long as other source files include this header file, they can know which functions this library provides. These functions, types, and variables provided for external code to use are generally collectively referred to as the library's API.

Header files are generally responsible for containing:

  1. Function declarations.
  2. Declarations or definitions of types, structures, and classes.
  3. Necessary constant and variable declarations.
  4. Other header files required to use these declarations.

A header file cannot run on its own. After modifying a header file, you need to recompile the .c or .cpp files that include it for verification.

2.1.Pure C++ engineering header files

The swap library in this chapter is implemented by swap.cpp and called by main.cpp, and all source files are compiled according to the C++ code. Therefore, let us first look at the complete writing of an ordinary C++ header file.

It is recommended to use the following header file template for pure C++ projects:

#ifndef __FILE_NAME_H_  // 防止头文件被重复包含,使用时要修改宏名
#define __FILE_NAME_H_

/* 头文件内容开始 */

// 在这里编写必要的预处理指令、类型定义、函数声明和变量声明

/* 头文件内容结束 */

#endif  // __FILE_NAME_H_

When using the template, modify the include guard according to the actual file name. For example:

  • swap.h Can use __SWAP_H_.
  • camera.hpp Can use __CAMERA_HPP_.

In this chapter, the swap.h can be organized as:

#ifndef __SWAP_H_
#define __SWAP_H_

#include <iostream>
using namespace std;

void swap(int a, int b);

#endif  // __SWAP_H_

This is exactly what the most standard form of a C++ header file looks like.

After compilation and execution:

alt text

The #ifndef and #define at the beginning, and the #endif at the end, form a header guard macro. Even if the same header file is included multiple times indirectly, its contents will only participate in compilation once, avoiding issues such as duplicate definitions.

Pure C++ projects also often use the .hpp extension, to emphasize that this is a C++ header file;.h can likewise be used. A project's own header files should generally retain the extension, for example:

#include "swap/swap.h"

Standard library names such as <iostream> and <string> have no extension; this is the C++ naming convention specified by the standard, and it does not mean that header files in a project should also omit the extension.

2.2.Header files for C interface and C/C++ mixed projects

In a project, both .c and .cpp files can exist at the same time. For example, you can have a C++ source filexxx.cppcall functions in a .c file, and you can also have main.ccall functions in a .cpp file.

However, C and C++ compilers do not handle function names in exactly the same way. Whenever a call crosses the boundary between C and C++, a compatible interface header should be provided.

In the header file, extern "C" only takes effect when it is processed by the C++ compiler, and its purpose is to tell the C++ compiler: these functions are handled according to the linking rules of the C language.

For interfaces that need to be used between C and C++, the following header file template is recommended, and the suggested extension is.h:

#ifndef __FILE_NAME_H_  // 防止头文件被重复包含,使用时要修改宏名
#define __FILE_NAME_H_

#ifdef __cplusplus      // 当前文件被 C++ 编译器处理时才会成立
extern "C" {
#endif

/* 头文件内容开始 */

// 在这里编写必要的预处理指令、类型定义、函数声明和变量声明

/* 头文件内容结束 */

#ifdef __cplusplus
}
#endif

#endif  // __FILE_NAME_H_

The following two examples are two independent engineering forms, both modified on the basis of the previously completed swap engineering. You may choose only one for practice; if practicing consecutively, you need to first restore the file name according to the instructions in the second example.

2.2.1.main.cpp Call swap.c

这个例子保留 C++ 编写的 main.cpp,把 swap 函数改为使用 C 语言实现。

First, rename swap.cpp to swap.c:

The main directory structure after the modification is:

src/
├── CMakeLists.txt
├── main.cpp
└── swap/
    ├── CMakeLists.txt
    ├── inc/swap/swap.h
    └── src/swap.c

alt text

因为 src/swap/CMakeLists.txt 已经会同时收集 .c 和 .cpp 文件,所以这里只需重命名文件,再重新 Configure。

Change src/swap/inc/swap/swap.h to a form that can be included by both C and C++:

#ifndef __SWAP_H_
#define __SWAP_H_

#ifdef __cplusplus
extern "C" {
#endif

void swap(int a, int b);

#ifdef __cplusplus
}
#endif

#endif  // __SWAP_H_

注意,这个头文件可能被 C 编译器处理,所以不能再在其中包含 <iostream> 或编写 using namespace std;。

in src/swap/src/swap.c implement in C language swap:

#include "swap/swap.h"
#include <stdio.h>

void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;

    printf("a = %d\n", a);
    printf("b = %d\n", b);
}

src/main.cpp Still C++ source file, and no changes are required:

#include "swap/swap.h"

int main()
{
    int a = 100;
    int b = 200;

    swap(a, b);
    return 0;
}

Running result (compile and link together with the above swap.c):

a = 200
b = 100

alt text

When compiling swap.c, the C compiler does not define __cplusplus, so it skips extern "C". When compiling main.cpp, the C++ compiler sees extern "C", and thus follows the C language linking rules to look for the swap.c in swap function.

2.2.2.main.c Call swap.cpp

Difficulties, commonly used in the STM32 HAL library

This example is the reverse: the entry file uses the C language, while the swap library continues to be implemented using C++.

If you just finished the previous example, first change swap.c back to swap.cpp:

Next, rename the entry file main.cpp to main.c:

alt text

The main directory structure after the modification is:

src/
├── CMakeLists.txt
├── main.c
└── swap/
    ├── CMakeLists.txt
    ├── inc/swap/swap.h
    └── src/swap.cpp

After the file name has changed, you also need to open src/CMakeLists.txt and change:

add_executable(${PROJECT_NAME}
  ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)

Change to:

add_executable(${PROJECT_NAME}
  ${CMAKE_CURRENT_SOURCE_DIR}/main.c
)

src/swap/inc/swap/swap.h Continue using the previous compatible syntax:

#ifndef __SWAP_H_
#define __SWAP_H_

#ifdef __cplusplus
extern "C" {
#endif

void swap(int a, int b);

#ifdef __cplusplus
}
#endif

#endif  // __SWAP_H_

在 src/swap/src/swap.cpp 中使用 C++ 实现 swap:

#include "swap/swap.h"
#include <iostream>

void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;

    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
}

swap.cpp must include swap.h.C++ Only after the compiler reads the header file will it know that this swap function needs to be provided externally according to C language linkage rules.

Finally in src/main.c call it,main.cstill followsmain.cppno modifications are needed:

#include "swap/swap.h"

int main(void)
{
    int a = 100;
    int b = 200;

    swap(a, b);
    return 0;
}

main.c Call only through header files swap,there is no need to understand std::cout such as C++ notation. C++ All code remains in swap.cpp.

Only functions that need to cross the C/C++ boundary require this compatibility syntax; other .cpp files are still called between them in the ordinary C++ manner.

模板的顶层 CMakeLists.txt 已经通过 LANGUAGES C CXX 启用了 C 和 C++。CMake 会用 C 编译器编译 main.c,用 C++ 编译器编译 swap.cpp,并把 swap.cpp 构建成模板中的 swap_src_lib 共享库,最后再将它链接给主程序。

extern "C" 只改变链接规则,并不会把 C++ 代码自动转换成 C。放在其中的接口应使用 C 能理解的类型和写法,不要使用函数重载、类、模板或 C++ 引用等 C++ 专有功能。

2.2.3.main.cCall swap.cpp(Advanced)

Difficulties, commonly used in the STM32 HAL library

The previous example directly exposed the swap function as a C interface. This works, but it also introduces a limitation: swap.h must be understandable by the C compiler, so it cannot contain namespaces, classes, templates, function overloading, C++ references, and other C++-specific constructs.

A more common approach is to add another layer of C interface files.

  1. main.c Only call a function that C can understand, such as cpp_main().
  2. cpp_interface.cpp is responsible for entering the C++ world.
  3. swap.cpp Keep the normal C++ syntax; namespaces can be used.

The main directory structure after the modification is:

src/
├── CMakeLists.txt
├── cpp_interface.cpp
├── cpp_interface.h
├── main.c
└── swap/
    ├── CMakeLists.txt
    ├── inc/swap/swap.h 或者 inc/swap/swap.hpp (纯C++头文件用.h和.hpp都可以)
    └── src/swap.cpp

First create src/ under cpp_interface.h:

#ifndef __CPP_INTERFACE_H_
#define __CPP_INTERFACE_H_

#ifdef __cplusplus
extern "C" {
#endif

int cpp_main(void);

#ifdef __cplusplus
}
#endif

#endif  // __CPP_INTERFACE_H_

This header file is the actual C interface intended for use by main.c. It only declares cpp_main(void) within it, and its parameters and return values are all types that C can understand.

Then modify src/main.c:

#include "cpp_interface.h"

int main(void)
{
    return cpp_main();
}

此时 main.c 不再包含 swap/swap.h,也不再直接调用 swap。它只负责启动 C++ 侧的 cpp_main()。

Next, change src/swap/inc/swap/swap.h or src/swap/inc/swap/swap.hpp (for pure C++ headers, either .h or .hpp is fine) back to a regular C++ header, and add a namespace to swap:

#ifndef __SWAP_H_
#define __SWAP_H_

namespace swap_demo
{
void swap(int a, int b);
}

#endif  // __SWAP_H_

Note that this swap.h or swap.hppis no longer a C interface header file. It contains C++-specific namespace, so main.c can no longer be allowed to directly include it.

Here, there is also no need to add: to swap.h or swap.hpp.

#ifdef __cplusplus
extern "C" {
#endif

The reason is:swap_demo::swap It is originally an ordinary C++ function, and we want it to preserve the namespace notation of C++.extern "C" It should only be placed on the layer of interface that the C code actually needs to call, that is, the preceding cpp_interface.h in the cpp_main(void).

In other words, there are two header files in the advanced version:

Header file Who should include it? Is it necessary to include extern "C"
cpp_interface.h main.c and cpp_interface.cpp Yes, because it is a C/C++ boundary
swap/swap.h swap.cpp and cpp_interface.cpp No, because it is a pure C++ header file

Continue modifying src/swap/src/swap.cpp:

#include "swap/swap.h"
#include <iostream>

namespace swap_demo
{
void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;

    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
}
}

Finally create src/cpp_interface.cpp:

#include "cpp_interface.h"
#include "swap/swap.h"

int cpp_main(void)
{
    int a = 100;
    int b = 200;

    swap_demo::swap(a, b);

    return 0;
}

cpp_interface.cpp is the boundary between C and C++. It contains cpp_interface.h, so cpp_main() is exposed externally according to C language linkage rules; it is itself a .cpp file, so internally it can normally call swap_demo::swap(a, b) this C++ notation.

After adding the file, still open src/CMakeLists.txt, add cpp_interface.cpp to the main program:

add_executable(${PROJECT_NAME}
  ${CMAKE_CURRENT_SOURCE_DIR}/main.c
  ${CMAKE_CURRENT_SOURCE_DIR}/cpp_interface.cpp
)

Here, there is no need to put cpp_interface.h into add_executable(). CMake is mainly responsible for passing source files such as .c and .cpp to the compiler for compilation; header files themselves are not compiled separately into an object file.

cpp_interface.h was found through the #include in the code:

#include "cpp_interface.h"

Since main.c、cpp_interface.cpp and cpp_interface.h are all in src/ the same directory, when double quotes are used for inclusion here, the compiler can find it from the directory where the current source file is located.

If you later move cpp_interface.h to a separate inc/ directory, you will need to use target_include_directories() again to add that directory to the header file search path. In the current example, it is placed in the same directory level, so no additional modification to CMake is required.

src/swap/CMakeLists.txt No modification is needed, because it will already collect src/swap/src/ files in the .cpp directory, and swap.cpp will still be compiled into swap_src_lib.

After compiling and running, the output is still similar to:

a = 200
b = 100

The key point of this advanced approach is:extern "C" place it only on the outermost, simplest interface, that is, cpp_main(void). The actual C++ code is placed after cpp_interface.cpp. swap can use namespaces, and in the future can also continue to use C++ features such as classes, templates, and function overloading, as long as these C++-specific contents are not directly exposed to main.c.

2.3.#include <...> and #include "..."

Both notations are used to include header files, but they are commonly used for different purposes:

#include <iostream>
#include <opencv2/opencv.hpp>

Angle brackets are typically used for standard library or already installed third-party library header files.

#include "swap/swap.h"

Double quotation marks are usually used for header files belonging to the current project. The reason it can be written as "swap/swap.h" here is that CMake has already added src/swap/inc/ to the compiler's header file search path.

无论使用 C 还是 C++,都应为头文件添加保护宏;只有头文件需要作为 C 与 C++ 之间的接口时,才在保护宏内部增加 extern "C" 兼容部分。