Description
STX is a collection of libraries and utilities designed to make working with C++ easier and less error-prone.
STX alternatives and similar libraries
Based on the "Miscellaneous" category.
Alternatively, view STX alternatives based on common mentions on social networks and blogs.
-
RE2
RE2 is a fast, safe, thread-friendly alternative to backtracking regular expression engines like those used in PCRE, Perl, and Python. It is a C++ library. -
UNITS
a compile-time, header-only, dimensional analysis and unit conversion library built on c++14 with no dependencies. -
cxx-prettyprint
A header-only library for C++(0x) that allows automagic pretty-printing of any container. -
CommonPP
Small library helping you with basic stuff like getting metrics out of your code, thread naming, etc.
InfluxDB - Purpose built for real-time analytics at any scale.
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of STX or a related project?
Popular Comparisons
README
C++ 17 & C++ 20 error-handling and utility extensions.
Overview
STX is a collection of libraries and utilities designed to make working with C++ easier and less error-prone.
READ THE DOCUMENTATION
SEE THE EXAMPLES
Libraries
- Panicking
-
Result<T, E>
(with constexpr in C++20) -
Option<T>
(with constexpr in C++20) - Backtracing
Features
- Efficient
Result<T, E>
(error-handling) andOption<T>
(optional-value) implementation with monadic methods - Unignorable error-types
- Fail-fast (Abandonment/ Fatal failure) via
panic
s - Runtime panic hooks
- Panic backtraces
- Signal backtraces (
SIGSEGV
,SIGILL
, andSIGFPE
) - Backtrace library
- Portable, suitable, and easily-adoptable for embedded systems, real-time systems, safety-critical systems, and operating systems
- Easy debugging
- Easy to use and hard to misuse API
- Exception-free, RTTI-free, and memory allocation free (
no-std
) - Space and time deterministic error-handling
- Deterministic value lifetimes
- Eliminates repitive code and abstractable error-handling logic code via monadic extensions
- Fast success and error return paths
- Modern and clean API
- Well-documented
- Extensively tested
- Functions using only
Result
andOption
for error and optional value handling are callable from C code as they are unions.
Basic Examples
Option
#include <iostream>
#include "stx/option.h"
using stx::Option, stx::Some, stx::None;
auto safe_divide(double numerator, double denominator) -> Option<double> {
if (denominator == 0.0) return None;
return Some(numerator / denominator);
}
int main() {
safe_divide(5.0, 2.0).match(
[](auto value) { std::cout << "Result: " << value << std::endl; },
[]() { std::cout << "Cannot divide by zero" << std::endl; });
}
Result
#include <array>
#include <cinttypes>
#include <iostream>
#include "stx/result.h"
using std::array, std::string_view;
using namespace std::literals;
using stx::Result, stx::Ok, stx::Err;
enum class Version { V1 = 1, V2 = 2 };
auto parse_version(array<uint8_t, 6> const& header) -> Result<Version, string_view> {
switch (header.at(0)) {
case 1:
return Ok(Version::V1);
case 2:
return Ok(Version::V2);
default:
return Err("Unknown Version"sv);
}
}
int main() {
parse_version({2, 3, 4, 5, 6, 7}).match([](auto version){
std::cout << "Version: " << static_cast<int>(version) << std::endl;
}, [](auto error){
std::cout << error << std::endl;
});
}
Propagating Errors with TRY_OK
TRY_OK
assigns the successful value to its first parameter version
if parse_version
returned an Ok
, else propagates the error value.
// As in the example above
auto parse_version(array<uint8_t, 6> const& header) -> Result<Version, string_view>;
auto parse_data(array<uint8_t, 6> const& header) -> Result<uint8_t, string_view> {
TRY_OK(version, parse_version(header));
return Ok(version + header[1] + header[2]);
}
int main() {
auto parsed = parse_data({2, 3, 4, 5, 6, 7}).unwrap();
std::cout << parsed << std::endl;
}
You can also add const/volatile attributes to TRY_OK
's assigned value, i.e:
auto parse_data(array<uint8_t, 6> const& header) -> Result<uint8_t, string_view> {
TRY_OK(const version, parse_version(header));
return Ok(version + header[1] + header[2]);
}
Guidelines
- Result and Option will only work in
constexpr
context (compile-time error-handling) in C++ 20, to check if you can use it asconstexpr
check if the macrosSTX_RESULT_IS_CONSTEXPR
andSTX_OPTION_IS_CONSTEXPR
are set to1
, for an example see [constexpr_test
](tests/constexpr_test.cc) . - To ensure you never forget to use the returned errors/results, raise the warning levels for your project (
-Wall
-Wextra
-Wpedantic
on GNUC-based compilers, and/W4
on MSVC) - Some methods like
map
,unwrap
,or_else
, and most ofResult
andOption
's monadic methods consume the stored value and thus theResult
orOption
has to be destroyed as its lifetime has ended. For example:
Say we define a function named safe_divide
as in the example above, with the following prototype:
auto safe_divide(float n, float d) -> Option<float>;
And we call:
float result = safe_divide(n, d).unwrap(); // compiles, because 'safe_divide' returns a temporary
Option option = safe_divide(n, d);
float result = option.unwrap(); // will not compile, because 'unwrap' consumes the value and is only usable with temporaries (as above) or r-value references (as below)
Alternatively, suppose the Option
or Result
is no longer needed, we can obtain an r-value reference:
Option option = safe_divide(n, d);
float result = std::move(option).unwrap(); // will compile, the value is moved out of 'option' , 'option' should not be used any more
NOTE: Just as any moved-from object, Option
and Result
are not to be used after a std::move
! (as the objects will be left in an unspecified state).
Result
andOption
do not make any implicit copies of the contained object as they are designed as purely forwarding types, this is especially due to their primary purpose as return channels in which we do not want duplication nor implicit copies of the returned values.
To make explicit copies:
Option option = safe_divide(n, d);
float result = option.clone().unwrap(); // note that 'clone()' explicitly makes a copy of the 'Option'
We can also obtain an l-value reference to copy the value:
Option option = safe_divide(n, d);
float result = option.value(); // note that 'value()' returns an l-value reference and 'result' is copied from 'option''s value in the process
float result = safe_divide(n, d).value(); // this won't compile as 'value' always returns an l-value reference, use 'unwrap()' instead
- All methods of
Result
andOption
pass r-value/l-value references to their invocable parameters.
[Benchmarks](benchmarks)
Release Mode ( -O3
)
Target | Real Time | CPU Time | Iterations |
---|---|---|---|
Variant/SuccessPath | 0.392 ns | 0.392 ns | 1000000000 |
Exception/SuccessPath | 0.386 ns | 0.386 ns | 1000000000 |
Result/SuccessPath | 0.381 ns | 0.381 ns | 1000000000 |
C-Style/SuccessPath | 0.387 ns | 0.386 ns | 1000000000 |
Variant/FailurePath | 0.408 ns | 0.408 ns | 1000000000 |
Exception/FailurePath | 2129 ns | 2129 ns | 317810 |
Result/FailurePath | 0.384 ns | 0.384 ns | 1000000000 |
C-Style/FailurePath | 0.384 ns | 0.383 ns | 1000000000 |
Build Requirements
- CMake
- Make or Ninja Build
- C++ 17 or C++ 20 Compiler
- Git
- Doxygen and Graphviz (for documentation)
License
[MIT License](LICENSE)
FAQ
Is STX's ABI stable?
*Note that all licence references and agreements mentioned in the STX README section above
are relevant to that project's source code only.