news 2026/9/19 20:03:46

RIOT 中 C++ 与 C 混合编程实战指南:以 riot_and_cpp 示例为例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RIOT 中 C++ 与 C 混合编程实战指南:以 riot_and_cpp 示例为例

RIOT 中 C++ 与 C 混合编程实战指南:以 riot_and_cpp 示例为例

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

本篇技术指南围绕 RIOT 官方语言支持示例中的 riot_and_cpp 展开,系统讲解如何在以 C 为核心的 RIOT 操作系统上编写同时包含 C 与 C++ 源文件的应用。你将掌握:通过CXXEXFLAGS为 C++ 文件注入编译选项、用FEATURES_REQUIRED开启 C++ 运行时支持、用extern "C"打通 C/C++ 符号链接(name mangling)、在嵌入式多线程环境下使用 C++ 类、函数重载、命名空间与std::vector容器等完整实战方案,并可直接在native模拟目标上编译运行验证。

示例工程概览

riot_and_cpp是 RIOT 官方提供的“C++ 与 C 混合编程”最小演示工程,位于仓库 examples/lang_support/official/riot_and_cpp/ 目录,其 README 开宗明义地指出:

This project demonstrates how user can use both C++ and C in their application with RIOT.

整个工程由 6 个文件构成,结构非常清晰:

examples/lang_support/official/riot_and_cpp/ ├── Makefile # 构建配置:APPLICATION、BOARD、RIOTBASE、FEATURES_REQUIRED、CXXEXFLAGS ├── Makefile.ci # CI 内存不足板卡清单 ├── README.md # 使用说明 ├── c_functions.c # 纯 C 函数实现(hello、day_of_week) ├── c_functions.h # C 函数声明,含 extern "C" 保护 ├── cpp_class.cpp # C++ 类实现(构造/析构、say_hello 重载、私有 greet) ├── cpp_class.hpp # C++ 类声明(public/private 成员划分) └── main.cpp # C++ 主程序:创建线程、实例化类、使用 std::vector

它演示了 RIOT 应用中 C 与 C++ 共存的全部关键点,源码头注释(main.cpp)将其概括为:

  • 混合编译 C 与 C++ 源文件,验证名称修饰(name mangling)处理;
  • 引入命名空间,避免每次调用都写std::前缀;
  • 使用 private/public 成员函数(如cpp_obj.greet()无法从main.cpp访问);
  • cpp_obj.say_hello(...)针对noneintfloat进行函数重载;
  • 演示 C++ 模板容器std::vector
  • 使用迭代器访问容器元素。

Makefile 配置要点

Makefile 是本示例的核心配置,其中与 C++ 直接相关的部分如下:

APPLICATION = riot_and_cpp # If no BOARD is found in the environment, use this default: BOARD ?= native # This has to be the absolute path to the RIOT base directory: RIOTBASE ?= $(CURDIR)/../../../.. # Features required FEATURES_REQUIRED += cpp # basic C++ support FEATURES_REQUIRED += libstdcpp # libstdc++ support (for #include <cstdio>) # If you want to add some extra flags when compile c++ files, add these flags # to CXXEXFLAGS variable CXXEXFLAGS += include $(RIOTBASE)/Makefile.include

FEATURES_REQUIRED:显式声明 C++ 能力

RIOT 采用“特性(features)声明”机制。示例通过两行FEATURES_REQUIRED明确要求目标平台必须具备的能力:

  • cpp:基础 C++ 支持(C++ 编译器与运行时);
  • libstdcpp:libstdc++ 支持,示例注释专门指出这是#include <cstdio>等 C++ 标准库头文件所必需的。

若目标板卡不支持这些特性,构建会在早期阶段报错,而不是等到链接时才失败,这保证了混合语言工程的可移植性。

CXXEXFLAGS:C++ 文件的专属编译选项

这是 README 中唯一单列的 Makefile 选项,原文描述为:

CXXEXFLAGS : user's extra flags used to build c++ files should be defined here (e.g -std=gnu++11).

即:用户针对 C++ 源文件的额外编译标志必须定义在CXXEXFLAGS。例如需要指定 C++ 标准版本时,可以这样写:

CXXEXFLAGS += -std=gnu++17

从源码结构看,RIOT 的构建系统在编译.cpp/.cc文件时使用独立的 C++ 编译规则,CXXEXFLAGS正是该规则下专用于 C++ 文件的用户扩展标志位——它与 C 文件的CFLAGS/EXFLAGS通道相互独立,避免 C++ 专属标志(如-std-fno-exceptions等)污染 C 代码的编译单元。

其他通用配置

  • BOARD ?= native:默认使用native(宿主机模拟)目标,无需任何硬件即可编译运行;
  • RIOTBASE ?= $(CURDIR)/../../../..:指向 RIOT 仓库根目录,此相对路径正是从examples/lang_support/official/riot_and_cpp/向上回溯 4 级得到;
  • DEVELHELP ?= 1:启用 RIOT 的开发期安全检查代码,生产环境可注释关闭;
  • QUIET ?= 1:默认隐藏完整编译命令,置 0 可查看编译器的具体调用行。

