ERBSLAND CORE

A dependency-free C++20 foundation for secure, portable applications

Language C++
License Apache 2.0

Erbsland Core is a cross-platform C++20 foundation library for applications that need dependable building blocks without a large external framework.

It provides one consistent API across Linux, macOS, and Windows and has no required library dependency beyond the C++ standard library. Its modules cover everything from Unicode text and filesystem access to application lifecycle management, event-driven networking, and cryptography.

The project is currently in alpha in transition to beta. It is ready for experiments and new applications that can track API changes.

Straightforward CMake Integration

set(CMAKE_UNITY_BUILD ON)
add_subdirectory(erbsland)
add_executable(example src/main.cpp)
erbsland_core_setup_application(TARGET example)

The recommended setup adds Core as a pinned Git submodule below an erbsland directory. Linking the erbsland::core target supplies its public headers, C++20 requirement, and selected public build definitions to the consuming target.

Read the usage guide for all details.

A Broad, Coherent Foundation

Erbsland Core collects the facilities that portable applications repeatedly need behind a consistent set of conventions:

  • Unicode-aware strings, formatting, encodings, and text processing for UTF-8, UTF-16, and UTF-32.
  • A safe regular-expression engine, byte and text streams, buffers, filesystem paths, and file operations.
  • Date and time types, system-independent time-zone calculations, safe numeric utilities, and random-number generators.
  • Command-line parsing, formatted help, application lifecycle management, logging system, resources, terminal output, errors, and diagnostics.
  • Event loops, schedulers, timers, event threads, TCP and UDP, host-name resolution, TLS 1.3, and HTTP clients and servers.
  • The Erbsland Configuration Language with validation rules, plus an extensive cryptography layer.

These areas are designed to work together, reducing the adapters and platform-specific branches that otherwise accumulate around an application.

Build a Unicode-Safe Search Tool

The getting-started tutorial builds elgrep, a practical recursive search tool. Along the way it introduces the application framework, command-line options, portable paths, decoded text streams, regular expressions, and safe terminal output.

Portable Without a Required Third-Party Stack

Core targets Linux, macOS, and Windows while requiring no third-party library at runtime or build time beyond the C++ standard library.

That independence also extends to several areas that are commonly delegated to platform services or large external libraries:

  • Generated Unicode data is part of Core itself. It provides Unicode categories, case folding, and all four standard normalization forms—NFC, NFD, NFKC, and NFKD—for UTF-8, UTF-16, and UTF-32, without requiring ICU or a platform Unicode library.
  • The date and time API uses bundled IANA time-zone data for conversions. The operating system is consulted to identify the configured local zone, but the conversion rules do not depend on the platform’s time-zone database. This helps the same input produce consistent results across supported systems.
  • The TLS 1.3 client and server layers, including their cryptographic building blocks, are implemented within Core rather than delegated to OpenSSL or a platform cryptography library.

This is intended to keep deployment predictable and behavior consistent. For these facilities, Core provides a common source of truth across all supported platforms. When an issue is found, it can be corrected in Core instead of worked around separately on each platform.

Less Boilerplate Around Applications

The application framework coordinates executable startup, metadata, command-line options, help and version output, terminal access, error reporting, and exit codes.

Application parts provide a structured way to assemble larger programs, while the resource system compiles required files directly into the executable. Together, these facilities let an application focus on its own workflow without recreating the surrounding infrastructure for every project.

Erbsland Core allows writing script like applications like the following:

auto main(const int argc, char *argv[]) -> int {
    auto app = el::Application{argc, argv};
    app.setInitializeFn([&app]() -> void {
        app.info().setApplicationName("Deployment Label"_el);
        app.info().setApplicationVersion(el::Version{1, 0, 0});
        app.options()
            ->addOption({"-e"_el, "--environment"_el, "environment"_el})
            .setType(el::OptionType::Text)
            .setDefaultValue("staging"_el)
            .setHelpDescription("Environment written into the deployment label."_el);
    });
    app.setMainFn([&app]() -> el::ExitCode {
        el::io::printLine("deployment environment: "_el, app.optionValues()->getText("environment"_el));
        return el::ExitCode::success();
    });
    return app.run();
}

… procedural applications …

class ReportApplication final : public el::Application {
public:
    using Application::Application;

protected: // implement Application
    void initialize() override {
        info().setApplicationName("Deployment Report"_el);
        info().setApplicationVersion(el::Version{1, 0, 0});
    }
    void registerCommandLineOptions(const el::OptionsPtr &options) override {
        options->addOption("environment"_el)
            .setRequired()
            .setHelpDescription("Environment summarized by the report."_el);
        options->addOption({"-d"_el, "--dry-run"_el, "dry-run"_el})
            .setHelpDescription("Marks the report as a simulation."_el);
    }
    [[nodiscard]] auto main() -> el::ExitCode override {
        el::io::printLine("environment: "_el, optionValues()->getText("environment"_el));
        el::io::printLine("mode: "_el, optionValues()->getFlag("dry-run"_el) ? "simulation"_el : "deployment"_el);
        return el::ExitCode::success();
    }
};

