StormByte is a comprehensive, cross-platform C++ library aimed at easing system programming, configuration management, logging, and database handling tasks. This library provides a unified API that abstracts away the complexities and inconsistencies of different platforms (Windows, Linux).
Features
- Configuration Management: Provides an intuitive API for reading and writing configuration files.
- Database Handling: Includes SQLite support for embedded database management while hiding SQLite3 internals conveniently.
- Buffers: Provides a variety of buffer types for managing byte data in both single-threaded and multi-threaded environments. This includes lightweight non-thread-safe buffers, thread-safe shared buffers, and robust producer/consumer models that track buffer status.
- Logging: Offers various logging levels, customizable formats, and supports outputs to files, streams, or other destinations.
- Multimedia: Includes a set of classes to work with multimedia files.
- Network: Contains everything needed to handle network communication portable to Linux and Windows.
- System Operations: Manages pipes, processes, and system variables seamlessly across different platforms.
- Serialization: Provides a flexible serialization/deserialization framework that works with trivial types, standard containers, pairs, optionals, and can be extended for custom types through template specialization.
- Cryptographic functions: Cryptographic functions for encrypting, decrypting, hashing, signing and verify signatures.
Table of Contents
- Repository
- Installation
- Modules
- Contributing
- License
Repository
You can visit the code repository at GitHub
Installation
Prerequisites
Ensure you have the following installed:
- C++23 compatible compiler
- CMake 3.12 or higher
Building
To build the library, follow these steps:
git clone https://github.com/StormBytePP/StormByte.git
cd StormByte
mkdir build
cd build
cmake ..
make
Modules
StormByte Library is composed of several modules:
Base
The Base component is the core of the library containing only templates, string helpers, and the base exception framework.
Exception Handling
The Exception class provides a consistent, cross-platform mechanism for error handling in the StormByte library. It uses const char* for internal storage to avoid issues with std::string across DLL boundaries on Windows.
Features
- Formatted Messages: Supports
std::format for constructing exception messages
- Cross-Platform: Works consistently across Windows and Linux
- DLL-Safe: Uses
const char* internally to avoid std::string ABI issues
- Inheritable: Can be subclassed for domain-specific exceptions
Example Usage
#include <StormByte/exception.hxx>
#include <iostream>
void process_data(int value) {
if (value < 0) {
}
if (value > 100) {
throw Exception(std::string(
"Value too large: ") + std::to_string(value));
}
}
int main() {
try {
process_data(-5);
std::cerr <<
"Error: " << e.
what() << std::endl;
}
try {
process_data(150);
std::cerr <<
"Error: " << e.
what() << std::endl;
}
return 0;
}
Base class for exceptions in the StormByte library.
Definition exception.hxx:23
virtual const char * what() const noexcept
Gets the exception message.
Main namespace for the StormByte library.
Expected and Error Handling
StormByte provides Expected<T, E> as an alias for std::expected with enhanced support for reference types and shared error ownership. It also includes the Unexpected helper for creating error values and a custom error code system.
Features
- Reference Support: Automatically uses
std::reference_wrapper for reference types
- Shared Error Ownership: Uses
std::shared_ptr<E> for error types to allow safe copying
- Formatted Errors:
Unexpected supports std::format for error messages
- Custom Error Codes: Integration with
std::error_code via Error::Code and Error::Category
Example Usage
Basic Expected Usage
#include <StormByte/expected.hxx>
#include <StormByte/exception.hxx>
#include <iostream>
if (b == 0) {
return Unexpected<Exception>("Division by zero");
}
return a / b;
}
if (key.empty()) {
return Unexpected<Exception>("Empty key: {}", key);
}
return "config_value";
}
int main() {
auto result = divide(10, 2);
if (result) {
std::cout << "Result: " << result.value() << std::endl;
} else {
std::cerr << "Error: " << result.error()->what() << std::endl;
}
auto config = read_config("");
if (!config) {
std::cerr << "Config error: " << config.error()->what() << std::endl;
}
return 0;
}
std::conditional_t< Type::Reference< T >, std::expected< std::reference_wrapper< std::remove_reference_t< T > >, std::shared_ptr< E > >, std::expected< T, std::shared_ptr< E > > > Expected
Expected type with support for reference types.
Definition expected.hxx:33
Custom Error Codes
#include <StormByte/error.hxx>
#include <iostream>
int main() {
const auto& cat = Error::category();
std::cout << "Error category: " << cat.name() << std::endl;
return 0;
}
Serialization
The Serializable template class provides a flexible and efficient way to serialize and deserialize data into byte vectors (std::vector<std::byte>). It automatically handles trivially copyable types, standard containers (like std::vector, std::map), pairs, optionals, and can be extended for custom types through template specialization.
Features
- Automatic Type Detection: Automatically selects the appropriate serialization method based on the type
- Container Support: Works seamlessly with STL containers
- Pair and Optional Support: Built-in support for
std::pair and std::optional
- Extensible: Can be specialized for custom types
- Type-Safe: Uses
Expected<T, DeserializeError> for safe deserialization with error handling
- Zero-Copy Deserialization: Supports
std::span<const std::byte> for efficient deserialization without allocations
- Alignment-Safe: Uses
std::memcpy internally to avoid undefined behavior with misaligned data
Built-in Type Support
The library automatically handles:
- Trivially copyable types:
int, float, double, char, etc.
- Containers:
std::vector, std::list, std::map, std::set, etc.
- Pairs:
std::pair<T1, T2>
- Optionals:
std::optional<T>
- Strings:
std::string (as a container of characters)
Example Usage with Built-in Types
#include <StormByte/serializable.hxx>
#include <iostream>
#include <string>
#include <vector>
int main() {
int number = 42;
std::vector<std::byte> intData = intSer.Serialize();
if (deserializedInt) {
std::cout << "Deserialized int: " << deserializedInt.value() << std::endl;
}
std::string text = "Hello, World!";
std::vector<std::byte> strData = strSer.Serialize();
if (deserializedStr) {
std::cout << "Deserialized string: " << deserializedStr.value() << std::endl;
}
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<std::byte> vecData = vecSer.Serialize();
if (deserializedVec) {
std::cout << "Deserialized vector: ";
for (int val : deserializedVec.value()) {
std::cout << val << " ";
}
std::cout << std::endl;
}
std::span<const std::byte> dataSpan(vecData.data(), vecData.size());
if (spanResult) {
std::cout << "Deserialized from span without copying!" << std::endl;
}
return 0;
}
The class to serialize and deserialize data.
Definition serializable.hxx:27
Extending Serialization for Custom Types
You can extend the serialization framework for your custom types by implementing the SerializeComplex() and DeserializeComplex() methods, or by providing template specializations. Here's an example:
#include <StormByte/serializable.hxx>
#include <iostream>
#include <vector>
struct Point {
int x;
int y;
};
template<>
public:
std::vector<std::byte>
Serialize() const noexcept {
std::vector<std::byte> buffer;
auto xData = xSer.Serialize();
buffer.insert(buffer.end(), xData.begin(), xData.end());
auto yData = ySer.Serialize();
buffer.insert(buffer.end(), yData.begin(), yData.end());
return buffer;
}
std::size_t offset = 0;
if (offset + sizeof(int) > data.size())
return Unexpected<DeserializeError>("Insufficient data for Point.x");
offset += sizeof(int);
if (offset + sizeof(int) > data.size())
return Unexpected<DeserializeError>("Insufficient data for Point.y");
return Point{x.value(), y.value()};
}
return Deserialize(std::span<const std::byte>(data.data(), data.size()));
}
private:
const Point& m_data;
};
}
int main() {
Point p = {10, 20};
std::vector<std::byte> data = serializer.Serialize();
std::cout << "Serialized Point (" << p.x << ", " << p.y << ")" << std::endl;
if (deserialized) {
Point deserializedPoint = deserialized.value();
std::cout << "Deserialized Point: (" << deserializedPoint.x << ", " << deserializedPoint.y << ")" << std::endl;
} else {
std::cerr << "Deserialization failed: " << deserialized.error()->what() << std::endl;
}
return 0;
}
static StormByte::Expected< T, DeserializeError > Deserialize(std::span< const std::byte > data) noexcept
Deserializes data from a byte span.
Definition serializable.hxx:100
std::vector< std::byte > Serialize() const noexcept
Serializes the data into a byte vector.
Definition serializable.hxx:76
auto Unexpected(std::shared_ptr< E > error_ptr)
Creates an std::unexpected with a shared pointer to the error.
Definition expected.hxx:44
Note: By implementing template specializations in your library, you can extend serialization support to any custom type, making it seamlessly integrate with the built-in serialization framework.
String Utilities
The String namespace provides a comprehensive set of utilities for string manipulation, conversion, and formatting. These functions help with common string operations while maintaining cross-platform compatibility.
Features
- Case Conversion: Convert strings to uppercase or lowercase
- String Splitting: Split strings by delimiters or whitespace
- Fraction Parsing: Parse and scale fraction strings
- Human-Readable Formatting: Format numbers and byte sizes in human-readable formats
- UTF-8 Encoding/Decoding: Convert between wide strings and UTF-8
- Byte Vector Conversion: Convert between strings and byte vectors
- Newline Sanitization: Normalize line endings across platforms
Available Functions
Case Conversion
#include <StormByte/string.hxx>
#include <iostream>
using namespace StormByte::String;
int main() {
std::string text = "Hello World";
std::string lower = ToLower(text);
std::string upper = ToUpper(text);
std::cout << "Lower: " << lower << std::endl;
std::cout << "Upper: " << upper << std::endl;
return 0;
}
String Splitting
#include <StormByte/string.hxx>
#include <iostream>
#include <queue>
using namespace StormByte::String;
int main() {
std::string path = "path/to/file.txt";
std::queue<std::string> parts = Explode(path, '/');
while (!parts.empty()) {
std::cout << parts.front() << std::endl;
parts.pop();
}
std::string sentence = "Hello World from StormByte";
std::vector<std::string> words = Split(sentence);
for (const auto& word : words) {
std::cout << word << std::endl;
}
return 0;
}
Human-Readable Formatting
#include <StormByte/string.hxx>
#include <iostream>
using namespace StormByte::String;
int main() {
uint64_t largeNumber = 1234567890;
std::string formatted = HumanReadable(largeNumber, Format::HumanReadableNumber);
std::cout << "Number: " << formatted << std::endl;
uint64_t fileSize = 1536000;
std::string readableSize = HumanReadable(fileSize, Format::HumanReadableBytes);
std::cout << "Size: " << readableSize << std::endl;
return 0;
}
Byte Vector Conversion
#include <StormByte/string.hxx>
#include <iostream>
#include <vector>
using namespace StormByte::String;
int main() {
std::string text = "Hello, World!";
std::vector<std::byte> bytes = ToByteVector(text);
std::cout << "Byte vector size: " << bytes.size() << std::endl;
std::string recovered = FromByteVector(bytes);
std::cout << "Recovered string: " << recovered << std::endl;
return 0;
}
Fraction Parsing
#include <StormByte/string.hxx>
#include <iostream>
using namespace StormByte::String;
int main() {
auto result = SplitFraction("3/4");
if (result) {
auto [numerator, denominator] = result.value();
std::cout << "Fraction: " << numerator << "/" << denominator << std::endl;
}
auto scaled = SplitFraction("3/4", 16);
if (scaled) {
auto [num, denom] = scaled.value();
std::cout << "Scaled: " << num << "/" << denom << std::endl;
}
return 0;
}
UTF-8 Encoding/Decoding
#include <StormByte/string.hxx>
#include <iostream>
using namespace StormByte::String;
int main() {
std::wstring wide = L"Hello, 世界!";
std::string utf8 = UTF8Encode(wide);
std::cout << "UTF-8 encoded string" << std::endl;
std::wstring recovered = UTF8Decode(utf8);
return 0;
}
System Utilities
The System namespace provides cross-platform utilities for common system operations such as temporary file management, path operations, and timing functions.
Features
- Temporary File Management: Safely create temporary files with custom prefixes
- Path Operations: Get current working directory
- Sleep Functions: Cross-platform sleep with any
std::chrono::duration
Available Functions
Temporary File Creation
#include <StormByte/system.hxx>
#include <iostream>
#include <fstream>
using namespace StormByte::System;
int main() {
std::filesystem::path tempFile = TempFileName();
std::cout << "Temp file: " << tempFile << std::endl;
std::filesystem::path customTemp = TempFileName("myapp");
std::cout << "Custom temp file: " << customTemp << std::endl;
std::ofstream file(customTemp);
file << "Temporary data" << std::endl;
file.close();
std::filesystem::remove(customTemp);
return 0;
}
Current Path
#include <StormByte/system.hxx>
#include <iostream>
using namespace StormByte::System;
int main() {
std::filesystem::path current = CurrentPath();
std::cout << "Current directory: " << current << std::endl;
return 0;
}
Sleep Function
#include <StormByte/system.hxx>
#include <iostream>
#include <chrono>
using namespace StormByte::System;
using namespace std::chrono_literals;
int main() {
std::cout << "Sleeping for 2 seconds..." << std::endl;
Sleep(2s);
std::cout << "Awake!" << std::endl;
Sleep(500ms);
Sleep(std::chrono::minutes(1));
return 0;
}
UUID
The StormByte library provides a simple helper to generate RFC4122-compliant version 4 UUIDs.
- Function:
StormByte::GenerateUUIDv4()
- Returns: a 36-character, lowercase UUID string in the form
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx (where y is one of 8, 9, a, or b).
- Exception safety: marked
noexcept in the public header.
- Randomness: implementation prefers the OS CSPRNG and falls back to a PRNG if necessary.
Example
#include <StormByte/uuid.hxx>
#include <iostream>
int main() {
std::cout << "Generated UUID v4: " << uuid << std::endl;
return 0;
}
STORMBYTE_PUBLIC std::string GenerateUUIDv4() noexcept
Generate a RFC4122-compliant UUID version 4 string.
Thread Synchronization
The ThreadLock class provides a lightweight, reentrant lock with thread ownership tracking. Unlike standard mutexes, it allows the owning thread to call Lock() multiple times without blocking itself.
Features
- Reentrant for Owner: The owning thread can lock multiple times without deadlock
- Thread Ownership: Tracks which thread owns the lock
- Simple API: Just
Lock() and Unlock() methods
- Non-copyable, Non-movable: Ensures proper resource management
Example Usage
#include <StormByte/thread_lock.hxx>
#include <iostream>
#include <thread>
#include <vector>
class SharedResource {
private:
int value = 0;
public:
void increment() {
lock.Lock();
++value;
nested_operation();
lock.Unlock();
}
void nested_operation() {
lock.Lock();
value *= 2;
lock.Unlock();
}
int get_value() {
lock.Lock();
int v = value;
lock.Unlock();
return v;
}
};
int main() {
SharedResource resource;
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&resource]() {
for (int j = 0; j < 100; ++j) {
resource.increment();
}
});
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final value: " << resource.get_value() << std::endl;
return 0;
}
A lightweight thread-owned lock with reentrant semantics for the owning thread.
Clonable Interface
The Clonable template class provides a CRTP (Curiously Recurring Template Pattern) base for types that need cloning support with smart pointers. It works with both std::shared_ptr and std::unique_ptr.
Features
- Smart Pointer Agnostic: Works with
std::shared_ptr and std::unique_ptr
- Type-Safe Cloning: Virtual
Clone() method returns the correct smart pointer type
- Factory Method:
MakePointer for creating instances with the correct pointer type
- CRTP Pattern: Compile-time polymorphism for efficient cloning
Example Usage
#include <StormByte/clonable.hxx>
#include <iostream>
#include <memory>
class Shape :
public Clonable<Shape, std::shared_ptr<Shape>> {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
virtual std::shared_ptr<Shape> Clone() const override = 0;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
void draw() const override {
std::cout << "Circle with radius: " << radius << std::endl;
}
std::shared_ptr<Shape> Clone() const override {
return MakePointer<Circle>(radius);
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
void draw() const override {
std::cout << "Rectangle " << width << "x" << height << std::endl;
}
std::shared_ptr<Shape> Clone() const override {
return MakePointer<Rectangle>(width, height);
}
};
int main() {
auto circle = Shape::MakePointer<Circle>(5.0);
auto rect = Shape::MakePointer<Rectangle>(10.0, 20.0);
circle->draw();
rect->draw();
auto circle_copy = circle->Clone();
auto rect_copy = rect->Clone();
circle_copy->draw();
rect_copy->draw();
return 0;
}
A class that can be cloned.
Definition clonable.hxx:29
Type Traits
StormByte provides several custom type traits for compile-time type inspection, particularly useful for template metaprogramming and serialization.
Available Type Traits
- **
is_string<T>**: Checks if T is a string type (std::string, std::wstring, std::u16string, std::u32string)
- **
is_container<T>**: Checks if T is a container (has begin(), end(), value_type), excluding strings
- **
is_optional<T>**: Checks if T is std::optional<U>
- **
is_pair<T>**: Checks if T is std::pair<U, V> or has first and second members
- **
is_reference<T>**: Checks if T is a reference type
Example Usage
#include <StormByte/type_traits.hxx>
#include <iostream>
#include <vector>
#include <optional>
#include <string>
template<typename T>
void process() {
if constexpr (is_string<T>::value) {
std::cout << "String type" << std::endl;
} else if constexpr (is_container<T>::value) {
std::cout << "Container type" << std::endl;
} else if constexpr (is_optional<T>::value) {
std::cout << "Optional type" << std::endl;
} else if constexpr (is_pair<T>::value) {
std::cout << "Pair type" << std::endl;
} else {
std::cout << "Other type" << std::endl;
}
}
int main() {
process<std::string>();
process<std::vector<int>>();
process<std::optional<int>>();
process<std::pair<int, double>>();
process<int>();
return 0;
}
Contributing
Contributions are welcome! Please fork the repository and submit pull requests for any enhancements or bug fixes.
License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.