hyperion-utils is a foundational library for the Hyperion game engine, containing many of the "vocabulary"-level types, data structures, and core utilities used internally by the engine.
It's currently under active development and has not yet hit a stable release point.
hyperion-utils is not dependent on other parts of the engine and can be used freely, separately from any other Hyperion projects.
Some of the notable features include:
- Robust, compile-time configurable logging (logging level, sinks, global and local loggers, threading support)
- Unique data structures (ringbuffer, lock-free multi-producer, single consumer queue)
- Robust error handling facilities similar to
boost::outcomeand Rust, and semantics and an API closely matching Rust'sResult<T, E>andOption<T> - Various meta-programming facilities including custom Concepts, Type Traits, and a small meta-programming library
- Synchronization primitives similar to Rust's owning synchronization types like
Mutex<T>andRwLock<T> Enum<Types...>, a C++20 alternative to std::variant with improved API, compile-time, and performance, designed to more closely model algebraic data types from other languages
For how to get started using hyperion-utils, check out the Quick Start Guide.
You can also find the rest of the project documentation here
After following the Quick Start Guide, you'll be able to use hyperion-utils in your project. A basic example of what hyperion-utils can do is below:
#include "HyperionUtils/HyperionUtils.h"
using hyperion::Option;
using hyperion::Some;
using hyperion::None;
using hyperion::MESSAGE;
using hyperion::ERROR;
using hyperion::i32;
using hyperion::operator""_i32;
using LogParams = hyperion::logging::DefaultParameters;
struct Thing{i32 x = 0;, i32 y = 2; };
bool condition = true;
inline auto get_thing() -> Option<Thing> {
if(condition) {
return Some(Thing{.x=42_i32});
}
else {
return None();
}
}
inline auto log_thing() -> void {
if(auto thing = get_thing()) {
// Logging with the global logger can return an error if the logger hasn't been initialized,
// or the logging policy implies logging can fail (for example, if using an asynchronous
// logger that discards entries when the queue is full). Because of this, the default
// logging functions return a hyperion::Result.
// For this simple example, we'll just ignore it
ignore(MESSAGE(
"{}", // format string, see libfmt
thing.unwrap().x); //the stuff we want to log.
}
else {
ignore(ERROR("thing was None!"));
}
}
auto main(i32 argc, char** argv) -> i32 {
// create a file in the system temporary files directory in the default logging subdirectory
// and with default timestamped name (ie "/tmp/Hyperion/Hyperion [2022-11-21=13:44:56].log"),
// then create a file sink that will sink to it. If any step fails, abort the program with an
// error message
auto file_sink = hyperion::logging::FileSink::create_file()
.and_then(hyperion::logging::make_sink<hyperion::logging::FileSink, hyperion::fs::File&&>)
.expect("Failed to create the example logging file!");
// create a container of logging sinks, and push our file_sink into it
auto sinks = hyperion::logging::Sinks();
sinks.push_back(std::move(file_sink));
// create a new logger and initialize the global logger to it
auto logger = hyperion::make_unique<hyperion::Logger<Parameters>>(std::move(sinks));
hyperion::GlobalLog::set_global_logger(std::move(logger));
log_thing();
return 0;
}
Tests are currently setup as a separate target build and use the Doctest testing framework.
To run the tests, simply configure the project and build the test target, then run the resulting executable:
cmake -B build -G "Ninja"
cmake --build build --target HyperionUtilsTest
./build/HyperionUtilsTest
Feel free to submit issues, pull requests, etc!
When contributing code, please following the project .clang-format (except in judicious cases of
templates or requires clauses ruining formatting), use trailing returns types, use assign-init over direct-init
(parenthesis or braced init), and prefer simplicity and correctness over performance by default
HyperionUtils uses the MIT license.
HyperionUtils includes an assortment of CMake toolchain files. These are simply a quick and easy
(and lazy) way for us to use an associated platform + compiler combination with our preferred
optimization flags, without hard coding them in the project CMakeLists.txt. They're NOT
necessary to use HyperionUtils and they won't affect your project unless you pass them as a
toolchain file argument to CMake