🦭seallib🦭

A modular and tiny C++20 powered library collection of random stuff I've needed for other projects.

--- ## Table of Contents * [Installation & Integration](#installation--integration) * [Build Options](#build-options) * [Modules](#modules) * [Logging](#1-logging) * [Assert](#2-assert) * [Events](#3-events) * [VFS](#4-vfs) * [Running tests](#running-tests) ## Installation & Integration ### 1. Requirements * **Compiler**: A C++20 compliant compiler. * **Build System**: CMake 4.1.0 or higher. ### 2. Integration via CMake #### Option A: Using FetchContent (Recommended) You can pull the library directly into your project from the git repository. Add this to your CMakeLists.txt: ```cmake include(FetchContent) FetchContent_Declare( seallib GIT_REPOSITORY https://git.neru.rip/neru/seallib.git GIT_TAG main # or a specific commit hash ) # Enable the modules you want before making it available set(SEALLIB_LOG ON) set(SEALLIB_ASSERT OFF) FetchContent_MakeAvailable(seallib) target_link_libraries(your_project PRIVATE seallib) ``` #### Option B: Local Path If you have the library downloaded locally: ```cmake # Enable the modules you want before adding it set(SEALLIB_LOG ON) set(SEALLIB_ASSERT OFF) add_subdirectory(path/to/seallib) target_link_libraries(your_project PRIVATE seallib) ``` --- ## Build Options Toggle features by setting these variables to ``ON`` or ``OFF`` in your CMake configuration. | Option | Description | Default | | :--- | :--- | :--- | | SEALLIB_ASSERT | Enable Assertion utility | OFF | | SEALLIB_EVENTS | Enable Event Dispatcher | OFF | | SEALLIB_LOG | Enable Logging Framework | OFF | | SEALLIB_VFS | Enable Virtual File System | OFF | | SEALLIB_TEST | Build the test project | OFF | --- ## Modules ### 1. Logging The Log module uses a Sink architecture. You create a Logger, attach an ILogSink, and log messages using std::format syntax. #### Example ```C++ #include #include using namespace seallib; class ConsoleSink : public ILogSink { public: void receiveLog(LogType type, std::string_view loggerName, std::string_view message) override { std::cout << getLogTypeColor(type) << "[" << getLogTypeName(type) << "] " << "[" << loggerName << "] " << message << "\x1b[0m" << std::endl; } }; int main() { Logger logger("MainApp"); logger.addSink(std::make_shared()); logger.info("Application started with version {}", 1.0); logger.error("Failed to load config: {}", "file_not_found.json"); return 0; } ``` ### 2. Assert The assert module provides diagnostic macros made to trap critical logic failures. #### Macros and their behavior | Macro | Debug Mode (`_DEBUG`) | Release Mode | | :--- | :--- | :--- | | `ASSERT(cond, msg)` | Evaluates `cond`. If `false`, logs the error location, pops up an error box, drops a debugger breakpoint trap, and calls `std::exit`. | Evaluates to a compiler optimization hint informing the optimizer that `cond` is always `true`. | | `PANIC(msg)` | Logs the error, displays an error box, breaks execution, and terminates. | Logs the error, displays an error box, breaks execution, and terminates. **Always halts execution.** | #### Example ```c++ #include void processSystemData(void* dataPtr, int size) { // 1. Defensively check states that should literally be impossible in a valid run ASSERT(dataPtr != nullptr, "Managed data pointer cannot be null during processing pipe"); // 2. Asserts behave as optimizer hints in Release mode. // The compiler now optimizes this function knowing 'size' can never be less than zero. ASSERT(size >= 0, "Buffer stream size constraint violation"); if (size == 0) { return; // Valid empty state } // Processing logic... } void processNetworkPacket(int packetId) { switch (packetId) { case 1: /* Handle read */ break; case 2: /* Handle write */ break; default: // 3. Use PANIC for unrecoverable structural states that must crash // the software immediately in both Debug and Release builds. PANIC("Critical Error: Received completely unhandled or corrupted packet opcode"); break; } } int main() { // Fails in debug: displays an error window detailing the file and line number processSystemData(nullptr, -1); return 0; } ``` ### 3. Events The Events module provides a thread and type safe signal/slot mechanism. It allows you to define custom events with any number of parameters and subscribe to them using callbacks. #### Example ```cpp #include #include using namespace seallib; // Define an event that takes an int and a string Event OnUserLogin; void onLogin(int id, std::string name) { std::cout << "User " << name << " (ID: " << id << ") logged in!" << std::endl; } int main() { // 1. Subscribe to the event auto connection = OnUserLogin.addListener(onLogin); // 2. Add a lambda listener OnUserLogin.addListener([](int id, auto name) { std::clog << "[Log] Login detected for ID: " << id << std::endl; }); // 3. Trigger the event OnUserLogin.run(42, "SomeUser"); // 4. Remove a listener when no longer needed OnUserLogin.removeListener(connection); return 0; } ``` ### 4. VFS The VFS (Virtual File System) module provides a unified interface for file operations by mapping virtual paths to user defined providers. #### Implementing a file provider To create a new storage backend (e.g., a zip loader, encrypted volume, etc), you must inherit from `IFileProvider`. #### Example provider implementation ```cpp #include using namespace seallib; class MyCustomProvider : public IFileProvider { public: // checks if file exists bool exists(const std::string& path) const override { /* implementation logic here */ return true; } // loads data from provider to outBuffer VFSResult readFile(const std::string& path, FileBuffer& outBuffer) const override { /* 1. locate the file in your system. 2. if not found, return VFSResult::FileNotFound. 3. allocate memory: outBuffer.data = std::make_unique(size); 4. copy data into outBuffer.data and set outBuffer.size = size; */ return VFSResult::Success; } // write data from a buffer to your provider VFSResult writeFile(const std::string& path, const FileBuffer& buffer) override { /* VFS ensures 'buffer' is valid (data != nullptr) before calling this. return VFSResult::AccessDenied if your provider is read-only or if that path shouldn't be written to */ return VFSResult::Success; } // generic file management operations VFSResult deleteFile(const std::string& path) override { return VFSResult::Success; } VFSResult createDirectory(const std::string& path) override { return VFSResult::Success; } // list all files and subdirectories within a given path std::vector listDirectory(const std::string& path) const override { // note: directories should ideally end with a '/' for clarity return { "file1", "file2", "file3" }; } // optional: return a name for debugging/logging std::string getProviderName() const override { return "MyCustomProvider"; } }; ``` #### Example provider usage ```c++ #include using namespace seallib; int main() { VirtualFileSystem vfs; /* mount a disk provider to the virtual "assets" folder higher priority (10 in this case) means this mount is checked before others */ vfs.mount("/assets", std::make_unique("./data"), 10); // reading a file (Paths are automatically normalized to remove '../' etc.) FileBuffer buffer; if (vfs.readFile("/assets/somefile.bin", buffer) == VFSResult::Success) { std::string rawData = buffer.toString(); } return 0; } ``` --- ## Running tests ### Windows (PowerShell + Visual Studio) A script is provided to automate generation and open the solution in Visual Studio: ./create-test.ps1 ### Cross-Platform (CLI) To build and run manually from the terminal: ```bash mkdir build && cd build cmake .. -DSEALLIB_TEST=ON cmake --build . ./seallib-test ``` ## License [MIT](https://choosealicense.com/licenses/mit/)