Skip to content

Repository files navigation

coost

简体中文 | English
Linux Mac Windows Windows-arm64 FreeBSD Release License: MIT

A tiny, minimalist Swiss Army knife for C++.

Introduction

coost is a lightweight, high-performance, easy-to-use C++ base library. It supports Linux, macOS, Windows, FreeBSD, and CPU architectures such as x86, x64, ARM, and ARM64.

coost, abbreviated as co, includes go-style coroutines, networking, logging, config parsing, unit testing, benchmarking, memory allocator, and other components. Its goal is to make C++ programming simple and enjoyable.

Commercial Support

coost remains open source. If you use coost in production and need in-depth support such as custom development, architecture adaptation (RISC-V / MIPS), coroutine hooks, performance optimization, or team training, the author offers paid commercial support to reduce risk and save time.

For service packages, pricing, and process, see:

👉 coost Commercial Support

Contact the author via GitHub Issues or email idealvin@qq.com. A free initial diagnosis is available to assess scope and feasibility.

Documentation

The documentation currently lags behind the latest version of coost. Please refer to the latest code and the include/co headers for the most accurate information.

Core Components

flag

flag is a command-line argument and config file parsing library. It supports flag aliases, automatic config file generation, and more.

#include "co/flag.h"
#include "co/print.h"

DEF_bool(x, false, "Comment here");
DEF_int32(n, 0, "Comment here");
DEF_string(s, "hello world", "Comment here");

int main(int argc, char** argv) {
    flag::parse(argc, argv);
    co::println("x: ", FLG_x);
    co::println("n: ", FLG_n);
    co::println("s: ", FLG_s);
    return 0;
}

Coroutines

coost implements a coroutine mechanism similar to goroutine in Golang. It has the following features:

  • Multi-thread scheduling. By default, the number of scheduling threads is the number of CPU cores.
  • Shared stacks. Coroutines in the same thread share several stacks (default size 1MB), resulting in low memory usage.
  • Supports coroutine synchronization events, coroutine locks, waitgroup.
#include "co/co.h"
#include "co/flag.h"
#include "co/print.h"

int main(int argc, char** argv) {
    flag::parse(argc, argv);

    co::wait_group wg(2);

    go([wg](){
        co::println("hello world");
        wg.done();
    });

    go([wg](){
        co::println("hello again");
        wg.done();
    });

    wg.wait();
    return 0;
}

Networking

coost provides a coroutine-based network programming framework:

  • Socket API. It is similar in form to the system socket API. Users familiar with socket programming can easily write high-performance network programs in a synchronous style.
  • TCP. Simple encapsulation of TCP server and client, compatible with IPv6.
  • RPC. A simple RPC framework using JSON for serialization.

Logging

log is a high-performance logging component. It supports printing stack traces when the program crashes.

#include "co/log.h"

int main(int argc, char** argv) {
    flag::parse(argc, argv);
    log::info("hello ", 23);   // info
    log::warn("hello ", 23);   // warning
    log::error("hello ", 23);  // error
    log::check(1+1==2, "xx");  // runtime assertion; prints stack trace and exits on failure
    return 0;
}

log is very fast. Here are some test results:

platform glog co/log speedup
win2012 HDD 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

The table above shows the write speed comparison between co/log and glog when continuously printing 1 million logs in a single thread. co/log is nearly two orders of magnitude faster than 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

The table above shows the time consumed to print 1 million logs with 1, 2, 4, and 8 threads respectively, in seconds. speedup is the performance improvement factor of co/log over spdlog on Linux and Windows.

Unit Testing

unitest is a simple and easy-to-use unit testing framework. Many components in coost use it to write unit test code, providing important assurance for coost's stability.

#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);
    co::run_unitests();
    return 0;
}

The above is a simple example. The DEF_test macro defines a test unit, which is actually a function. The DEF_case macro defines a test case, and each test case is actually a code block.

The coost/unitest directory contains the unit test code in coost. Run the following commands to build and run:

xmake b unitest
xmake r unitest      # run all unit test cases
xmake r unitest -os  # run only test cases in the os unit; os is the unit test name

Benchmark

benchmark is a simple and easy-to-use performance benchmark framework.

#include "co/benchmark.h"
#include "co/atomic.h"

BM_group(atomic) {
    int i = 0;

    BM_add(atomic_inc) {
        co::atomic_inc(&i);
    }
    BM_use(i);

    BM_add(atomic_dec) {
        co::atomic_dec(&i);
    }
    BM_use(i);
}

The coost/benchmark directory contains some performance test code. Run the following commands to build and run:

xmake b benchmark
xmake r benchmark        # run all benchmark code by default
xmake r benchmark -mem   # run only benchmark code in BM_group(mem)

JSON

JSON adopts a fluent interface design, making it easier to use.

// {"a":23,"b":false,"s":"123","v":[1,2,3],"o":{"xx":0}}
json::any x = {
    { "a", 23 },
    { "b", false },
    { "s", "123" },
    { "v", {1,2,3} },
    { "o", {
        {"xx", 0}
    }},
};

// equal to x
json::any 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

Below is the performance comparison between co/json and 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

The table above shows the average time for stringify and parse measured after minifying twitter.json, in microseconds (us). speedup is the performance improvement factor of co/json over rapidjson in stringify and parse.

Code Structure

  • include

    Header files of coost.

  • src

    Source code of coost, which compiles into libco.

  • benchmark

    Performance benchmark code. Each .cc file corresponds to a different test unit, and all code is compiled into a single test program.

  • test

    Test code. Each .cc file is compiled into a separate test program.

  • unitest

    Unit test code. Each .cc file corresponds to a different test unit, and all code is compiled into a single test program.

  • gen

    Code generation tools.

Build

Compiler Requirements

The latest version of coost requires a compiler that supports C++17:

Build with xmake

coost recommends xmake as the build tool.

Quick Start

# All commands are executed in the coost root directory, and this will not be repeated below
xmake       # build libco by default
xmake -a    # build all projects (libco, benchmark, gen, test, unitest)

Enable backtrace

On Linux and macOS, printing stack traces on crashes requires libbacktrace. Newer versions of gcc on Linux already have a built-in backtrace library; on macOS it usually needs to be installed manually.

xmake f --with_backtrace=true
xmake b stack   # test/stack.cc
xmake r stack   # run stack test program

Install libco

xmake install -o pkg          # package and install to the pkg directory
xmake i -o pkg                # same as above
xmake install -o /usr/local   # install to /usr/local

Build with cmake

Build libco

mkdir cmakebuild && cd cmakebuild
cmake ..
make -j8

Build all projects

mkdir cmakebuild && cd cmakebuild
cmake .. -DBUILD_ALL=ON -DCMAKE_INSTALL_PREFIX=/usr/local
make -j8
cd bin
./unitest  # run unit test program

Enable backtrace

mkdir cmakebuild && cd cmakebuild
cmake .. -DWITH_BACKTRACE=ON
make -j8

License

The MIT license. coost includes code from some other projects, which may use different licenses. See LICENSE.md for details.

Special Thanks

  • The related code of context is taken from ruki's tbox. ruki also helped improve the xmake build script. Special thanks!
  • izhengfan provided the cmake build script. Special thanks!
  • SpaceIm improved the cmake build script and provided find_package support. Special thanks!
  • Leedehai and daidai21 helped translate coost's Chinese reference documentation into English in the early days. Special thanks!

About

A minimalist Swiss Army knife for C++.

Topics

Resources

Stars

4.2k stars

Watchers

135 watching

Forks

Releases

Packages

Used by

Contributors

Languages