diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-10-04 21:36:51 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-10-04 21:36:51 +0200 |
commit | a220880dc12ff40b59130e9da0f8dfb02bef96e2 (patch) | |
tree | c4f87ef7321e5f850c70f4c43abc28d57c4e7d70 /compressed_pair.cc | |
parent | a41d3104dc730d2cba15e1ef290b52e032d6e054 (diff) | |
download | cpp-templates-a220880dc12ff40b59130e9da0f8dfb02bef96e2.tar.gz cpp-templates-a220880dc12ff40b59130e9da0f8dfb02bef96e2.zip |
add compressed pair example
Diffstat (limited to 'compressed_pair.cc')
-rw-r--r-- | compressed_pair.cc | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/compressed_pair.cc b/compressed_pair.cc new file mode 100644 index 0000000..873c4ce --- /dev/null +++ b/compressed_pair.cc @@ -0,0 +1,101 @@ +#include <algorithm> +#if 0 +set -xe +g++ -std=c++17 -fsyntax-only compressed_pair.cc +clang++ -std=c++17 -fsyntax-only compressed_pair.cc +g++ -std=c++20 -fsyntax-only compressed_pair.cc +clang++ -std=c++20 -fsyntax-only compressed_pair.cc +exit 0 +#endif + +#include <type_traits> + +// Minimal compressed pair implementation. +// +// In cpp even if a class is zero-sized, adding a member of such a class, +// allocates 1 byte in the layout of the instantiating class. In generic +// programming one sometimes wants to store a member of a template argument and +// in the best case for zero-sized template types one does not want to pay a +// price. +// +// One example for this is std::unique_ptr with a custom state-less deleter: +// +#include <memory> +struct freer { + void operator()(void* p) { + std::free(p); + } +}; + +std::unique_ptr<char, freer> ptr{nullptr}; +static_assert(sizeof(ptr) == sizeof(char*), ""); +// +// Before cpp20 this can be achieved with some template specialization tricks +// and deriving from the type if it is not empty and not final. +// +// Starting with cpp20 the [[no_unique_address]] attribute [1] was added which +// allows the compiler to not allocate space in the layout of a class for a +// given member if that is zero-sized. +// +// [1] https://en.cppreference.com/w/cpp/language/attributes/no_unique_address + +namespace cpp11 { +template <typename T, int Idx, bool = std::is_empty_v<T> && !std::is_final_v<T>> +struct entry { + T& get() { + return val; + } + + T val; +}; + +template <typename T, int Idx> +struct entry<T, Idx, true> : T { + T& get() { + return *static_cast<T*>(this); + } +}; + +template <typename T, typename U> +struct pair : entry<T, 0>, entry<U, 1> { + T& first() { + return static_cast<entry<T, 0>*>(this)->get(); + } + + U& second() { + return static_cast<entry<U, 1>*>(this)->get(); + } +}; +} // namespace cpp11 + +struct empty {}; + +#if __cplusplus >= 202002L +namespace cpp20 { +template <typename T, typename U> +struct pair { + T& first() { + return m_first; + } + + U& second() { + return m_second; + } + + [[no_unique_address]] T m_first; + [[no_unique_address]] U m_second; +}; +} // namespace cpp20 +#endif + +// -- TEST ME ------------------------------------------------------------------ + +static_assert(sizeof(cpp11::pair<int, int>) == 8); +static_assert(sizeof(cpp11::pair<int, empty>) == 4); +static_assert(sizeof(cpp11::pair<empty, empty>) == 2); + +#if __cplusplus >= 202002L +static_assert(sizeof(cpp20::pair<int, int>) == 8); +static_assert(sizeof(cpp20::pair<int, empty>) == 4); +static_assert(sizeof(cpp20::pair<empty, empty>) == 2); +#endif |