简体中文 | English
atom.utils is a collection of basic tools used in the atom engine.
It is a headers-only modern C++ library that is easy to link in various projects.
Now it includes some data structures (such as sparse_map, compressed_pair, spin_lock, etc.) as well as reflection and its corresponding serialization. The purpose of this library is to provide easy-to-use modern C++ basic tools to help users quickly build content in modern C++.
This library only supports C++20 or higher, so, make sure your compiler supports C++20.
- g++ 10 above;
- clang++13 above;
- msvc 14.29 above.
-
Clone this repo
git clone https://github.com/Shucahrjer/atom.utils cd atom.utils
-
build
cmake -B build # or you could add other args, such as cmake -DCMAKE_C_COMPILER="clang" -DCMAKE_CXX_COMPILER="clang++" -DCMAKE_MAKE_PROGRAM="ninja" -G "Ninja" -B build cd build cmake --build . --config debug
-
install
cmake --install . # --prefix
-
start dev
Core Pair Pipeline and Closure Adaptor Closure Closure Range Reflection No Macro Reflection Custom Reflection Support for Serialization Structures dense_map dense_set Thread Thread Pool Coroutine Spin Lock Hybrid Lock Signal Lambda Delegate Sink Dispatcher Memory Memory Pool Allocator Destroyer Storage around_ptr
The following content is based on such a statement
using namespace atom::utils;
Since C++20 does not have the range adapter closure in C++23, the closure type has to be introduced and supported in C++20
As the name implies, pipeline_base
is the base class that scope closures need to inherit, pipline_result
is the result of pipeline operations, and closure
is the closure
struct get_vector_fn {
template <typename... Args>
std::vector<int> operator()(Args&&... args) {
return std::vector<int>(std::forward<Args>(args)...);
}
};
auto closure = make_closure<get_vector_fn>();
// A vector of 10 elements
auto vector = closure(10);
You can use pipeline_base
to quickly build a scope closure. For example:
struct empty_view {
using pipeline_tag = pipeline_tag;
template <std::ranges::range Rng>
requires std::is_default_constructible_v<Rng>
constexpr auto operator()(Rng&& range) const {
return Rng{};
}
};
constexpr inline empty_view empty;
...
// do pipeline operation between a range and a range closure
std::vector vector = { 2, 3, 4, 6 };
auto empty_vector = vector | empty;
assert(empty_vector.empty());
// do pipeline operator between two range closures
auto closure = empty | std::views::reverse;
auto another_empty_vector = vector | closure;
assert(another_empty_vector.empty());
As we all know, std::ranges::to provided in C++23 is a very useful function that can build a container of another type from a range of one type with simple syntax. Now, it has been brought to C++20. If you use C++23, the implementation in the standard library will be called.
auto map = std::map<int, int>{{1, 2}, {2, 1}, {465, 0}, {53, 634}};
auto values = map | std::views::values;
auto vector = ranges::to<std::vector>(values);
It is almost the implementation in the standard library, except that C++20 does not have std::from_range in C++23. It is not difficult to guess that it can also get a closure like in C++23.
auto closure = ranges::to<std::vector<int>>(4);
auto vector = closure(values);
In the process of implementing std::ranges::to, some types are also introduced,
such as phony_input_iterator
, pipline_result
and range_closure
,
which are respectively "fake input iterators" for delayed loading, the results of pipeline operations and closures for delayed loading.
You can try to use them to implement some delayed loading outside the standard library.
// definition
struct a {
int member1;
char member2;
};
BEGIN_TYPE(a)
FIELDS(FIELD(member1))
END_TYPE()
// usage
auto a = ::a{};
auto reflected = utils::reflected<::a>{};
auto& tuple = reflected.fields();
auto& traits1 = std::get<index_of<"member1">(tuple)>(tuple);
int value1 = traits1.get(a);
auto& traits2 = std::get<index_of<"member2">(tuple)>(tuple);
char value2 = traits1.get(a);
Also supports serialization and deserialization of nlohmann-json
// Serialization
nlohmann::json json = a;
std::cout << json.dump(4) << '\n';
// Change the value of member1, expect it to change back to the previous value after deserialization
a.member1 = 90;
// Deserialization
a = json;
std::cout << a.member1 << '\n';
Provides allocators, memory pools, and storage
Provides two types of memory pools
synchronized_pool synchronized_pool{};
unsynchronized_pool unsynchronized_pool{};
Provides two types of allocators: standard_allocator
and allocator
Among them, standard_allocator
just encapsulates std::allocator
And allocator
needs to be created from the memory pool
synchronized_pool pool;
allocator<int, decltype(pool)> allocator{pool};
std::vector<int, decltype(allocator)> vector{allocator};
Exclusive memory and shared memory are provided.
Shared memory supports copy-on-write, which is suitable for scenarios with high copy overhead.
It should be noted that this copy-on-write is only effective when =
is used.