Skip to content

Repository files navigation

Windows MSYS2 Linux macOS codecov

libsg

A modern C++20 utility library bundling networking, concurrency primitives, buffers, file I/O, compression, and assorted helpers behind a single SG::common CMake target. Tested on Linux, macOS, Windows (MSVC), and MSYS2.

Requirements

  • A C++20 compiler (GCC, Clang, AppleClang, MSVC);
  • CMake 3.15 or newer;
  • Boost (headers);
  • {fmt} (can use system library, or pull own copy);
  • libuv (can use system library, or pull own copy);
  • Optional: zstd for compression support.

Installation

The recommended way to consume libsg is via CPM:

cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
project(MyProject CXX)

file(DOWNLOAD
  https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)
include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)

CPMAddPackage(
  NAME libsg
  GITHUB_REPOSITORY samangh/libsg
  GIT_TAG master
  GIT_SHALLOW
  GIT_SUBMODULES_RECURSE ON
  OPTIONS
    "LIBSG_BUILD_TESTING OFF"
    "LIBSG_IMGUI OFF")

add_executable(main main.cpp)
target_link_libraries(main PRIVATE SG::common)

Build options

Project specific options CMakeLists.txt:

Option Default Description
LIBSG_IMGUI ON Build the optional ImGui helpers (SG::imgui).
LIBSG_IMGUI_DIRECTX ON Enable the DirectX ImGui backend (Windows only).
LIBSG_IMGUI_OPENGL ON Enable the OpenGL ImGui backend (default off on Windows).
LIBSG_ZSTD ON Build the zstd compression helpers.
LIBSG_STACKTRACE OFF Attach stack traces to SG_THROW exceptions.
LIBSG_EXCEPTION_DETAILS OFF Attach function/file/line info to SG_THROW exceptions.
LIBSG_BUILD_TESTING ON Build the Catch2 test suite (off when a libsg is included as a sub-project).
LIBSG_BUILD_DOCS OFF Generate Doxygen docs (only honoured as a sub-project).

Additional options (from cmake/DefaultPreamble.cmake)

Option Default Description
BUILD_SHARED_LIBS ON Build shared libraries (set OFF for static).
USE_STATIC_LIBS !BUILD_SHARED_LIBS Prefer .a over .so/.dylib when locating external libraries.
USE_STATIC_RUNTIME USE_STATIC_LIBS Statically link the C++ runtime (MSVC only).
IPO OFF Enable interprocedural / link-time optimisation if the toolchain supports it.
ARCH_NATIVE OFF Add -march=native (non-MSVC) or the equivalent SSE/AVX flags.
USE_SSE ARCH_NATIVE Enable SSE4.2 / AVX2 (+ CLMUL on x86-64) globally where supported.
USE_LIBC++ OFF Build against libc++ (Clang only).
USE_LINTING OFF Run clang-tidy on every target via CXX_CLANG_TIDY.
SANITIZE OFF Turn on address + UB sanitizers (also exposes SANITIZE_THREAD / SANITIZE_MEMORY).
COVERAGE OFF Add -fprofile-instr-generate -fcoverage-mapping and create a coverage target (Clang only).
OWN_FMT OFF Fetch & build {fmt} via CPM instead of using a system package.
OWN_UV OFF Fetch & build libuv via CPM instead of using a system package.
BUILD_DOCS OFF Generate Doxygen documentation (requires Doxygen).
BUILD_DOCS_SRC OFF Also scan source files when generating docs (requires BUILD_DOCS).

There maybe other CMake options available, consult this file.

What's in the library

All public headers live under sg/. Most types are in the sg:: namespace, with subsystem-specific namespaces (sg::net, sg::compression::zstd, etc.).

Networking (sg::net)

  • tcp_server — accept-loop based server with callback-driven session model
  • tcp_client — asynchronous TCP client sharing a session abstraction
  • tcp_client_sync — blocking TCP
  • asio_io_pool — restartable thread pool driving a single boost::asio::io_context
  • number of utility functions to list all local IP addresses, interfaces and do DNS lookup

Concurrency

  • worker — interval-driven background thread with start/tick/stop callbacks and on-demand notify()
  • state_machine<TState> — generic state machine layered on top of worker
  • jthread.h — wrapper that uses std::jthread when available, polyfills it otherwise.

Buffers and memory

  • IBuffer<T> interface and the unique_buffer / shared_buffer / unique_opaque_buffer / shared_opaque_buffer family, with type-erased deleters (including deleter_free for C-allocated memory).
  • rolling_contiguous_buffer<T> — circular buffer with contiguous storage.
  • enable_lifetime_indicator, pimpl<T> helpers.

Data channels (sg::data)

  • channel, channel_vector, channel_rolling, channel_compressed — named, typed data streams sharing a common IChannelBase interface.

