一个零开销、header-only 的 C++23 嵌入式 shell 库。面向裸机目标(Cortex-M 级 MCU):无堆分配、无异常、命令在编译期注册。
跑在 STM32F103C8 真机上的 bareline shell(USART1 @115200):
help/ledon/ledoff/version/stat。固件在example/stm32/。
- 行输入:归一化
\n/\r\n/\r三种行尾(lone-CR 感知,跨行字节正确进入下一行) - 空白分词:零拷贝
string_viewtoken,UTF-8 安全 - 编译期命令注册:
command<Name, Func, Help> - 双分派:fold-expression(小列表)或 constexpr FNV-1a 哈希(大列表)自动选择,二次 name 校验防碰撞
- 内置命令:
help/version/stat;stat关闭时零开销 - 后端抽象(
io::backend::IOAbleconcept)—— 自带read/write;附带zeros_io(回调适配器)与mocked_io(测试用) - 错误上报:
base::expected<T, error::Error>—— shell 自身不打印,由调用者决定
上图:host PC 上的 bareline shell(stdin/stdout,
example/main.cpp):help/echo/add/greet/ledon/version/stat。STM32 真机版见页首。
#include <bareline/bareline.hpp>
int cmd_led(std::span<std::string_view> args) {
// args[0] 是命令名(argc/argv 约定)
return 0;
}
using namespace bareline::cmd;
using MyCmds = command_list<command<"led", cmd_led, "toggle the LED">>;
int main() {
YourBackend backend; // 任何满足 bareline::io::backend::IOAble 的类型
bareline::shell::shell<decltype(backend), MyCmds> shell(backend);
for (;;) shell.run_once();
}命令是编译期的 command<Name, Func, Help> 值,收集进 command_list。Func
需满足 CommandCallable:可按 int(std::span<std::string_view>) 调用并返回
退出码(0 = 成功)。Help 可选 —— 不提供时命令仍可用,help 只打印它的名字。
using namespace bareline::cmd;
int led(std::span<std::string_view> args) { /* ... */ return 0; }
int reboot(std::span<std::string_view>) { /* ... */ return 0; }
using MyCmds = command_list<
command<"led", led, "control the LED">,
command<"reboot", reboot>>; // 无 help 文本shell_config 是模板,每个旋钮都在编译期确定且有默认值:
| 参数 | 默认值 | 含义 |
|---|---|---|
Prompt |
"> " |
提示符字符串(fixed_string NTTP) |
LineBufSize |
128 |
单行输入缓冲容量 |
MaxArgs |
16 |
单行最多解析的 token 数 |
Strategy |
Auto |
分派策略(FoldExpression / ConstexprHash / Auto) |
EnableStat |
true |
是否收集运行时统计(false = 零开销) |
using Cfg = bareline::shell::shell_config<
"bareline> ", 256, 32,
bareline::cmd::DispatchStrategy::ConstexprHash, false>; // 关闭 stat后端是任何满足 io::backend::IOAble 的类型:提供
size_t read(char*, size_t) 与 size_t write(const char*, size_t)。zeros_io
把一对 C 函数指针(加一个不透明 context)适配到该 concept —— 用它包裹你的设备:
std::size_t dev_read(char* buf, std::size_t n, void* ctx) { /* ... */ }
std::size_t dev_write(const char* buf, std::size_t n, void* ctx) { /* ... */ }
bareline::io::backend::zeros_io backend(dev_read, dev_write, &my_device);
bareline::shell::shell<decltype(backend), MyCmds> shell(backend);mocked_io(仅 hosted 测试)喂入预设字节、捕获输出 —— stdin/stdout 变体见
example/main.cpp。
help / version / stat 是保留名,由 shell 自身处理(它们需要 backend 访问,
而普通命令回调拿不到)。不要把它们放进 command_list —— static_assert 会拒绝。
help—— 列出所有命令(名字 + help 文本)version—— 打印库版本(来自 CMake)stat—— 打印运行时统计(仅当EnableStat为 true)
shell 自身不打印。run_once() 返回 base::expected<int, error::Error>:
成功时是命令退出码,失败时是 error::Error(UnknownCommand / BufferOverflow)。
空行不算错误(返回 0)。
auto r = shell.run_once();
if (!r) {
if (r.error() == bareline::error::Error::UnknownCommand) { /* ... */ }
}cmake -B build -G Ninja -DBARELINE_BUILD_TEST=ON -DBARELINE_BUILD_EXAMPLE=ON
cmake --build build
ctest --test-dir build --output-on-failureexample/main.cpp—— stdin/stdout 交互式 shell(hosted)。输入命令回车执行; Ctrl-C 退出(readline 在 EOF 会阻塞 —— 管道喂入会挂起,见io::readline的 @note)。example/size_main.cpp—— ARM flash/RAM 体积探针(freestanding)。example/stm32/—— STM32F103C8(Blue Pill)固件:bareline shell 跑在真机 USART1 上,自包含构建 + OpenOCD 烧录/调试。详见 example/stm32/README.md。
- GCC / Clang(hosted,含 Catch2 测试套件)
arm-none-eabi-g++—— Cortex-M3,freestanding,-fno-exceptions -fno-rtti(见cmake/toolchains/arm-none-eabi.cmake)
Flash(text)目标 < 8 KB;RAM(data + bss)极小。stat_block 为 24 字节,当
shell_config::enable_stat 为 false 时经 [[no_unique_address]] + conditional_t
完全编译消除。Cortex-M3 上完整 shell 管道约 968 字节 text。
v0.1 —— 核心 shell 已完成并通过审查。详见 CHANGELOG.md。
MIT —— 见 LICENSE。