C 与 C++ 互操作的核心:extern "C"

C 与 C++ 混合编程最大的技术障碍是名称修饰(name mangling):C++ 编译器会对函数符号进行重载相关的名字改编,而 C 编译器不做。若 C++ 代码直接#includeC 头文件并调用其中函数,链接阶段会因符号不匹配而失败。

c_functions.h给出了标准解法——用__cplusplus宏加extern "C"包裹全部 C 函数声明:

#include <stdio.h> /* * all headers defining c functions must be marked as extern "C" when included * into c++ sources */ #ifdef __cplusplus extern "C" { #endif int hello(void); int day_of_week(int day, int month, int year); #ifdef __cplusplus } #endif

要点说明:

  • 该头文件同时被c_functions.c(C 编译器)和main.cpp(C++ 编译器)包含;
  • 在 C++ 编译单元中,__cplusplus宏已定义,声明被extern "C"包裹,从而禁止 C++ 编译器对这些符号做名称修饰,保证与c_functions.c中编译出的 C 符号一致;
  • 在纯 C 编译单元中__cplusplus未定义,extern "C"不生效,代码等价于普通 C 头文件。

这是所有“C++ 调用 C 函数”场景的通用模式,也是本示例验证 name mangling 处理的关键。

C 侧的实现

c_functions.c提供了两个纯 C 函数:

  • int hello(void):打印"Hello world in a C function"
  • int day_of_week(int day, int month, int year):以 2000-01-01(星期六,first_day = 6)为基准计算任意日期是星期几,返回 0 表示 Sunday、6 表示 Saturday,非法日期或year < 2000返回-1

day_of_week内部使用宏IS_LEAP_YEAR(year)判断闰年,并用取模 7 的方式累计天数差,是一个不依赖任何 RIOT 模块的纯算法函数,非常适合作为 C 侧代码的代表。

C++ 侧:类、重载、命名空间与容器

类的声明与私有成员

cpp_class.hpp定义了一个带 public/private 划分的类:

#include <cstdio> class cpp_class { public: cpp_class(); // 构造函数 ~cpp_class(); // 析构函数 void say_hello(void); void say_hello(int n); // 重载:int 参数 void say_hello(float f);// 重载:float 参数 private: void greet(void); // 私有成员,外部不可访问 };

对应实现 cpp_class.cpp 中:

  • 构造函数调用printf打印"Instantiating Object [constructor called]"并调用私有方法greet()
  • 析构函数打印"Destroying Object [destructor called]""Im shutting down!"
  • say_hello三个重载版本分别输出Hello!Hello![int: %d]Hello![float: %f]

函数重载的实际效果

在 main.cpp 中:

cpp_class cpp_obj; printf("\n-= Test overloading functions =-\n"); cpp_obj.say_hello(); cpp_obj.say_hello(42); cpp_obj.say_hello(3.141592f);

编译器根据实参类型(无参 /int/float)自动选择对应重载版本。注意3.141592ff后缀必不可少——若写成字面量3.141592,其类型为double,而类中只重载了float版本,编译将失败。这恰好演示了 C++ 重载决议的精确匹配规则。

命名空间与 std::vector

示例展示了嵌入式环境下使用 C++ 标准库容器的可行性:

using namespace std; // 引入命名空间,之后可省略 std:: 前缀 vector<int> vInts; vInts.push_back(1); vInts.push_back(3); vInts.push_back(2); printf("The vector vInts has been filled with %" PRIuSIZE " numbers.\n", vInts.size());

using namespace std;之后std::vector<int>可直接写作vector<int>。随后用迭代器遍历并打印容器内容:

for (vector<int>::iterator it = vInts.begin(); it != vInts.end(); ++it) { printf("%d ", *(it)); }

这里有两个值得注意的 RIOT 相关细节:

  1. printf%格式串使用了 RIOT 提供的 PRI 宏(PRIkernel_pidPRIuSIZE),用于保证打印kernel_pid_tsize_t类型时在不同架构(如 native 的 64 位与 32 位 MCU)上格式正确;
  2. 本工程能使用std::vector,正是因为 Makefile 中声明了FEATURES_REQUIRED += libstdcpp,使 C++ 标准模板库可用。

在 C++ 程序中创建 RIOT 线程

混合编程不仅要打通语言边界,还要让 C++ 代码与 RIOT 内核 API 协作。main.cpp 展示了标准的线程创建流程:

char threadA_stack [THREAD_STACKSIZE_MAIN]; void *threadA_func(void *arg); thread_create(threadA_stack, sizeof(threadA_stack), 0, THREAD_CREATE_WOUT_YIELD, threadA_func, NULL, "thread A");
  • 栈空间使用 RIOT 提供的THREAD_STACKSIZE_MAIN宏(thread.h 中定义的标准线程栈大小),由用户态静态数组提供;
  • THREAD_CREATE_WOUT_YIELD标志表示创建后不立即让出 CPU;
  • 线程函数threadA_func以 C 函数形式实现,内部调用 C 侧函数hello()day_of_week(...)
  • 主线程通过thread_getpid()打印自己的kernel_pid

