
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(...)针对none、int、float进行函数重载演示 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.includeFEATURES_REQUIRED显式声明 C 能力RIOT 采用“特性features声明”机制。示例通过两行FEATURES_REQUIRED明确要求目标平台必须具备的能力cpp基础 C 支持C 编译器与运行时libstdcpplibstdc 支持示例注释专门指出这是#include cstdio等 C 标准库头文件所必需的。若目标板卡不支持这些特性构建会在早期阶段报错而不是等到链接时才失败这保证了混合语言工程的可移植性。CXXEXFLAGSC 文件的专属编译选项这是 README 中唯一单列的 Makefile 选项原文描述为CXXEXFLAGS : users extra flags used to build c files should be defined here (e.g -stdgnu11).即用户针对 C 源文件的额外编译标志必须定义在CXXEXFLAGS中。例如需要指定 C 标准版本时可以这样写CXXEXFLAGS -stdgnu17从源码结构看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 CC 与 C 混合编程最大的技术障碍是名称修饰name manglingC 编译器会对函数符号进行重载相关的名字改编而 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.cC 编译器和main.cppC 编译器包含在 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 functionint 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.141592f的f后缀必不可少——若写成字面量3.141592其类型为double而类中只重载了float版本编译将失败。这恰好演示了 C 重载决议的精确匹配规则。命名空间与 std::vector示例展示了嵌入式环境下使用 C 标准库容器的可行性using namespace std; // 引入命名空间之后可省略 std:: 前缀 vectorint 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::vectorint可直接写作vectorint。随后用迭代器遍历并打印容器内容for (vectorint::iterator it vInts.begin(); it ! vInts.end(); it) { printf(%d , *(it)); }这里有两个值得注意的 RIOT 相关细节printf的%格式串使用了 RIOT 提供的 PRI 宏PRIkernel_pid、PRIuSIZE用于保证打印kernel_pid_t、size_t类型时在不同架构如 native 的 64 位与 32 位 MCU上格式正确本工程能使用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, youre in thread #1 ******** Well test C class and methods here! - Test overloading functions - Instantiating Object [constructor called] Im starting! Hello! Hello![int: 42] Hello![float: 3.141593] ... ******** Hello, youre in thread #2 ******** Well test some C functions here! - hello function - Hello world in a C function - day_of_week function - day 13, month 6, year 2014 is Friday2014-06-13确实是星期五可据此验证day_of_week的 C 算法正确性。程序结束后native下按Ctrl-C可观察到析构函数输出Destroying Object [destructor called]。换用真实板卡编译时例如make BOARDnrf52840dkRIOT 会先检查该板卡的 features 是否满足cpp与libstdcpp要求。对于 Flash/RAM 较小的板卡请参考 Makefile.ci 中列出的BOARD_INSUFFICIENT_MEMORY清单如nucleo-l011k4、samd10-xmini、stm32f0discovery等这些板卡因内存不足以运行本示例而不参与 CI 构建。总结与扩展通过riot_and_cpp这一官方示例可以得出 RIOT 中 C/C 混合编程的四条核心经验特性声明先行在 Makefile 中用FEATURES_REQUIRED cpp libstdcpp显式声明对 C 工具链与标准库的依赖C 头文件一律包extern C凡会被 C 源文件包含的 C 头文件都用#ifdef __cplusplus extern C { ... }保护消除名称修饰问题C 专属标志走CXXEXFLAGS如-stdgnu11等只影响.cpp编译单元的标志统一放入该变量C 与 RIOT 内核 API 可以无缝协作thread_create、printf的 PRI 宏等 RIOT 设施均可直接在 C 代码中使用无需额外适配层。该工程位于 RIOT 仓库 examples/lang_support/official/riot_and_cpp/ 目录同目录下还有rust-hello-world、rust-async、rust-gcoap等其它语言支持示例可对照学习 RIOT 对多语言应用开发的整体支持策略。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考