auto main(const int argc, char *argv[]) -> int {
    auto app = ReportApplication{argc, argv};
    return app.run();
}

… fully event-based applications …

class MaintenanceApplication final : public el::Application {
public:
    using Application::Application;

protected: // implement Application
    void initialize() override {
        info().setApplicationName("Maintenance Queue"_el);
        info().setApplicationVersion(el::Version{1, 0, 0});
        events()->invoke([this]() -> void { runMaintenance(); });
    }

private:
    void runMaintenance() {
        el::io::printLine("maintenance job started"_el);
        events()->invoke([this]() -> void {
            el::io::printLine("maintenance job completed"_el);
            quit();
        });
    }
};

auto main(const int argc, char *argv[]) -> int {
    auto app = MaintenanceApplication{argc, argv};
    return app.run();
}

… up to large and complex applications with independent internal services, dependencies, and communications.

auto main(const int argc, char *argv[]) -> int {
    auto app = el::Application{argc, argv};
    app.info().setApplicationName("Catalog Service"_el);
    app.registerPart<CatalogStoragePart>();
    app.registerPart<CatalogServerPart>();
    app.registerPart<CatalogUIPart>();
    app.registerPart<CatalogClientPart>();
    return app.run();
}

Events and Networking Built Together

Core combines event loops, schedulers, timers, function invocation, and event threads with event-driven network APIs.

Applications can build on TCP and UDP connections, asynchronous host-name resolution, TLS 1.3 over TCP, and HTTP client and server facilities for plain or encrypted connections. Because the layers share one event model, they can be composed without bridging unrelated callback and scheduling systems.

void HttpsServerApp::startServer() {
    _server = events()->get<el::Network>().createHttpServer();
    _server->enableTls();
    auto handler = el::HttpStaticFileHandler::create(root, "/assets"_el);
    _server->addStaticContentHandler(std::move(handler));
    configureRoutes();
    _server->events()
        .onListening([this]() -> void { onListening(); })
        .onClosed([]() -> void { el::stdOut()->printLine("HTTPS server closed gracefully."_el); })
        .onError([](const el::NetworkErrorContext &error) -> void { throw el::network::NetworkError{error}; })
        .onFinal([this]() -> void { quit(); });
    _server->start(el::IpEndpoint{_address, _port});
}

void HttpsServerApp::configureRoutes() {
    _server->events()
        .onRequest(
            el::HttpMethod{el::HttpMethodType::Get},
            "/health"_el,
            [](el::HttpServerSessionPtr, el::HttpServerRequestPtr request, el::ByteBlock) -> void {
                request->sendJson("{\"status\":\"ok\"}"_el);
            })
        .onRequest(
            el::HttpMethod{el::HttpMethodType::Get},
            "/hello/{name}"_el,
            [](el::HttpServerSessionPtr, el::HttpServerRequestPtr request, el::ByteBlock) -> void {
                request->sendText(
                    el::StringFormat{"Hello, {}!"_el}.build(request->parameter("name"_el).value_or("visitor"_el)));
            })
        .onRequestHead(
            el::HttpMethod{el::HttpMethodType::Get},
            "/stream"_el,
            [this](el::HttpServerSessionPtr, el::HttpServerRequestPtr request) -> void {
                auto response = std::make_shared<StreamedResponse>(
                    std::move(request), [this](StreamedResponse *finished) -> void { removeResponse(finished); });
                _responses.emplace_back(response);
                response->start();
            });
}

Active Development

Erbsland Core is under active development and currently in the transition from alpha to beta status. Therefore, its public API can still change without a compatibility period.

Yet the project is ready for experimenting and for new applications if you don’t mind source migrations when updating.

We are actively working on stabilizing the API and improving the documentation and closing gaps in the feature set.

Requirements

Using Erbsland Core requires:

  • A modern compiler and standard library with C++20 support (clang, GCC, or MSVC).
  • CMake 3.28 or newer.
  • Git for the recommended submodule integration.
  • Linux, macOS, or Windows.

Ninja is optional. Core can also be built and installed as a static library when source integration is not appropriate.

Sources and License

The complete source code for Erbsland Core is available on GitHub.

The documentation is published at core.erbsland.dev, including integration guides, feature topics, and an API reference. The project is released under the Apache License 2.0.