运行时两个线程并行执行:线程 A 演示“C++ 代码调用 C 函数”,主线程演示“C++ 类与 STL 容器”,完整覆盖了单进程中 C/C++/RIOT 三者的协作场景。

编译、运行与验证

在示例目录下执行(需要本机已安装 RIOT 工具链,native目标依赖宿主机 gcc/g++ 与pkg-config等):

cd examples/lang_support/official/riot_and_cpp make ./bin/native/riot_and_cpp.elf

预期输出包含以下关键片段:

************ RIOT and C++ demo program *********** ******** Hello, you're in thread #1 ******** We'll test C++ class and methods here! -= Test overloading functions =- Instantiating Object [constructor called] Im starting! Hello! Hello![int: 42] Hello![float: 3.141593] ... ******** Hello, you're in thread #2 ******** We'll test some C functions here! -= hello function =- Hello world in a C function -= day_of_week function =- day 13, month 6, year 2014 is Friday

2014-06-13确实是星期五,可据此验证day_of_week的 C 算法正确性。程序结束后(native下按Ctrl-C)可观察到析构函数输出Destroying Object [destructor called]

换用真实板卡编译时,例如:

make BOARD=nrf52840dk

RIOT 会先检查该板卡的 features 是否满足cpplibstdcpp要求。对于 Flash/RAM 较小的板卡,请参考 Makefile.ci 中列出的BOARD_INSUFFICIENT_MEMORY清单(如nucleo-l011k4samd10-xministm32f0discovery等),这些板卡因内存不足以运行本示例而不参与 CI 构建。

总结与扩展

通过riot_and_cpp这一官方示例可以得出 RIOT 中 C/C++ 混合编程的四条核心经验:

  1. 特性声明先行:在 Makefile 中用FEATURES_REQUIRED += cpp libstdcpp显式声明对 C++ 工具链与标准库的依赖;
  2. C 头文件一律包extern "C":凡会被 C++ 源文件包含的 C 头文件,都用#ifdef __cplusplus extern "C" { ... }保护,消除名称修饰问题;
  3. C++ 专属标志走CXXEXFLAGS:如-std=gnu++11等只影响.cpp编译单元的标志,统一放入该变量;
  4. C++ 与 RIOT 内核 API 可以无缝协作thread_createprintf的 PRI 宏等 RIOT 设施均可直接在 C++ 代码中使用,无需额外适配层。

该工程位于 RIOT 仓库 examples/lang_support/official/riot_and_cpp/ 目录,同目录下还有rust-hello-worldrust-asyncrust-gcoap等其它语言支持示例,可对照学习 RIOT 对多语言应用开发的整体支持策略。

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 20:01:46

FRAGSTATS景观格局指数速查:从面积、边缘到分维数

简介&#xff1a;这是一份聚焦景观格局指数体系的PDF参考文档&#xff0c;适合景观生态学、地理信息科学、城乡规划等领域的研究者、研究生及从业人员使用。文档以英文缩写—英文全称—指标名称—应用尺度—单位为线索&#xff0c;系统整理面积指标&#xff08;AREA、CA、PLAND…

作者头像 李华
网站建设 2026/9/19 19:58:11

Java重构会展服务平台:状态机、并发控制与数据库优化实践

简介&#xff1a;基于Java的会展服务平台设计与实现文档&#xff0c;内容覆盖平台需求、设计目标、设计方案及实现技术&#xff0c;面向Java Web方向的高校毕业生或开发者。文档从会展服务管理实际场景出发&#xff0c;围绕管理员与用户两类角色展开&#xff0c;管理员侧涉及个…

作者头像 李华
网站建设 2026/9/19 19:56:44

BMAD Deep Recon 研究生命周期:Refresh 与 Deepen 机制实战指南

BMAD Deep Recon 研究生命周期&#xff1a;Refresh 与 Deepen 机制实战指南 【免费下载链接】BMAD-METHOD Breakthrough Method for Agile Ai Driven Development 项目地址: https://gitcode.com/gh_mirrors/bm/BMAD-METHOD 导读 本指南围绕 BMAD-METHOD 项目中 bmad-d…

作者头像 李华
网站建设 2026/9/19 19:56:03

自考AI工具对比:千笔与灵感的场景化应用

1. 项目背景与需求解析作为一名长期关注教育科技领域的从业者&#xff0c;我注意到近年来自考学习群体对AI辅助工具的需求呈现爆发式增长。特别是在论文写作、作业辅导等场景中&#xff0c;AI生成内容&#xff08;AIGC&#xff09;工具的使用率持续攀升。但市面上的通用型AI工具…

作者头像 李华