English | 简体中文
A tiny boost library in C++11.
coost 是一个兼具性能与易用性的跨平台 C++ 基础库,其目标是打造一把 C++ 开发神器,让 C++ 编程变得简单、轻松、愉快。
coost 简称为 co,曾被称为小型 boost 库,与 boost 相比,coost 小而精美,在 linux 与 mac 上编译出来的静态库仅 1M 左右大小,却包含了不少强大的功能:
|
|
|
coost 的发展离不开大家的帮助,如果您在使用或者喜欢 coost,可以考虑赞助本项目,非常感谢。
co/god.h 提供模板相关的一些功能。模板用到深处有点玄,有些 C++ 程序员称之为面向玄学编程。
#include "co/god.h"
void f() {
god::bless_no_bugs();
god::is_same<T, int, bool>(); // T is int or bool?
}
flag 是一个命令行参数与配置文件解析库,用法与 gflags 类似,但功能更加强大:
- 支持从命令行、配置文件传入参数。
- 支持自动生成配置文件。
- 支持 flag 别名。
- 整数类型的 flag,值可以带单位
k,m,g,t,p
,不分大小写。
// xx.cc
#include "co/flag.h"
#include "co/cout.h"
DEF_bool(x, false, "x");
DEF_bool(y, true, "y");
DEF_bool(debug, false, "dbg", d);
DEF_uint32(u, 0, "xxx");
DEF_string(s, "", "xx");
int main(int argc, char** argv) {
flag::parse(argc, argv);
cout << "x: " << FLG_x << '\n';
cout << "y: " << FLG_y << '\n';
cout << "debug: " << FLG_debug << '\n';
cout << "u: " << FLG_u << '\n';
cout << FLG_s << "|" << FLG_s.size() << '\n';
return 0;
}
上述代码中 DEF_
开头的宏定义了 4 个 flag,每个 flag 对应一个全局变量,变量名是 FLG_
加 flag 名,其中 flag debug
还有一个别名 d
。上述代码编译后,可以按如下方式运行:
./xx # 按默认配置运行
./xx -x -s good # x -> true, s -> "good"
./xx -debug # debug -> true
./xx -xd # x -> true, debug -> true
./xx -u 8k # u -> 8192, 整数可带单位(k,m,g,t,p), 不分大小写
./xx -mkconf # 自动生成配置文件 xx.conf
./xx xx.conf # 从配置文件传入参数
./xx -conf xx.conf # 与上同
log 是一个高性能日志组件,coost 中的一些组件用它打印日志。
log 支持两种类型的日志:一种是 level log,分为 debug, info, warning, error, fatal 5 个级别,打印 fatal 级别的日志会终止程序的运行;另一种是 topic log,日志按 topic 分类,不同 topic 的日志写入不同的文件。
#include "co/log.h"
int main(int argc, char** argv) {
flag::parse(argc, argv);
TLOG("xx") << "s" << 23; // topic log
DLOG << "hello " << 23; // debug
LOG << "hello " << 23; // info
WLOG << "hello " << 23; // warning
ELOG << "hello " << 23; // error
FLOG << "hello " << 23; // fatal
return 0;
}
log 还提供了一系列 CHECK
宏,可以视为加强版的 assert
,它们在 debug 模式下也不会被清除。CHECK 断言失败时,log 会打印函数调用栈信息,然后终止程序的运行。
void* p = malloc(32);
CHECK(p != NULL) << "malloc failed..";
CHECK_NE(p, NULL) << "malloc failed..";
log 速度非常快,下面是一些测试结果:
platform | glog | co/log | speedup |
---|---|---|---|
win2012 HHD | 1.6MB/s | 180MB/s | 112.5 |
win10 SSD | 3.7MB/s | 560MB/s | 151.3 |
mac SSD | 17MB/s | 450MB/s | 26.4 |
linux SSD | 54MB/s | 1023MB/s | 18.9 |
上表是 co/log 与 glog 在单线程连续打印 100 万条日志时测得的写速度对比,可以看到 co/log 比 glog 快了近两个数量级。
threads | linux co/log | linux spdlog | win co/log | win spdlog | speedup |
---|---|---|---|---|---|
1 | 0.087235 | 2.076172 | 0.117704 | 0.461156 | 23.8/3.9 |
2 | 0.183160 | 3.729386 | 0.158122 | 0.511769 | 20.3/3.2 |
4 | 0.206712 | 4.764238 | 0.316607 | 0.743227 | 23.0/2.3 |
8 | 0.302088 | 3.963644 | 0.406025 | 1.417387 | 13.1/3.5 |
上表是分别用 1、2、4、8 个线程打印 100 万条日志的耗时,单位为秒,speedup 是 co/log 在 linux、windows 平台相对于 spdlog 的性能提升倍数。
unitest 是一个简单易用的单元测试框架,coost 中的很多组件用它写单元测试代码,为 coost 的稳定性提供了重要保障。
#include "co/unitest.h"
#include "co/os.h"
DEF_test(os) {
DEF_case(homedir) {
EXPECT_NE(os::homedir(), "");
}
DEF_case(cpunum) {
EXPECT_GT(os::cpunum(), 0);
}
}
int main(int argc, char** argv) {
flag::parse(argc, argv);
unitest::run_tests();
return 0;
}
上面是一个简单的例子,DEF_test
宏定义了一个测试单元,实际上就是一个函数(类中的方法)。DEF_case
宏定义了测试用例,每个测试用例实际上就是一个代码块。
unitest 目录下面是 coost 中的单元测试代码,编译后可执行下述命令运行:
xmake r unitest # 运行所有单元测试用例
xmake r unitest -os # 仅运行 os 单元中的测试用例
coost v3.0 中,Json 采用流畅(fluent)接口设计,用起来更加方便。
// {"a":23,"b":false,"s":"123","v":[1,2,3],"o":{"xx":0}}
co::Json x = {
{ "a", 23 },
{ "b", false },
{ "s", "123" },
{ "v", {1,2,3} },
{ "o", {
{"xx", 0}
}},
};
// equal to x
co::Json y = Json()
.add_member("a", 23)
.add_member("b", false)
.add_member("s", "123")
.add_member("v", Json().push_back(1).push_back(2).push_back(3))
.add_member("o", Json().add_member("xx", 0));
x.get("a").as_int(); // 23
x.get("s").as_string(); // "123"
x.get("s").as_int(); // 123, string -> int
x.get("v", 0).as_int(); // 1
x.get("v", 2).as_int(); // 3
x.get("o", "xx").as_int(); // 0
x["a"] == 23; // true
x["s"] == "123"; // true
x.get("o", "xx") != 0; // false
下面是 co/json 与 rapidjson 的性能对比:
os | co/json stringify | co/json parse | rapidjson stringify | rapidjson parse | speedup |
---|---|---|---|---|---|
win | 569 | 924 | 2089 | 2495 | 3.6/2.7 |
mac | 783 | 1097 | 1289 | 1658 | 1.6/1.5 |
linux | 468 | 764 | 1359 | 1070 | 2.9/1.4 |
上表是将 twitter.json 最小化后测得的 stringify 及 parse 的平均耗时,单位为微秒(us),speedup 是 co/json 在 stringify、parse 方面相对于 rapidjson 的性能提升倍数。
coost 实现了类似 golang 中 goroutine 的协程机制,它有如下特性:
- 支持多线程调度,默认线程数为系统 CPU 核数。
- 共享栈,同一线程中的协程共用若干个栈(大小默认为 1MB),内存占用低。
- 各协程之间为平级关系,可以在任何地方(包括在协程中)创建新的协程。
- 支持协程同步事件、协程锁、channel、waitgroup 等协程同步机制。
#include "co/co.h"
int main(int argc, char** argv) {
flag::parse(argc, argv);
co::wait_group wg;
wg.add(2);
go([wg](){
LOG << "hello world";
wg.done();
});
go([wg](){
LOG << "hello again";
wg.done();
});
wg.wait();
return 0;
}
上面的代码中,go()
创建的协程会分配到不同的调度线程中。用户也可以自行控制协程的调度:
// run f1 and f2 in the same scheduler
auto s = co::next_sched();
s->go(f1);
s->go(f2);
// run f in all schedulers
for (auto& s : co::scheds()) {
s->go(f);
}
coost 提供了一套基于协程的网络编程框架:
- 协程化的 socket API,形式上与系统 socket API 类似,熟悉 socket 编程的用户,可以轻松的用同步的方式写出高性能的网络程序。
- TCP、HTTP、RPC 等高层网络编程组件,兼容 IPv6,同时支持 SSL,用起来比 socket API 更方便。
RPC server
#include "co/co.h"
#include "co/rpc.h"
#include "co/time.h"
int main(int argc, char** argv) {
flag::parse(argc, argv);
rpc::Server()
.add_service(new xx::HelloWorldImpl)
.start("127.0.0.1", 7788, "/xx");
for (;;) sleep::sec(80000);
return 0;
}
rpc::Server 同时支持 HTTP 协议,可以用 HTTP 的 POST 方法调用 RPC 服务:
curl http://127.0.0.1:7788/xx --request POST --data '{"api":"ping"}'
静态 web server
#include "co/flag.h"
#include "co/http.h"
DEF_string(d, ".", "root dir"); // docroot for the web server
int main(int argc, char** argv) {
flag::parse(argc, argv);
so::easy(FLG_d.c_str()); // mum never have to worry again
return 0;
}
HTTP server
void cb(const http::Req& req, http::Res& res) {
if (req.is_method_get()) {
if (req.url() == "/hello") {
res.set_status(200);
res.set_body("hello world");
} else {
res.set_status(404);
}
} else {
res.set_status(405); // method not allowed
}
}
// http
http::Server().on_req(cb).start("0.0.0.0", 80);
// https
http::Server().on_req(cb).start(
"0.0.0.0", 443, "privkey.pem", "certificate.pem"
);
HTTP client
void f() {
http::Client c("https://github.com");
c.get("/");
LOG << "response code: "<< c.status();
LOG << "body size: "<< c.body().size();
LOG << "Content-Length: "<< c.header("Content-Length");
LOG << c.header();
c.post("/hello", "data xxx");
LOG << "response code: "<< c.status();
}
go(f);
-
coost 的头文件。
-
coost 的源代码,编译生成 libco。
-
测试代码,每个
.cc
文件都会编译成一个单独的测试程序。 -
单元测试代码,每个
.cc
文件对应不同的测试单元,所有代码都会编译到单个测试程序中。 -
代码生成工具。
编译 coost 需要编译器支持 C++11:
- Linux: gcc 4.8+
- Mac: clang 3.3+
- Windows: vs2015+
coost 推荐使用 xmake 作为构建工具。
# 所有命令都在 coost 根目录执行,后面不再说明
xmake # 默认构建 libco
xmake -a # 构建所有项目 (libco, gen, test, unitest)
xmake f -k shared
xmake -v
xmake f -p mingw
xmake -v
xmake f --with_libcurl=true --with_openssl=true
xmake -v
xmake install -o pkg # 打包安装到 pkg 目录
xmake i -o pkg # 同上
xmake install -o /usr/local # 安装到 /usr/local 目录
xrepo install -f "openssl=true,libcurl=true" coost
izhengfan 帮忙提供了 cmake 支持,SpaceIm 进一步完善了 cmake 脚本。
mkdir build && cd build
cmake ..
make -j8
mkdir build && cd build
cmake .. -DBUILD_ALL=ON -DCMAKE_INSTALL_PREFIX=/usr/local
make -j8
make install
mkdir build && cd build
cmake .. -DWITH_LIBCURL=ON -DWITH_OPENSSL=ON
make -j8
cmake .. -DBUILD_SHARED_LIBS=ON
make -j8
find_package(coost REQUIRED CONFIG)
target_link_libraries(userTarget coost::co)
vcpkg install coost:x64-windows
# 启用 HTTP & SSL
vcpkg install coost[libcurl,openssl]:x64-windows
conan install coost
The MIT license. coost 包含了一些其他项目的代码,可能使用了不同的 License,详情见 LICENSE.md。