I/O

  • sg::common::file::read / write for whole-file buffer I/O.
  • file_writer — append-only writer with an async queue and dedicated thread.

Compression (sg::compression::zstd)

  • One-shot compress / decompress over raw pointers, contiguous ranges or IBuffer<std::byte>.

Utilities

  • sg::checksum::crc32, crc32c (with hardware fast path), crc16.
  • sg::uuids::uuid — value type around boost::uuids::uuid.
  • sg::version — comparable dotted version.
  • sg::bytes — byteswap, endian helpers.
  • sg::bounds — upper_bound_index, lower_bound_index over raw arrays.
  • sg::format, sg::string, sg::ranges, sg::math, sg::map.
  • sg::process — process / thread enumeration.
  • sg::cpu — vendor, model, parallelism estimate.
  • sg::locale — UTF-8 ctype helpers.
  • sg::random::generate<T>(count) — quick shuffled-iota vectors.

Core concepts

Tagged callbacks

Callbacks are declared with the CREATE_CALLBACK macro. Each call generates a phantom tag type so that two callbacks with the same signature but different meanings cannot be mistakenly swapped:

#include <sg/callback.h>

CREATE_CALLBACK(on_connected_cb_t,  void(int session_id))
CREATE_CALLBACK(on_disconnect_cb_t, void(int session_id))

on_connected_cb_t cb = [](int id) { /* ... */ };
cb.invoke(42);   // use .invoke(), not operator()

Buffers

unique_buffer and shared_buffer own a contiguous run of T with a configurable deleter. Use the _c_ variants when the memory was allocated with malloc (or any function compatible with free):

#include <sg/buffer.h>

auto buf = sg::make_shared_c_buffer<std::byte>(1024); // free()-backed
std::memcpy(buf.get(), source, 1024);

Use the *_opaque_buffer variants when you need to hand a buffer across an API boundary without exposing the deleter type.

Examples

TCP echo server

#include <sg/tcp_server.h>

using namespace sg::net;

int main() {
    tcp_server::CallBacks cb;
    cb.OnSessionDataAvailable =
        [](tcp_server& s, auto id, const std::byte* data, size_t size) {
            s.write(id, data, size);
        };

    tcp_server server;
    server.start({{"127.0.0.1", 55555}}, cb);
    server.future_get_once(); // wait until stopped
}

Add connection lifecycle callbacks the same way:

cb.OnSessionCreated = [](tcp_server&, auto id) {
    std::cout << "client " << id << " connected\n";
};
cb.OnDisconnected = [](tcp_server&, auto id, std::exception_ptr ex) {
    std::cout << "client " << id << " disconnected\n";
};

Synchronous TCP client

#include <sg/tcp_client_sync.h>
using namespace sg::net;

tcp_client_sync client;
client.connect({"127.0.0.1", 55555});
client.write("hello\n");
auto reply = client.read_until("\n");

Periodic background work

#include <sg/worker.h>
using namespace std::chrono_literals;

sg::worker w(100ms,
    sg::worker::callbacks_t{
        .on_start_callback = [](sg::worker*) { /* setup */ },
        .on_tick_callback  = [](sg::worker*) { /* every 100ms */ },
        .on_stop_callback  = [](sg::worker*) { /* teardown */ },
    });
w.start();
w.notify();          // run the tick immediately
w.request_stop();
w.wait_for_stop();

File writer

#include <sg/file_writer.h>

sg::file_writer writer;
writer.start("log.bin",
             /*on_error*/  nullptr,
             /*on_start*/  nullptr,
             /*on_stop*/   nullptr);
writer.write_async("hello\n");
writer.stop();

zstd compression

#include <sg/compression_zstd.h>

std::vector<std::byte> input = /* ... */;
auto compressed = sg::compression::zstd::compress(
    input, sg::compression::zstd::default_compresssion_level(), /*threads*/ 1);
auto roundtrip  = sg::compression::zstd::decompress<std::byte>(
    compressed.get(), compressed.size());

Custom exceptions

#include <sg/exceptions.h>

namespace myapp::errors {
    SG_CREATE_EXCEPTION_ANY();                          // myapp::errors::any
    SG_CREATE_EXCEPTION(bad_config, "bad config");      // myapp::errors::bad_config
}

try {
    SG_THROW(myapp::errors::bad_config, "missing key 'port'");
} catch (const myapp::errors::any& e) { /* ... */ }

Building from source

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure

To produce a coverage report (GCC), add -DCOVERAGE=ON and build the coverage target.

Testing

Tests live in test/ and use Catch2. They are built whenever libsg is the top-level project and discovered via catch_discover_tests, so any standard CTest invocation will run them.

License

See LICENSE and the LICENCES directory for third-party notices.

About

Library containing some commonly used C/C++ functions

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages