├── .gitmodules ├── test ├── test.cpp ├── check_size.cpp ├── detail │ └── ilog2.cpp ├── tiny_storage.cpp ├── tagged_union_impl.cpp ├── CMakeLists.txt ├── poiner_variant_impl.cpp ├── pointer_tiny_storage.cpp ├── optional_impl.cpp ├── padding_traits.cpp ├── padding_tiny_storage.cpp ├── tombstone_traits.cpp ├── bit_view.cpp └── tiny_types.cpp ├── foonathan_tiny-config.cmake ├── example ├── CMakeLists.txt ├── custom_tiny_type.cpp ├── tombstone.cpp └── tiny_storage.cpp ├── external └── external.cmake ├── include └── foonathan │ └── tiny │ ├── detail │ ├── assert.hpp │ ├── ilog2.hpp │ ├── index_sequence.hpp │ └── select_integer.hpp │ ├── tiny_bool.hpp │ ├── tiny_enum.hpp │ ├── check_size.hpp │ ├── tiny_type.hpp │ ├── pointer_variant_impl.hpp │ ├── enum_traits.hpp │ ├── tiny_flag_set.hpp │ ├── optional_impl.hpp │ ├── pointer_tiny_storage.hpp │ ├── padding_traits.hpp │ ├── padding_tiny_storage.hpp │ ├── tagged_union_impl.hpp │ └── tiny_int.hpp ├── LICENSE.md ├── azure-pipelines.yml ├── .clang-format ├── CMakeLists.txt └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/debug_assert"] 2 | path = external/debug_assert 3 | url = https://github.com/foonathan/debug_assert 4 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #define CATCH_CONFIG_MAIN 6 | #include 7 | -------------------------------------------------------------------------------- /foonathan_tiny-config.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Jonathan Müller 2 | # This file is subject to the license terms in the LICENSE file 3 | # found in the top-level directory of this distribution. 4 | 5 | include(CMakeFindDependencyMacro) 6 | find_dependency(debug_assert) 7 | include("${CMAKE_CURRENT_LIST_DIR}/foonathan_tiny-targets.cmake") 8 | -------------------------------------------------------------------------------- /test/check_size.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | using namespace foonathan::tiny; 8 | 9 | namespace 10 | { 11 | struct foo 12 | {}; 13 | static_assert(check_size(), ""); 14 | FOONATHAN_TINY_CHECK_SIZE(foo, 1); 15 | static_assert(check_alignment(), ""); 16 | FOONATHAN_TINY_CHECK_ALIGNMENT(foo, 1); 17 | } // namespace 18 | -------------------------------------------------------------------------------- /test/detail/ilog2.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace foonathan::tiny::detail; 10 | 11 | namespace 12 | { 13 | void check(std::size_t x, std::size_t a, std::size_t b) 14 | { 15 | INFO(x); 16 | CHECK(ilog2(x) == a); 17 | REQUIRE(ilog2_ceil(x) == b); 18 | } 19 | } // namespace 20 | 21 | TEST_CASE("detail::ilog2") 22 | { 23 | check(1, 0, 0); 24 | check(2, 1, 1); 25 | check(3, 1, 2); 26 | check(4, 2, 2); 27 | check(5, 2, 3); 28 | check(6, 2, 3); 29 | check(7, 2, 3); 30 | check(8, 3, 3); 31 | 32 | check(255, 7, 8); 33 | check(256, 8, 8); 34 | } 35 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Jonathan Müller 2 | # This file is subject to the license terms in the LICENSE file 3 | # found in the top-level directory of this distribution. 4 | 5 | add_executable(foonathan_tiny_storage tiny_storage.cpp) 6 | target_link_libraries(foonathan_tiny_storage PUBLIC foonathan_tiny) 7 | 8 | add_executable(foonathan_tiny_custom_tiny_type custom_tiny_type.cpp) 9 | target_link_libraries(foonathan_tiny_custom_tiny_type PUBLIC foonathan_tiny) 10 | 11 | add_executable(foonathan_tiny_tombstone tombstone.cpp) 12 | target_link_libraries(foonathan_tiny_tombstone PUBLIC foonathan_tiny) 13 | 14 | add_executable(foonathan_tiny_tombstone_manual tombstone.cpp) 15 | target_link_libraries(foonathan_tiny_tombstone_manual PUBLIC foonathan_tiny) 16 | target_compile_definitions(foonathan_tiny_tombstone_manual PUBLIC TOMBSTONE_MANUAL) 17 | -------------------------------------------------------------------------------- /external/external.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Jonathan Müller 2 | # This file is subject to the license terms in the LICENSE file 3 | # found in the top-level directory of this distribution. 4 | 5 | option(FOONATHAN_TINY_FORCE_FIND_PACKAGE "force find_package() instead of using git submodule" OFF) 6 | set(dependency_via_submodule OFF) 7 | 8 | if(FOONATHAN_TINY_FORCE_FIND_PACKAGE) 9 | find_package(debug_assert REQUIRED) 10 | else() 11 | find_package(debug_assert QUIET) 12 | if(NOT debug_assert_FOUND) 13 | set(dependency_via_submodule ON) 14 | if(TARGET debug_assert) 15 | message(STATUS "Using inherited debug_assert target") 16 | else() 17 | message(STATUS "Installing debug_assert via submodule") 18 | execute_process(COMMAND git submodule update --init -- external/debug_assert 19 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 20 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/debug_assert EXCLUDE_FROM_ALL) 21 | endif() 22 | endif() 23 | endif() 24 | -------------------------------------------------------------------------------- /include/foonathan/tiny/detail/assert.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_DETAIL_ASSERT_HPP_INCLUDED 6 | #define FOONATHAN_TINY_DETAIL_ASSERT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #ifndef FOONATHAN_TINY_ENABLE_ASSERTIONS 11 | # define FOONATHAN_TINY_ENABLE_ASSERTIONS 0 12 | #endif 13 | 14 | #ifndef FOONATHAN_TINY_ENABLE_PRECONDITIONS 15 | # ifdef NDEBUG 16 | # define FOONATHAN_TINY_ENABLE_PRECONDITIONS 0 17 | # else 18 | # define FOONATHAN_TINY_ENABLE_PRECONDITIONS 1 19 | # endif 20 | #endif 21 | 22 | namespace foonathan 23 | { 24 | namespace tiny 25 | { 26 | namespace detail 27 | { 28 | struct assert_handler 29 | : debug_assert::default_handler, 30 | debug_assert::set_level(FOONATHAN_TINY_ENABLE_ASSERTIONS)> 31 | {}; 32 | 33 | struct precondition_handler 34 | : debug_assert::default_handler, 35 | debug_assert::set_level(FOONATHAN_TINY_ENABLE_PRECONDITIONS)> 36 | {}; 37 | } // namespace detail 38 | } // namespace tiny 39 | } // namespace foonathan 40 | 41 | #endif // FOONATHAN_TINY_DETAIL_ASSERT_HPP_INCLUDED 42 | -------------------------------------------------------------------------------- /include/foonathan/tiny/tiny_bool.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TINY_BOOL_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TINY_BOOL_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace foonathan 11 | { 12 | namespace tiny 13 | { 14 | /// A `TinyType` implementation of `bool`. 15 | class tiny_bool 16 | { 17 | public: 18 | using object_type = bool; 19 | 20 | static constexpr std::size_t bit_size() noexcept 21 | { 22 | return 1u; 23 | } 24 | 25 | template 26 | class proxy 27 | { 28 | public: 29 | const proxy& operator=(bool value) const noexcept 30 | { 31 | view_.put(value ? 1 : 0); 32 | return *this; 33 | } 34 | 35 | operator bool() const noexcept 36 | { 37 | return view_.extract() != 0; 38 | } 39 | 40 | private: 41 | explicit proxy(BitView view) noexcept : view_(view) {} 42 | 43 | BitView view_; 44 | 45 | friend tiny_type_access; 46 | }; 47 | }; 48 | } // namespace tiny 49 | } // namespace foonathan 50 | 51 | #endif // FOONATHAN_TINY_TINY_BOOL_HPP_INCLUDED 52 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Jonathan Müller 2 | 3 | Boost Software License - Version 1.0 - August 17th, 2003 4 | 5 | Permission is hereby granted, free of charge, to any person or organization 6 | obtaining a copy of the software and accompanying documentation covered by 7 | this license (the "Software") to use, reproduce, display, distribute, 8 | execute, and transmit the Software, and to prepare derivative works of the 9 | Software, and to permit third-parties to whom the Software is furnished to 10 | do so, all subject to the following: 11 | 12 | The copyright notices in the Software and this entire statement, including 13 | the above license grant, this restriction and the following disclaimer, 14 | must be included in all copies of the Software, in whole or in part, and 15 | all derivative works of the Software, unless such copies or derivative 16 | works are solely in the form of machine-executable object code generated by 17 | a source language processor. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /include/foonathan/tiny/detail/ilog2.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_DETAIL_ILOG2_HPP_INCLUDED 6 | #define FOONATHAN_TINY_DETAIL_ILOG2_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace foonathan 11 | { 12 | namespace tiny 13 | { 14 | namespace detail 15 | { 16 | // undefined for 0 17 | template 18 | constexpr bool is_power_of_two(UInt x) noexcept 19 | { 20 | return (x & (x - 1)) == 0; 21 | } 22 | 23 | constexpr std::size_t ilog2_base(std::size_t x) noexcept 24 | { 25 | return x == 0 ? 0 : ilog2_base(x >> 1) + 1; 26 | } 27 | 28 | // ilog2() implementation, cuts part after the comma 29 | // e.g. 1 -> 0, 2 -> 1, 3 -> 1, 4 -> 2, 5 -> 2 30 | constexpr std::size_t ilog2(std::size_t x) noexcept 31 | { 32 | return ilog2_base(x) - 1; 33 | } 34 | 35 | // ceiling ilog2() implementation, adds one if part after comma 36 | // e.g. 1 -> 0, 2 -> 1, 3 -> 2, 4 -> 2, 5 -> 3 37 | constexpr std::size_t ilog2_ceil(std::size_t x) noexcept 38 | { 39 | // only subtract one if power of two 40 | return ilog2_base(x) - std::size_t(is_power_of_two(x)); 41 | } 42 | } // namespace detail 43 | } // namespace tiny 44 | } // namespace foonathan 45 | 46 | #endif // FOONATHAN_TINY_DETAIL_ILOG2_HPP_INCLUDED 47 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | name: 'CI build' 2 | trigger: 3 | branches: 4 | include: 5 | - master 6 | - feature/* 7 | paths: 8 | exclude: 9 | - README.md 10 | 11 | jobs: 12 | - job: windows 13 | pool: 14 | vmImage: 'vs2017-win2016' 15 | steps: 16 | - script: | 17 | mkdir build && cd build/ 18 | cmake ../ && cmake --build . && ctest -C Debug --output-on-failure 19 | displayName: "Compiling using Visual Studio 2017" 20 | 21 | - job: linux 22 | pool: 23 | vmImage: 'Ubuntu 16.04' 24 | strategy: 25 | matrix: 26 | GCC5: 27 | IMAGE: 'conanio/gcc5' 28 | GCC6: 29 | IMAGE: 'conanio/gcc6' 30 | GCC7: 31 | IMAGE: 'conanio/gcc7' 32 | GCC8: 33 | IMAGE: 'conanio/gcc8' 34 | clang4: 35 | IMAGE: 'conanio/clang40' 36 | clang5: 37 | IMAGE: 'conanio/clang50' 38 | clang6: 39 | IMAGE: 'conanio/clang60' 40 | clang7: 41 | IMAGE: 'conanio/clang7' 42 | steps: 43 | - script: docker run --privileged -u root -v "$PWD:/tiny" $(IMAGE) bash -c "cmake /tiny/ && cmake --build . && ctest --output-on-failure" 44 | displayName: "Compiling using $(IMAGE)" 45 | 46 | - job: macos 47 | pool: 48 | vmImage: 'macOS-10.13' 49 | strategy: 50 | matrix: 51 | XCode10: 52 | XCODE: '10' 53 | XCode9: 54 | XCODE: '9.4.1' 55 | steps: 56 | - script: | 57 | sudo xcode-select -s /Applications/Xcode_$(XCODE).app/Contents/Developer 58 | mkdir build && cd build/ 59 | cmake ../ && cmake --build . && ctest --output-on-failure 60 | displayName: "Compiling using XCode $(XCODE)" 61 | -------------------------------------------------------------------------------- /test/tiny_storage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | using namespace foonathan::tiny; 13 | 14 | TEST_CASE("tiny_storage") 15 | { 16 | SECTION("basic") 17 | { 18 | struct first 19 | {}; 20 | struct second 21 | {}; 22 | using storage = tiny_storage, tiny_bool, tiny_bool>; 23 | 24 | storage s; 25 | const auto& cs = s; 26 | 27 | REQUIRE(s.at<0>() == 0); 28 | REQUIRE(s.at<1>() == false); 29 | REQUIRE(s.at<2>() == false); 30 | 31 | s.at<0>() = 42; 32 | s.at<1>() = true; 33 | s.at<2>() = true; 34 | 35 | REQUIRE(s.at<0>() == 42); 36 | REQUIRE(s.at<1>() == true); 37 | REQUIRE(s.at<2>() == true); 38 | REQUIRE(cs.at<0>() == 42); 39 | REQUIRE(cs.at<1>() == true); 40 | REQUIRE(cs.at<2>() == true); 41 | 42 | REQUIRE(s[tiny_unsigned<7>{}] == 42); 43 | REQUIRE(cs[tiny_unsigned<7>{}] == 42); 44 | 45 | s = storage(7, false, true); 46 | REQUIRE(s.at<0>() == 7); 47 | REQUIRE(s.at<1>() == false); 48 | REQUIRE(s.at<2>() == true); 49 | } 50 | SECTION("empty") 51 | { 52 | using storage = tiny_storage<>; 53 | 54 | storage s; 55 | REQUIRE(s.spare_bits().size() == CHAR_BIT); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /include/foonathan/tiny/detail/index_sequence.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_DETAIL_INDEX_SEQUENCE_HPP_INCLUDED 6 | #define FOONATHAN_TINY_DETAIL_INDEX_SEQUENCE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace foonathan 11 | { 12 | namespace tiny 13 | { 14 | namespace detail 15 | { 16 | template 17 | struct integer_sequence 18 | { 19 | using type = integer_sequence; 20 | }; 21 | 22 | template 23 | using index_sequence = integer_sequence; 24 | 25 | #if defined(__clang__) 26 | template 27 | using make_index_sequence = __make_integer_seq; 28 | #elif defined(__GNUC__) && __GNUC__ >= 8 29 | template 30 | using make_index_sequence = index_sequence<__integer_pack(Size)...>; 31 | #else 32 | // adapted from https://stackoverflow.com/a/32223343 33 | template 34 | struct concat_seq; 35 | 36 | template 37 | struct concat_seq, index_sequence> 38 | : index_sequence 39 | {}; 40 | 41 | template 42 | struct make_index_sequence : concat_seq::type, 43 | typename make_index_sequence::type> 44 | {}; 45 | template <> 46 | struct make_index_sequence<0> : index_sequence<> 47 | {}; 48 | template <> 49 | struct make_index_sequence<1> : index_sequence<0> 50 | {}; 51 | #endif 52 | } // namespace detail 53 | } // namespace tiny 54 | } // namespace foonathan 55 | 56 | #endif // FOONATHAN_TINY_DETAIL_INDEX_SEQUENCE_HPP_INCLUDED 57 | -------------------------------------------------------------------------------- /include/foonathan/tiny/detail/select_integer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_DETAIL_SELECT_INTEGER_HPP_INCLUDED 6 | #define FOONATHAN_TINY_DETAIL_SELECT_INTEGER_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace foonathan 14 | { 15 | namespace tiny 16 | { 17 | namespace detail 18 | { 19 | template 20 | struct select_integer_impl 21 | { 22 | static_assert(Size == 0u, "too many bits"); 23 | using type = void; 24 | }; 25 | 26 | constexpr auto max_uint_bits = sizeof(std::uint_least64_t) * CHAR_BIT; 27 | 28 | #define FOONATHAN_TINY_DETAIL_SELECT(Min, Max, Type) \ 29 | template \ 30 | struct select_integer_impl (Min) && Size <= (Max))>::type> \ 32 | { \ 33 | using type = Type; \ 34 | }; 35 | 36 | FOONATHAN_TINY_DETAIL_SELECT(0u, 8u, std::uint_least8_t) 37 | FOONATHAN_TINY_DETAIL_SELECT(8u, 16u, std::uint_least16_t) 38 | FOONATHAN_TINY_DETAIL_SELECT(16u, 32u, std::uint_least32_t) 39 | FOONATHAN_TINY_DETAIL_SELECT(32u, max_uint_bits, std::uint_least64_t) 40 | 41 | #undef FOONATHAN_TINY_DETAIL_SELECT 42 | 43 | template 44 | using uint_least_n_t = typename select_integer_impl::type; 45 | } // namespace detail 46 | } // namespace tiny 47 | } // namespace foonathan 48 | 49 | #endif // FOONATHAN_TINY_DETAIL_SELECT_INTEGER_HPP_INCLUDED 50 | -------------------------------------------------------------------------------- /test/tagged_union_impl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace foonathan::tiny; 10 | 11 | namespace 12 | { 13 | using types = union_types; 14 | 15 | struct A 16 | { 17 | tagged_union_tag tag; 18 | int i; 19 | 20 | A() : i(42) {} 21 | 22 | void verify() const 23 | { 24 | REQUIRE(i == 42); 25 | } 26 | }; 27 | 28 | struct B 29 | { 30 | tagged_union_tag tag; 31 | using tiny_ts = tiny_types>; 32 | 33 | B() 34 | { 35 | tag.tiny_view(tiny_ts{}).tiny() = 11; 36 | } 37 | 38 | void verify() const 39 | { 40 | REQUIRE(tag.tiny_view(tiny_ts{}).tiny() == 11); 41 | } 42 | }; 43 | 44 | struct C 45 | { 46 | tagged_union_tag tag; 47 | char additional[3]; 48 | 49 | using tiny_ts = tiny_types>; 50 | 51 | C() 52 | { 53 | tag.tiny_view(tiny_ts{}, make_bit_view<0, last_bit>(additional)).tiny() = 1u << 20; 54 | } 55 | 56 | void verify() const 57 | { 58 | REQUIRE((tag.tiny_view(tiny_ts{}, make_bit_view<0, last_bit>(additional))).tiny() 59 | == 1u << 20); 60 | } 61 | }; 62 | 63 | template 64 | void verify_union(Union& u, std::size_t index) 65 | { 66 | REQUIRE(u.template has_value()); 67 | REQUIRE(u.tag() == index); 68 | u.template value().verify(); 69 | } 70 | } // namespace 71 | 72 | TEST_CASE("tagged_union_impl") 73 | { 74 | tagged_union_impl u; 75 | const auto& cu = u; 76 | 77 | u.create_value(); 78 | verify_union(u, 0); 79 | verify_union(cu, 0); 80 | u.destroy_value(); 81 | 82 | u.create_value(); 83 | verify_union(u, 1); 84 | verify_union(cu, 1); 85 | u.destroy_value(); 86 | 87 | u.create_value(); 88 | verify_union(u, 2); 89 | verify_union(cu, 2); 90 | u.destroy_value(); 91 | } 92 | -------------------------------------------------------------------------------- /include/foonathan/tiny/tiny_enum.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TINY_ENUM_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TINY_ENUM_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace foonathan 12 | { 13 | namespace tiny 14 | { 15 | /// A `TinyType` implementation of an enumeration type. 16 | /// 17 | /// `EnumOrTraits` is either an enum type or an `enum_traits`-like type. 18 | /// [lex::traits_of_enum]() is then applied. 19 | /// 20 | /// \requires The enum must be contiguous with the valid enumerators stored in the range `[0, 21 | /// Traits::max]`. 22 | template 23 | class tiny_enum 24 | { 25 | using traits = traits_of_enum; 26 | static_assert(traits::is_contiguous, "enum must be contiguous"); 27 | static_assert(traits::min() == typename traits::enum_type(0), "enum value must start at 0"); 28 | 29 | public: 30 | using object_type = typename traits::enum_type; 31 | 32 | static constexpr std::size_t bit_size() noexcept 33 | { 34 | return enum_bit_size(); 35 | } 36 | 37 | template 38 | class proxy 39 | { 40 | public: 41 | const proxy& operator=(object_type value) const noexcept 42 | { 43 | DEBUG_ASSERT(is_valid_enum_value(value), detail::precondition_handler{}, 44 | "not a valid enum value"); 45 | view_.put(static_cast(value)); 46 | return *this; 47 | } 48 | 49 | operator object_type() const noexcept 50 | { 51 | return static_cast(view_.extract()); 52 | } 53 | 54 | private: 55 | explicit proxy(BitView view) noexcept : view_(view) {} 56 | 57 | BitView view_; 58 | 59 | friend tiny_type_access; 60 | }; 61 | }; 62 | } // namespace tiny 63 | } // namespace foonathan 64 | 65 | #endif // FOONATHAN_TINY_TINY_ENUM_HPP_INCLUDED 66 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: Align 3 | AlignConsecutiveAssignments: true 4 | AlignConsecutiveDeclarations: true 5 | AlignEscapedNewlinesLeft: Right 6 | AlignOperands: true 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: Empty 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AlwaysBreakAfterReturnType: None 15 | AlwaysBreakBeforeMultilineStrings: false 16 | AlwaysBreakTemplateDeclarations: true 17 | BinPackArguments: true 18 | BinPackParameters: true 19 | BreakBeforeBraces: Custom 20 | BraceWrapping: 21 | AfterClass: true 22 | AfterControlStatement: true 23 | AfterEnum: true 24 | AfterFunction: true 25 | AfterNamespace: true 26 | AfterStruct: true 27 | AfterUnion: true 28 | AfterExternBlock: true 29 | BeforeCatch: true 30 | BeforeElse: true 31 | SplitEmptyFunction: false 32 | SplitEmptyRecord: false 33 | SplitEmptyNamespace: false 34 | BreakBeforeBinaryOperators: All 35 | BreakBeforeTernaryOperators: true 36 | BreakConstructorInitializers: BeforeColon 37 | BreakStringLiterals: false 38 | ColumnLimit: 100 39 | CompactNamespaces: true 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 41 | ConstructorInitializerIndentWidth: 0 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | FixNamespaceComments: true 46 | IncludeBlocks: Preserve 47 | IndentCaseLabels: false 48 | IndentPPDirectives: AfterHash 49 | IndentWidth: 4 50 | IndentWrappedFunctionNames: true 51 | KeepEmptyLinesAtTheStartOfBlocks: false 52 | Language: Cpp 53 | MaxEmptyLinesToKeep: 1 54 | NamespaceIndentation: Inner 55 | PenaltyBreakBeforeFirstCallParameter: 19937 56 | PenaltyReturnTypeOnItsOwnLine: 19937 57 | PointerAlignment: Left 58 | ReflowComments: true 59 | SortIncludes: true 60 | SortUsingDeclarations: true 61 | SpaceAfterCStyleCast: false 62 | SpaceAfterTemplateKeyword: true 63 | SpaceBeforeAssignmentOperators: true 64 | SpaceBeforeParens: ControlStatements 65 | SpaceInEmptyParentheses: false 66 | SpacesBeforeTrailingComments: 1 67 | SpacesInAngles: false 68 | SpacesInCStyleCastParentheses: false 69 | SpacesInParentheses: false 70 | SpacesInSquareBrackets: false 71 | Standard: Cpp11 72 | TabWidth: 4 73 | UseTab: Never 74 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Jonathan Müller 2 | # This file is subject to the license terms in the LICENSE file 3 | # found in the top-level directory of this distribution. 4 | 5 | # get Catch 6 | if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/catch.hpp) 7 | file(DOWNLOAD 8 | https://raw.githubusercontent.com/catchorg/Catch2/master/single_include/catch2/catch.hpp 9 | ${CMAKE_CURRENT_BINARY_DIR}/catch.hpp 10 | STATUS status 11 | LOG log) 12 | 13 | list(GET status 0 status_code) 14 | list(GET status 1 status_string) 15 | 16 | if(NOT status_code EQUAL 0) 17 | message(FATAL_ERROR "error downloading catch: ${status_string}" 18 | "${log}") 19 | endif() 20 | endif() 21 | 22 | # define dummy target with all the options 23 | # can't link to foonathan_tiny, as it adds a SYSTEM interface 24 | add_library(foonathan_tiny_test_base INTERFACE) 25 | target_sources(foonathan_tiny_test_base INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/test.cpp) 26 | target_include_directories(foonathan_tiny_test_base INTERFACE 27 | ${CMAKE_CURRENT_SOURCE_DIR} 28 | ${CMAKE_CURRENT_SOURCE_DIR}/../include 29 | SYSTEM INTERFACE 30 | ${CMAKE_CURRENT_BINARY_DIR}) 31 | target_compile_features(foonathan_tiny_test_base INTERFACE cxx_std_11) 32 | target_link_libraries(foonathan_tiny_test_base INTERFACE debug_assert) 33 | target_compile_definitions(foonathan_tiny_test_base INTERFACE 34 | FOONATHAN_TINY_ENABLE_ASSERTIONS=1 35 | FOONATHAN_TINY_ENABLE_PRECONDITIONS=1) 36 | target_compile_options(foonathan_tiny_test_base INTERFACE 37 | # clang/GCC warnings 38 | $<$,$>: 39 | -pedantic-errors -Werror -Wall -Wextra -Wconversion -Wsign-conversion> 40 | # disable noexcept type warning on GCC 41 | $<$: -Wno-noexcept-type> 42 | # MSVC warnings 43 | $<$: 44 | /WX /W4>) 45 | # enable sanitizer 46 | target_compile_options(foonathan_tiny_test_base INTERFACE 47 | $<$: 48 | -g -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer>) 49 | target_link_libraries(foonathan_tiny_test_base INTERFACE 50 | $<$: 51 | -fsanitize=address -fsanitize=undefined>) 52 | 53 | # unit tests 54 | set(tests 55 | detail/ilog2.cpp 56 | bit_view.cpp 57 | check_size.cpp 58 | optional_impl.cpp 59 | pointer_tiny_storage.cpp 60 | padding_tiny_storage.cpp 61 | padding_traits.cpp 62 | poiner_variant_impl.cpp 63 | tombstone_traits.cpp 64 | tagged_union_impl.cpp 65 | tiny_types.cpp 66 | tiny_storage.cpp) 67 | 68 | add_executable(foonathan_tiny_test ${tests}) 69 | target_link_libraries(foonathan_tiny_test PUBLIC foonathan_tiny_test_base) 70 | add_test(NAME test COMMAND foonathan_tiny_test) 71 | 72 | -------------------------------------------------------------------------------- /include/foonathan/tiny/check_size.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_CHECK_SIZE_HPP_INCLUDED 6 | #define FOONATHAN_TINY_CHECK_SIZE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | namespace foonathan 11 | { 12 | namespace tiny 13 | { 14 | /// \exclude 15 | namespace detail 16 | { 17 | template 18 | struct sizeof_is 19 | { 20 | template 21 | struct but_expected 22 | { 23 | static_assert(Size == Expected, "size wasn't as expected"); 24 | static constexpr bool value = true; 25 | }; 26 | }; 27 | 28 | template 29 | struct alignment_is 30 | { 31 | template 32 | struct but_expected 33 | { 34 | static_assert(Alignment == Expected, "alignment wasn't as expected"); 35 | static constexpr bool value = true; 36 | }; 37 | }; 38 | } // namespace detail 39 | 40 | /// Checks that `T` has the given `Size`. 41 | /// 42 | /// The function will always return `true`, but if `sizeof(T)` isn't` Size, 43 | /// it will trigger a `static_assert()` where the instantiation will contain the actual value of 44 | /// `sizeof(T)`. 45 | /// 46 | /// You just need to call this function somewhere, but as it returns a boolean, 47 | /// you can just use a `static_assert(check_size())` after the class definition. 48 | template 49 | constexpr bool check_size() noexcept 50 | { 51 | return detail::sizeof_is::template but_expected::value; 52 | } 53 | 54 | /// Expands to something equivalent to `static_assert(check_size())`, i.e. checks that 55 | /// `T` has size `Size`. 56 | #define FOONATHAN_TINY_CHECK_SIZE(T, Size) \ 57 | static_assert(foonathan::tiny::detail::sizeof_is::template but_expected::value \ 59 | || true, \ 60 | "") 61 | 62 | /// Same as [tiny::check_size]() but for the alignment. 63 | template 64 | constexpr bool check_alignment() noexcept 65 | { 66 | return detail::alignment_is::template but_expected::value; 67 | } 68 | 69 | /// Same as [FOONATHAN_TINY_CHECK_SIZE]() but for the alignment. 70 | #define FOONATHAN_TINY_CHECK_ALIGNMENT(T, Alignment) \ 71 | static_assert(foonathan::tiny::detail::alignment_is::template but_expected::value \ 73 | || true, \ 74 | "") 75 | } // namespace tiny 76 | } // namespace foonathan 77 | 78 | #endif // FOONATHAN_TINY_CHECK_SIZE_HPP_INCLUDED 79 | -------------------------------------------------------------------------------- /include/foonathan/tiny/tiny_type.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TINY_TYPE_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TINY_TYPE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace foonathan 12 | { 13 | namespace tiny 14 | { 15 | #if 0 16 | /// A type that only occupies a couple of bits. 17 | struct TinyType 18 | { 19 | /// The proper object type whose size is measured in whole bytes. 20 | using object_type = ...; 21 | 22 | /// \returns The size of the object in bits. 23 | static constexpr std::size_t bit_size() noexcept; 24 | 25 | /// A proxy for `object_type`. 26 | /// 27 | /// `BitView` is a [tiny::bit_view]() viewing the `bit_size()` bits of storage the object occupies. 28 | /// It should implement the required functions to be a stand-in for `object_type`, 29 | /// but at the very least it should provide a conversion operator that converts the view into the object type 30 | /// and must provide an assignment operator from the object type that changes the bits to store a new object. 31 | template 32 | class proxy 33 | { 34 | public: 35 | ... 36 | 37 | private: 38 | /// \effects Constructs it from the given `BitView`. 39 | explicit proxy(BitView view); 40 | 41 | /// The constructor is private, so you must friend `tiny_type_access`. 42 | friend tiny_type_access; 43 | }; 44 | }; 45 | #endif 46 | 47 | /// This class must be friend of a `TinyType`. 48 | class tiny_type_access 49 | { 50 | template 51 | static auto make(View view) noexcept -> typename TinyType::template proxy 52 | { 53 | return typename TinyType::template proxy(view); 54 | } 55 | 56 | template 57 | friend auto make_tiny_proxy(bit_view view) noexcept -> 58 | typename TinyType::template proxy>; 59 | }; 60 | 61 | /// Creates a `TinyType` proxy viewing the given bits. 62 | template 63 | auto make_tiny_proxy(bit_view view) noexcept -> 64 | typename TinyType::template proxy> 65 | { 66 | static_assert(view.size() == TinyType::bit_size(), "invalid size"); 67 | return tiny_type_access::make(view); 68 | } 69 | 70 | namespace detail 71 | { 72 | template ( 73 | std::declval>()))> 74 | std::true_type test_tiny_type(int); 75 | 76 | template 77 | std::false_type test_tiny_type(short); 78 | } // namespace detail 79 | 80 | /// Whether or not the given type is a `TinyType`. 81 | template 82 | struct is_tiny_type : decltype(detail::test_tiny_type(0)) 83 | {}; 84 | 85 | /// A list of tiny types. 86 | template 87 | struct tiny_types 88 | {}; 89 | } // namespace tiny 90 | } // namespace foonathan 91 | 92 | #endif // FOONATHAN_TINY_TINY_TYPE_HPP_INCLUDED 93 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Jonathan Müller 2 | # This file is subject to the license terms in the LICENSE file 3 | # found in the top-level directory of this distribution. 4 | 5 | cmake_minimum_required(VERSION 3.8) 6 | project(foonathan_tiny VERSION 0.0.0) 7 | 8 | include(external/external.cmake) 9 | 10 | # source files 11 | set(detail_header_files 12 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/detail/assert.hpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/detail/ilog2.hpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/detail/index_sequence.hpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/detail/select_integer.hpp 16 | ) 17 | set(header_files 18 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/bit_view.hpp 19 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/check_size.hpp 20 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/enum_traits.hpp 21 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/optional_impl.hpp 22 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/padding_tiny_storage.hpp 23 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/padding_traits.hpp 24 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/pointer_tiny_storage.hpp 25 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/pointer_variant_impl.hpp 26 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tagged_union_impl.hpp 27 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tombstone.hpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tiny_bool.hpp 29 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tiny_enum.hpp 30 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tiny_flag_set.hpp 31 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tiny_int.hpp 32 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tiny_storage.hpp 33 | ${CMAKE_CURRENT_SOURCE_DIR}/include/foonathan/tiny/tiny_type.hpp 34 | ) 35 | 36 | # main target 37 | add_library(foonathan_tiny INTERFACE) 38 | add_library(foonathan::foonathan_tiny ALIAS foonathan_tiny) 39 | target_sources(foonathan_tiny INTERFACE $) 40 | target_compile_features(foonathan_tiny INTERFACE cxx_std_11) 41 | target_include_directories(foonathan_tiny SYSTEM INTERFACE 42 | $ 43 | $) 44 | target_link_libraries(foonathan_tiny INTERFACE debug_assert) 45 | 46 | # installation 47 | if(NOT dependency_via_submodule) 48 | include(CMakePackageConfigHelpers) 49 | write_basic_package_version_file(foonathan_tiny-config-version.cmake COMPATIBILITY ExactVersion) 50 | install(TARGETS foonathan_tiny EXPORT foonathan_tiny_targets 51 | INCLUDES DESTINATION include) 52 | install(EXPORT foonathan_tiny_targets 53 | DESTINATION lib/cmake/foonathan_tiny 54 | FILE foonathan_tiny-targets.cmake 55 | NAMESPACE foonathan::) 56 | install(DIRECTORY include/ 57 | DESTINATION include) 58 | install(FILES foonathan_tiny-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/foonathan_tiny-config-version.cmake 59 | DESTINATION lib/cmake/foonathan_tiny) 60 | else() 61 | message(STATUS "Dependency installed via submodule, installation unavailable") 62 | endif() 63 | 64 | # subdirectories 65 | option(FOONATHAN_TINY_BUILD_TEST "build tests of foonathan/tiny" OFF) 66 | if(${FOONATHAN_TINY_BUILD_TEST} OR (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)) 67 | enable_testing() 68 | add_subdirectory(test) 69 | endif() 70 | 71 | option(FOONATHAN_TINY_BUILD_EXAMPLE "build examples of foonathan/tiny" OFF) 72 | if(${FOONATHAN_TINY_BUILD_EXAMPLE} OR (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)) 73 | add_subdirectory(example) 74 | endif() 75 | -------------------------------------------------------------------------------- /test/poiner_variant_impl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace foonathan::tiny; 10 | 11 | namespace 12 | { 13 | template 14 | struct get_pointer 15 | { 16 | using type = T; 17 | 18 | static T* get() 19 | { 20 | return reinterpret_cast(std::uintptr_t(1024)); 21 | } 22 | }; 23 | 24 | template 25 | struct get_pointer> 26 | { 27 | using type = T; 28 | 29 | static T* get() 30 | { 31 | return reinterpret_cast(std::uintptr_t(1024)); 32 | } 33 | }; 34 | 35 | template 36 | void verify_variant_impl(bool is_compressed) 37 | { 38 | using variant = pointer_variant_impl; 39 | REQUIRE(typename variant::is_compressed{} == is_compressed); 40 | 41 | auto a_ptr = get_pointer::get(); 42 | using a_type = typename get_pointer::type; 43 | using a_tag_t = typename variant::template tag_of; 44 | REQUIRE(typename a_tag_t::is_valid{}); 45 | auto a_tag = a_tag_t::value; 46 | 47 | auto b_ptr = get_pointer::get(); 48 | using b_type = typename get_pointer::type; 49 | using b_tag_t = typename variant::template tag_of; 50 | REQUIRE(typename b_tag_t::is_valid{}); 51 | auto b_tag = b_tag_t::value; 52 | 53 | auto c_ptr = get_pointer::get(); 54 | using c_type = typename get_pointer::type; 55 | using c_tag_t = typename variant::template tag_of; 56 | REQUIRE(typename c_tag_t::is_valid{}); 57 | auto c_tag = c_tag_t::value; 58 | 59 | REQUIRE(a_tag != b_tag); 60 | REQUIRE(a_tag != c_tag); 61 | REQUIRE(b_tag != c_tag); 62 | 63 | auto verify_null = [&](const variant& v) { 64 | REQUIRE(!v.has_value()); 65 | REQUIRE(v.tag() != a_tag); 66 | REQUIRE(v.tag() != b_tag); 67 | REQUIRE(v.tag() != c_tag); 68 | REQUIRE(v.get() == nullptr); 69 | }; 70 | 71 | variant v(nullptr); 72 | verify_null(v); 73 | 74 | v.reset(a_ptr); 75 | REQUIRE(v.has_value()); 76 | REQUIRE(v.tag() == a_tag); 77 | REQUIRE(v.get() == a_ptr); 78 | REQUIRE(v.template pointer_to() == a_ptr); 79 | 80 | v.reset(b_ptr); 81 | REQUIRE(v.has_value()); 82 | REQUIRE(v.tag() == b_tag); 83 | REQUIRE(v.get() == b_ptr); 84 | REQUIRE(v.template pointer_to() == b_ptr); 85 | 86 | v.reset(nullptr); 87 | verify_null(v); 88 | 89 | v.reset(c_ptr); 90 | REQUIRE(v.has_value()); 91 | REQUIRE(v.tag() == c_tag); 92 | REQUIRE(v.get() == c_ptr); 93 | REQUIRE(v.template pointer_to() == c_ptr); 94 | 95 | v.reset(static_cast(nullptr)); 96 | verify_null(v); 97 | } 98 | } // namespace 99 | 100 | TEST_CASE("pointer_variant_impl") 101 | { 102 | SECTION("not compressed: normal") 103 | { 104 | verify_variant_impl(false); 105 | } 106 | SECTION("not compressed: aligned_obj") 107 | { 108 | verify_variant_impl, const char>(false); 109 | } 110 | SECTION("compressed: normal") 111 | { 112 | verify_variant_impl(true); 113 | } 114 | SECTION("compressed: aligned_obj") 115 | { 116 | verify_variant_impl, aligned_obj>(true); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/pointer_tiny_storage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | using namespace foonathan::tiny; 12 | 13 | namespace 14 | { 15 | template 16 | void verify_pointer_assignment(Storage& s, unsigned tiny_value) 17 | { 18 | alignas(16) typename Storage::value_type array[4]; 19 | 20 | s.pointer() = array; 21 | REQUIRE(s.tiny() == tiny_value); 22 | REQUIRE((s.pointer() == array)); 23 | 24 | s.pointer() += 2; 25 | REQUIRE(s.tiny() == tiny_value); 26 | REQUIRE((s.pointer() == array + 2)); 27 | 28 | s.pointer() -= 1; 29 | REQUIRE(s.tiny() == tiny_value); 30 | REQUIRE((s.pointer() == array + 1)); 31 | 32 | auto ptr = s.pointer()++; 33 | REQUIRE(s.tiny() == tiny_value); 34 | REQUIRE((s.pointer() == array + 2)); 35 | REQUIRE(ptr == array + 1); 36 | 37 | ptr = ++s.pointer(); 38 | REQUIRE(s.tiny() == tiny_value); 39 | REQUIRE((s.pointer() == array + 3)); 40 | REQUIRE(ptr == array + 3); 41 | 42 | ptr = s.pointer()--; 43 | REQUIRE(s.tiny() == tiny_value); 44 | REQUIRE((s.pointer() == array + 2)); 45 | REQUIRE(ptr == array + 3); 46 | 47 | ptr = --s.pointer(); 48 | REQUIRE(s.tiny() == tiny_value); 49 | REQUIRE((s.pointer() == array + 1)); 50 | REQUIRE(ptr == array + 1); 51 | } 52 | } // namespace 53 | 54 | TEST_CASE("pointer_tiny_storage") 55 | { 56 | SECTION("compressed") 57 | { 58 | using storage = pointer_tiny_storage>; 59 | REQUIRE(storage::is_compressed::value); 60 | 61 | storage s; 62 | REQUIRE(s.tiny() == 0); 63 | REQUIRE((s.pointer() == nullptr)); 64 | 65 | s.tiny() = 3; 66 | REQUIRE(s.tiny() == 3); 67 | REQUIRE((s.pointer() == nullptr)); 68 | 69 | const auto& cs = s; 70 | REQUIRE(cs.tiny() == 3); 71 | REQUIRE((cs.pointer() == nullptr)); 72 | 73 | verify_pointer_assignment(s, 3); 74 | } 75 | SECTION("compressed custom alignment") 76 | { 77 | using storage = pointer_tiny_storage, tiny_unsigned<3>>; 78 | REQUIRE(storage::is_compressed::value); 79 | 80 | storage s; 81 | REQUIRE(s.tiny() == 0); 82 | REQUIRE((s.pointer() == nullptr)); 83 | 84 | s.tiny() = 7; 85 | REQUIRE(s.tiny() == 7); 86 | REQUIRE((s.pointer() == nullptr)); 87 | 88 | const auto& cs = s; 89 | REQUIRE(cs.tiny() == 7); 90 | REQUIRE((cs.pointer() == nullptr)); 91 | 92 | alignas(8) int value = 0; 93 | s.pointer() = &value; 94 | REQUIRE(s.tiny() == 7); 95 | REQUIRE((s.pointer() == &value)); 96 | } 97 | SECTION("not compressed") 98 | { 99 | using storage = pointer_tiny_storage>; 100 | REQUIRE(!storage::is_compressed::value); 101 | 102 | storage s; 103 | REQUIRE(s.tiny() == 0); 104 | REQUIRE((s.pointer() == nullptr)); 105 | 106 | s.tiny() = 7; 107 | REQUIRE(s.tiny() == 7); 108 | REQUIRE((s.pointer() == nullptr)); 109 | 110 | const auto& cs = s; 111 | REQUIRE(cs.tiny() == 7); 112 | REQUIRE((cs.pointer() == nullptr)); 113 | 114 | verify_pointer_assignment(s, 7); 115 | } 116 | SECTION("big not compressed") 117 | { 118 | using storage = pointer_tiny_storage< 119 | std::uint32_t, tiny_unsigned<2 + sizeof(std::uintptr_t) * CHAR_BIT / 2, std::uint64_t>, 120 | tiny_unsigned>; 121 | REQUIRE(!storage::is_compressed::value); 122 | REQUIRE(sizeof(storage) == 2 * sizeof(std::uintptr_t)); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /example/custom_tiny_type.cpp: -------------------------------------------------------------------------------- 1 | // This example demonstrates how to write a tiny type. 2 | // Required reading: example `tiny_storage.cpp` 3 | 4 | #include 5 | 6 | #include // for `tiny::tiny_bool` 7 | #include // for `tiny::tiny_storage` 8 | #include // for writing a tiny type 9 | 10 | namespace tiny = foonathan::tiny; 11 | 12 | // The type we want to turn into a tiny type. 13 | // It is basically a bool with one additional member function, `flip()`. 14 | class my_bool 15 | { 16 | public: 17 | explicit my_bool(bool b) : impl_(b) {} 18 | 19 | explicit operator bool() const noexcept 20 | { 21 | return impl_; 22 | } 23 | 24 | void flip() noexcept 25 | { 26 | impl_ = !impl_; 27 | } 28 | 29 | private: 30 | bool impl_; 31 | }; 32 | 33 | // So we write a `tiny_my_bool` type. 34 | // This type represents a `my_bool` object that is stored in a space-efficient way. 35 | // We always need both types because `tiny_my_bool` is not a real type. 36 | struct tiny_my_bool 37 | { 38 | // The proper type we can use normally. 39 | // This is the type that we're storing in a compressed way. 40 | using object_type = my_bool; 41 | 42 | static constexpr std::size_t bit_size() noexcept 43 | { 44 | // We only need one bit to store it. 45 | return 1; 46 | } 47 | 48 | // This is the proxy that acts like a reference to the `object_type`. 49 | // `BitView` is a view to the `bit_size()`-bits that store it. 50 | template 51 | class proxy 52 | { 53 | // Always need to store that view. 54 | BitView view_; 55 | 56 | // Required: initialize the view. 57 | explicit proxy(BitView view) noexcept : view_(view) {} 58 | 59 | // Make a friend so the constructor can be used. 60 | friend tiny::tiny_type_access; 61 | 62 | public: 63 | // Required: conversion to the `object_type`. 64 | operator object_type() const noexcept 65 | { 66 | // We get an integer that contain our bits. 67 | auto bits = view_.extract(); 68 | // And then convert it to `my_bool`. 69 | return my_bool(bits != 0); 70 | } 71 | 72 | // Required: assignment from `object_type`. 73 | // Note that all functions on the proxy are `const`-qualified. 74 | // Proxies to `const` are determined by the `BitView` type. 75 | const proxy& operator=(object_type obj) const noexcept 76 | { 77 | // Just put the bits into the view. 78 | // If we have proxy to `const`, this will fail with a (nice) error message. 79 | view_.put(static_cast(obj) ? 1 : 0); 80 | return *this; 81 | } 82 | 83 | // The other functions are not required and just map to the corresponding function in the 84 | // object type. 85 | 86 | explicit operator bool() const noexcept 87 | { 88 | return static_cast(static_cast(*this)); 89 | } 90 | 91 | void flip() const noexcept 92 | { 93 | auto obj = static_cast(*this); 94 | obj.flip(); 95 | *this = obj; 96 | } 97 | }; 98 | }; 99 | 100 | void use_tiny_my_bool() 101 | { 102 | std::cout << "=== tiny_my_bool ===\n\n"; 103 | 104 | // Now we can use it just like any other tiny type. 105 | tiny::tiny_storage storage; 106 | std::cout << "My bool: " << !!storage.at<0>() << '\n'; 107 | std::cout << "Bool: " << !!storage.at<1>() << '\n'; 108 | std::cout << '\n'; 109 | 110 | storage.at<0>().flip(); 111 | storage.at<1>() = true; 112 | 113 | std::cout << "My bool: " << !!storage.at<0>() << '\n'; 114 | std::cout << "Bool: " << !!storage.at<1>() << '\n'; 115 | std::cout << '\n'; 116 | 117 | std::cout << '\n'; 118 | } 119 | 120 | //=== run all examples ===// 121 | int main() 122 | { 123 | use_tiny_my_bool(); 124 | } 125 | -------------------------------------------------------------------------------- /test/optional_impl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | using namespace foonathan::tiny; 12 | 13 | namespace 14 | { 15 | template 16 | void verify_optional_impl(const T& obj, bool is_compressed) 17 | { 18 | #if !defined(_MSC_VER) 19 | // types with deleted ctors are not trivial on MSVC 20 | REQUIRE(std::is_trivially_copyable::value 21 | == std::is_trivially_copyable>::value); 22 | #endif 23 | REQUIRE(optional_impl::is_compressed::value == is_compressed); 24 | 25 | optional_impl opt; 26 | REQUIRE(!opt.has_value()); 27 | 28 | opt.create_value(obj); 29 | REQUIRE(opt.has_value()); 30 | REQUIRE(opt.value() == obj); 31 | 32 | opt.destroy_value(); 33 | REQUIRE(!opt.has_value()); 34 | } 35 | 36 | enum class foo 37 | { 38 | a, 39 | b, 40 | c, 41 | _unsigned_count, 42 | }; 43 | } // namespace 44 | 45 | namespace foonathan 46 | { 47 | namespace tiny 48 | { 49 | template <> 50 | struct tombstone_traits : tombstone_traits> 51 | {}; 52 | } // namespace tiny 53 | } // namespace foonathan 54 | 55 | TEST_CASE("optional_impl") 56 | { 57 | SECTION("not compressed") 58 | { 59 | verify_optional_impl(std::string("hello"), false); 60 | verify_optional_impl(std::string(""), false); 61 | } 62 | SECTION("not compressed trivial") 63 | { 64 | verify_optional_impl(0, false); 65 | verify_optional_impl(42, false); 66 | verify_optional_impl(INT_MAX, false); 67 | } 68 | SECTION("compressed: bool") 69 | { 70 | verify_optional_impl(true, true); 71 | verify_optional_impl(false, true); 72 | } 73 | SECTION("compressed: enum") 74 | { 75 | verify_optional_impl(foo::a, true); 76 | verify_optional_impl(foo::b, true); 77 | verify_optional_impl(foo::c, true); 78 | } 79 | SECTION("compressed: optional optional bool") 80 | { 81 | using opt_t = optional_impl>; 82 | 83 | #if !defined(_MSC_VER) 84 | // types with deleted ctors are not trivial on MSVC 85 | REQUIRE(std::is_trivially_copyable::value); 86 | #endif 87 | REQUIRE(opt_t::is_compressed::value); 88 | 89 | opt_t opt; 90 | REQUIRE(!opt.has_value()); 91 | 92 | opt.create_value(); 93 | REQUIRE(opt.has_value()); 94 | REQUIRE(!opt.value().has_value()); 95 | 96 | opt.value().create_value(true); 97 | REQUIRE(opt.has_value()); 98 | REQUIRE(opt.value().has_value()); 99 | REQUIRE(opt.value().value() == true); 100 | 101 | opt.value().destroy_value(); 102 | REQUIRE(opt.has_value()); 103 | REQUIRE(!opt.value().has_value()); 104 | 105 | opt.value().create_value(false); 106 | REQUIRE(opt.has_value()); 107 | REQUIRE(opt.value().has_value()); 108 | REQUIRE(opt.value().value() == false); 109 | 110 | opt.destroy_value(); 111 | REQUIRE(!opt.has_value()); 112 | } 113 | SECTION("compressed: optional optional int") 114 | { 115 | using opt_t = optional_impl>; 116 | #if !defined(_MSC_VER) 117 | // types with deleted ctors are not trivial on MSVC 118 | REQUIRE(std::is_trivially_copyable::value); 119 | #endif 120 | REQUIRE(opt_t::is_compressed::value); 121 | 122 | opt_t opt; 123 | REQUIRE(!opt.has_value()); 124 | 125 | opt.create_value(); 126 | REQUIRE(opt.has_value()); 127 | REQUIRE(!opt.value().has_value()); 128 | 129 | opt.value().create_value(0); 130 | REQUIRE(opt.has_value()); 131 | REQUIRE(opt.value().has_value()); 132 | REQUIRE(opt.value().value() == 0); 133 | 134 | opt.value().destroy_value(); 135 | REQUIRE(opt.has_value()); 136 | REQUIRE(!opt.value().has_value()); 137 | 138 | opt.value().create_value(42); 139 | REQUIRE(opt.has_value()); 140 | REQUIRE(opt.value().has_value()); 141 | REQUIRE(opt.value().value() == 42); 142 | 143 | opt.destroy_value(); 144 | REQUIRE(!opt.has_value()); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /test/padding_traits.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace foonathan::tiny; 11 | 12 | TEST_CASE("aggregate_member") 13 | { 14 | struct foo 15 | { 16 | int a; 17 | const int b; 18 | char c; 19 | }; 20 | 21 | foo f{1, 2, 'a'}; 22 | const foo cf{1, 2, 'a'}; 23 | 24 | using member_a = FOONATHAN_TINY_MEMBER(foo, a); 25 | REQUIRE(std::is_same::value); 26 | REQUIRE(std::is_same::value); 27 | REQUIRE(member_a::offset() == 0); 28 | 29 | REQUIRE(member_a::get(cf) == 1); 30 | 31 | member_a::get(f) = -1; 32 | REQUIRE(member_a::get(f) == -1); 33 | 34 | using member_b = FOONATHAN_TINY_MEMBER(foo, b); 35 | REQUIRE(std::is_same::value); 36 | REQUIRE(std::is_same::value); 37 | REQUIRE(member_b::offset() == sizeof(int)); 38 | 39 | REQUIRE(member_b::get(f) == 2); 40 | REQUIRE(member_b::get(cf) == 2); 41 | 42 | using member_c = FOONATHAN_TINY_MEMBER(foo, c); 43 | REQUIRE(std::is_same::value); 44 | REQUIRE(std::is_same::value); 45 | REQUIRE(member_c::offset() == 2 * sizeof(int)); 46 | 47 | REQUIRE(member_c::get(cf) == 'a'); 48 | 49 | member_c::get(f) = 'A'; 50 | REQUIRE(member_c::get(f) == 'A'); 51 | } 52 | 53 | TEST_CASE("padding_traits_aggregate") 54 | { 55 | SECTION("no padding") 56 | { 57 | struct foo 58 | { 59 | std::uint8_t a; 60 | std::uint8_t b; 61 | }; 62 | 63 | using traits = padding_traits_aggregate; 65 | 66 | foo obj; 67 | bit_view padding 68 | = traits::padding_view(reinterpret_cast(&obj)); 69 | REQUIRE(padding.size() == 0); 70 | } 71 | SECTION("single padding") 72 | { 73 | struct foo 74 | { 75 | std::uint8_t a; 76 | std::uint16_t b; 77 | }; 78 | 79 | using traits = padding_traits_aggregate; 81 | 82 | foo obj; 83 | auto byte_ptr = reinterpret_cast(&obj); 84 | std::memset(&obj, 0, sizeof(foo)); 85 | REQUIRE(obj.a == 0); 86 | REQUIRE(obj.b == 0); 87 | 88 | bit_view padding = traits::padding_view(byte_ptr); 89 | REQUIRE(padding.size() == 8); 90 | REQUIRE(padding.extract() == 0); 91 | 92 | padding.put(~0ull); 93 | REQUIRE(obj.a == 0); 94 | REQUIRE(obj.b == 0); 95 | REQUIRE(byte_ptr[1] == 0xFF); 96 | } 97 | SECTION("complex") 98 | { 99 | struct foo 100 | { 101 | std::uint8_t a; 102 | std::uint32_t b; 103 | std::uint64_t c; 104 | std::uint8_t d; 105 | }; 106 | 107 | using traits 108 | = padding_traits_aggregate; 111 | 112 | foo obj; 113 | auto byte_ptr = reinterpret_cast(&obj); 114 | std::memset(&obj, 0, sizeof(foo)); 115 | REQUIRE(obj.a == 0); 116 | REQUIRE(obj.b == 0); 117 | REQUIRE(obj.c == 0); 118 | REQUIRE(obj.d == 0); 119 | 120 | joined_bit_view, 121 | bit_view> 122 | padding = traits::padding_view(byte_ptr); 123 | REQUIRE(padding.size() == (3 + 0 + 0 + 7) * CHAR_BIT); 124 | REQUIRE(padding.subview<0, 3 * CHAR_BIT>().extract() == 0); 125 | REQUIRE(padding.subview<3 * CHAR_BIT, last_bit>().extract() == 0); 126 | 127 | padding.subview<0, 3 * CHAR_BIT>().put(~0ull); 128 | REQUIRE(obj.a == 0); 129 | REQUIRE(obj.b == 0); 130 | REQUIRE(obj.c == 0); 131 | REQUIRE(obj.d == 0); 132 | REQUIRE(byte_ptr[1] == 0xFF); 133 | REQUIRE(byte_ptr[2] == 0xFF); 134 | REQUIRE(byte_ptr[3] == 0xFF); 135 | 136 | padding.subview<3 * CHAR_BIT, last_bit>().put(~0ull); 137 | REQUIRE(obj.a == 0); 138 | REQUIRE(obj.b == 0); 139 | REQUIRE(obj.c == 0); 140 | REQUIRE(obj.d == 0); 141 | for (auto i = 0; i != 7; ++i) 142 | REQUIRE(byte_ptr[2 + 2 * 64 / CHAR_BIT] == 0xFF); 143 | 144 | auto cpadding = traits::padding_view(const_cast(byte_ptr)); 145 | cpadding = padding; 146 | } 147 | } 148 | 149 | namespace 150 | { 151 | struct aggregate_padding 152 | { 153 | std::uint8_t a; 154 | std::uint16_t b; 155 | }; 156 | 157 | struct compatible 158 | { 159 | std::uint8_t a; 160 | std::uint16_t b; 161 | }; 162 | } // namespace 163 | 164 | namespace foonathan 165 | { 166 | namespace tiny 167 | { 168 | template <> 169 | struct padding_traits 170 | : padding_traits_aggregate 172 | {}; 173 | 174 | template <> 175 | struct padding_traits 176 | : padding_traits_layout_compatible 177 | {}; 178 | } // namespace tiny 179 | } // namespace foonathan 180 | 181 | TEST_CASE("padding_traits layout compatible") 182 | { 183 | REQUIRE(padding_bit_size() == 8); 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # foonathan/tiny 2 | 3 | ![Project Status](https://img.shields.io/endpoint?url=https%3A%2F%2Fwww.jonathanmueller.dev%2Fproject%2Ftiny%2Findex.json) 4 | [![Build Status](https://dev.azure.com/foonathan/tiny/_apis/build/status/foonathan.tiny)](https://dev.azure.com/foonathan/tiny/_build/latest?definitionId=3) 5 | 6 | > Note: This project is currently WIP, no guarantees are made until an 0.1 release. 7 | 8 | This project is a C++11 library for putting every last bit to good use. 9 | It combines various techniques such as [`llvm::PointerIntPair`](http://llvm.org/doxygen/classllvm_1_1PointerIntPair.html), [tombstones](https://youtu.be/MWBfmmg8-Yo?t=2466) and (a custom implementation of) bitfields to write types that use as little bits as possible. 10 | 11 | **Important**: This library is a low-level implementation library. 12 | It is meant for experienced C++ programmers who write foundational code, such as vocabulary types, containers etc. 13 | As such, the types used by this library should *not* appear in interfaces directly. 14 | Instead they are implementation details of other types. 15 | This is especially true for the `_impl` types such as `tiny::optional_impl`. 16 | Proper vocabulary types are meant to be built on top of them. 17 | 18 | ## Features 19 | 20 | ### Foundations 21 | 22 | * `tiny::bit_view`: A view for a range of (possibly disjoint) bits. 23 | This is a fundamental type used internally and for implementing some traits. 24 | * `tiny::enum_traits`: Traits to specify range of an `enum`. 25 | Will be automatically implemented for enumerations with members such as `unsigned_count_`, 26 | but can be specialized for own types. 27 | They are required for exposing information about your enumerations. 28 | * `tiny::padding_traits`: Traits to specify padding bytes of your type. 29 | They basically provide a `tiny::bit_view` to the bytes that are padding. 30 | `tiny::padding_traits_aggregate` provides a semi-automatic implementation for aggregate types. 31 | 32 | ### Tiny Types 33 | 34 | Tiny types are types that are just a couple of bits in size. 35 | They cannot be stored directly but instead in a storage type: 36 | 37 | * `tiny::tiny_storage`: Stores multiple tiny types tightly packed together (think bitfields). 38 | 39 | * `tiny::pointer_tiny_storage`: Stores tiny types in the alignment bits of a pointer. 40 | 41 | * `tiny::padding_tiny_storage`: Stores tiny types in the padding of another type. 42 | 43 | The tiny types provided by this library: 44 | 45 | * `tiny::tiny_bool`: a `bool` 46 | * `tiny::tiny_int`/`tiny::tiny_unsigned`: `N` bit integers (where `N` is tiny) 47 | * `tiny::tiny_int_range`: the specified integers 48 | * `tiny::tiny_enum`: a tiny enumeration 49 | * `tiny::tiny_flag_set`: a set of flags, i.e. multiple booleans with names 50 | 51 | ### Tombstones 52 | 53 | Optional implementations like `std::optional` need to have storage for `T` and a boolean indicating whether or not one is currently stored. 54 | Due to alignment and padding this can easily double the size beyond what is necessary. 55 | 56 | A *tombstone* is a special "invalid" value of a type, like a `nullptr` reference. 57 | It can be used to indicate an empty optional without needing to store a boolean. 58 | 59 | The `tiny::tombstone_traits` are a non-intrusive way of exposing tombstones without creating the ability to expose the invalid type states. 60 | 61 | ### Vocabulary Implementation Helpers 62 | 63 | A space efficient optional implementation can be built on top of the tombstone traits. 64 | However, certain design decisions of such vocabulary types are somewhat controversial. 65 | So instead of writing the full implementation, the library contains just the bare minimum. 66 | Proper vocabulary types can be built on top. 67 | 68 | Those are: 69 | 70 | * `tiny::optional_impl`: a tombstone enabled and thus compact optional 71 | * `tiny::pointer_variant_impl`: a union of multiple pointer types using alignment bits to store the currently active pointer 72 | 73 | ## FAQ 74 | 75 | **Q: Are those tricks standard conforming C++?** 76 | 77 | A: For the most part, yes: 78 | The implementations are carefully crafted to avoid undefined behavior. 79 | However, I'm certainly relying on some implementation-defined behavior. 80 | For example, the `tiny::pointer_tiny_storage` makes some assumptions about the integral representation of pointers that are not necessarily guaranteed, 81 | but all implementations I'm aware of work that way. 82 | Please let me know if yours don't! 83 | 84 | **Q: The tiny types behave weird when I use `auto`.** 85 | 86 | A: That's not a question, but yes. 87 | By their very nature I cannot expose references to a stored tiny type. 88 | Proxies are used instead, which interact poorly with `auto`. 89 | This is an implementation limit I can't really do anything about. 90 | 91 | **Q: It breaks when I do this!** 92 | 93 | A: Don't do that. And file an issue (or a PR, I have a lot of other projects...). 94 | 95 | **Q: This is awesome!** 96 | 97 | A: Thanks. I do have a Patreon page, so consider checking it out: 98 | 99 | [![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://patreon.com/foonathan) 100 | 101 | ## Documentation 102 | 103 | > A full reference documentation is WIP, look at the comments in the header files for now. 104 | 105 | Annotated tutorial style examples can be found in [the example directory](example/). 106 | 107 | ### Compiler Support 108 | 109 | The library requires a C++11 compiler. 110 | Compilers that are being tested on CI: 111 | 112 | * Linux: 113 | * GCC 5 to 8 114 | * clang 4 to 7 115 | * MacOS: 116 | * XCode 9 and 10 117 | * Windows: 118 | * Visual Studio 2017 119 | 120 | It only requires the following standard library headers: 121 | 122 | * `cstddef` and `cstdint` 123 | * `climits` and `limits` 124 | * `cstdlib` (for `std::abort`) and `cstring` (for `std::memcpy`) 125 | * `new` (for placement new only) 126 | * `type_traits` 127 | 128 | The `debug_assert` library optionally requires `cstdio` for printing messages to `stderr`. 129 | Defining `DEBUG_ASSERT_NO_STDIO` disables that. 130 | 131 | It does not use exceptions, RTTI or dynamic memory allocation. 132 | 133 | ### Installation 134 | 135 | The library is header-only and has only my [debug_assert](https://github.com/foonathan/debug_assert) library as dependency. 136 | 137 | If you use CMake, `debug_assert` will be cloned automatically if not installed on the system. 138 | You can use it with `add_subdirectory()` or install it and use `find_package(foonathan_tiny)`, 139 | then link to `foonathan::foonathan_tiny` and everything will be setup automatically. 140 | 141 | ## Planned Features 142 | 143 | * NaN floating point packing 144 | * More vocabulary type helpers 145 | -------------------------------------------------------------------------------- /include/foonathan/tiny/pointer_variant_impl.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_POINTER_VARIANT_IMPL_HPP_INCLUDED 6 | #define FOONATHAN_TINY_POINTER_VARIANT_IMPL_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace foonathan 12 | { 13 | namespace tiny 14 | { 15 | namespace detail 16 | { 17 | //=== variadic min ===// 18 | template 19 | constexpr T min(T a) 20 | { 21 | return a; 22 | } 23 | template 24 | constexpr T min(T a, T b) 25 | { 26 | return a < b ? a : b; 27 | } 28 | template 29 | constexpr Head min(Head h, Tail... tail) 30 | { 31 | return min(h, min(tail...)); 32 | } 33 | 34 | //=== storage type ===// 35 | template 36 | constexpr std::size_t min_alignment_of() noexcept 37 | { 38 | return min(alignment_of()...); 39 | } 40 | 41 | template 42 | using pointer_variant_value_type = aligned_obj()>; 43 | 44 | template 45 | using pointer_variant_tag = tiny_unsigned; 46 | 47 | template 48 | using pointer_variant_storage 49 | = pointer_tiny_storage, pointer_variant_tag>; 50 | 51 | //=== variant tag calculation ===// 52 | template 53 | struct get_pointer_tag; 54 | 55 | template 56 | struct get_pointer_tag : std::integral_constant 57 | { 58 | using is_valid = std::false_type; 59 | }; 60 | 61 | template 62 | struct get_pointer_tag : std::integral_constant 63 | { 64 | using is_valid = std::true_type; 65 | }; 66 | template 67 | struct get_pointer_tag, Tail...> 68 | : std::integral_constant 69 | { 70 | using is_valid = std::true_type; 71 | }; 72 | 73 | template 74 | struct get_pointer_tag 75 | : std::integral_constant::value> 76 | { 77 | using is_valid = typename get_pointer_tag::is_valid; 78 | }; 79 | } // namespace detail 80 | 81 | /// The storage implementation of a variant of pointer to one of the types that uses spare bits 82 | /// whenever possible. 83 | /// 84 | /// It will store a `const void*` and information about the current type. 85 | /// This is done in the spare bits of the pointer if there are enough. 86 | /// The amount of spare bits is dependent on the minimal alignment of all types, 87 | /// respecting any [tiny::aligned_obj]() to override the alignment. 88 | /// 89 | /// It is just a low-level implementation helper for an actual variant. 90 | /// A proper variant type should be built on top of it. 91 | template 92 | class pointer_variant_impl 93 | { 94 | using storage_type = detail::pointer_variant_storage; 95 | 96 | public: 97 | using is_compressed = typename storage_type::is_compressed; 98 | 99 | /// The tag of this element type. 100 | /// 101 | /// `::is_valid` is [std::true_type]() if a pointer to the element type can be stored, 102 | /// [std::false_type]() otherwise. 103 | /// `::value` returns the index of the element type in the list of pointers, 104 | /// if the element type is valid. 105 | template 106 | using tag_of = detail::get_pointer_tag; 107 | 108 | //=== constructors ===// 109 | /// \effects Same as `reset()`. 110 | /// \group ctor 111 | explicit pointer_variant_impl(std::nullptr_t) 112 | { 113 | reset(nullptr); 114 | } 115 | /// \group ctor 116 | template 117 | explicit pointer_variant_impl(T* ptr) 118 | { 119 | reset(ptr); 120 | } 121 | 122 | //=== modifiers ===// 123 | /// \effects Resets the variant to `nullptr`. 124 | void reset(std::nullptr_t) noexcept 125 | { 126 | storage_.pointer() = nullptr; 127 | } 128 | 129 | /// \effects Resets the variant to a pointer to the given object. 130 | /// \notes The pointer may be `nullptr`. 131 | template 132 | void reset(T* ptr) noexcept 133 | { 134 | static_assert(typename tag_of::is_valid{}, "type cannot be stored in variant"); 135 | 136 | storage_.pointer() = ptr; 137 | storage_.tiny() = tag_of::value; 138 | } 139 | 140 | //=== accessors ===// 141 | /// \returns Whether or not the variant currently points to an object, 142 | /// i.e. it is not `nullptr`. 143 | bool has_value() const noexcept 144 | { 145 | return storage_.pointer() != nullptr; 146 | } 147 | 148 | /// \returns The tag value of the currently active element type, 149 | /// or an invalid tag value if there is none. 150 | std::size_t tag() const noexcept 151 | { 152 | if (has_value()) 153 | return storage_.tiny(); 154 | else 155 | return std::size_t(-1); 156 | } 157 | 158 | /// \returns The untyped pointer. 159 | const void* get() const noexcept 160 | { 161 | return storage_.pointer(); 162 | } 163 | 164 | /// \returns The typed pointer to `T`. 165 | /// \requires It must contain a non-null pointer to the given type. 166 | template 167 | T* pointer_to() const noexcept 168 | { 169 | using tag_of_t = tag_of; 170 | static_assert(tag_of_t::is_valid::value, "type cannot be stored in variant"); 171 | DEBUG_ASSERT(tag() == tag_of_t::value, detail::precondition_handler{}); 172 | // C style cast because we might need to cast away const-ness 173 | return (T*)(get()); 174 | } 175 | 176 | private: 177 | storage_type storage_; 178 | }; 179 | } // namespace tiny 180 | } // namespace foonathan 181 | 182 | #endif // FOONATHAN_TINY_POINTER_VARIANT_IMPL_HPP_INCLUDED 183 | -------------------------------------------------------------------------------- /example/tombstone.cpp: -------------------------------------------------------------------------------- 1 | // This example shows how to define tombstones for your type. 2 | 3 | #include 4 | #include 5 | 6 | #include // for `tiny::check_size` 7 | #include // for `tiny::optional_impl` 8 | #include // for `tiny::tombstone_traits` 9 | 10 | namespace tiny = foonathan::tiny; 11 | 12 | // Custom type: a pointer to `T` that is never null. 13 | template 14 | class non_null_ptr 15 | { 16 | T* ptr_; 17 | 18 | public: 19 | explicit non_null_ptr(T* ptr) : ptr_(ptr) 20 | { 21 | assert(ptr); 22 | } 23 | 24 | T& operator*() const noexcept 25 | { 26 | return *ptr_; 27 | } 28 | 29 | T* operator->() const noexcept 30 | { 31 | return ptr_; 32 | } 33 | 34 | T* get() const noexcept 35 | { 36 | return ptr_; 37 | } 38 | }; 39 | 40 | // To create tombstones we need to specialize the tombstone traits. 41 | // Here we want to just have a single tombstone, `nullptr`. 42 | // An optimized implementation would also leverage the alignment bits. 43 | namespace foonathan 44 | { 45 | namespace tiny 46 | { 47 | #ifdef TOMBSTONE_MANUAL 48 | template 49 | struct tombstone_traits> 50 | { 51 | // This is the type that is being stored conceptually. 52 | // It is usually the type of the specialization, but for a tiny type it is the actual object 53 | // type, for example. 54 | using object_type = non_null_ptr; 55 | 56 | // The type that can store the object type or a tombstone. 57 | // Here we have a union of `non_null_ptr` and `T*`. 58 | // As the two types are layout compatible, we can do type punning through the union. 59 | // 60 | // This type is never copied but must be nothrow default constructible and trivially 61 | // destructible. 62 | union storage_type 63 | { 64 | non_null_ptr object; 65 | T* tombstone; 66 | 67 | // default constructor is required here 68 | // doesn't actually need to initialize anything 69 | storage_type() : tombstone() {} 70 | }; 71 | 72 | // A reference or proxy to the stored object type. 73 | // Here we can use a reference. 74 | // 75 | // Be careful when you are not writing a reference, 76 | // as this is then propagated until an optional implementation, for example. 77 | // If you're not careful, this can create a `vector` situation in generic code. 78 | // 79 | // The provided specialization of the tombstone traits will have actual references here, 80 | // except for the tiny types. 81 | // But those are weird in generic code anyways. 82 | using reference = non_null_ptr&; 83 | using const_reference = const non_null_ptr&; 84 | 85 | // The number of tombstones, here it is just one. 86 | static constexpr std::size_t tombstone_count = 1; 87 | 88 | // This function creates the tombstone with the specified index. 89 | // It is only called if nothing is stored in the storage. 90 | static void create_tombstone(storage_type& storage, std::size_t index) noexcept 91 | { 92 | // We have only one tombstone, so we can ignore the index. 93 | assert(index == 0); 94 | // Our only tombstone is `nullptr` 95 | storage.tombstone = nullptr; 96 | } 97 | 98 | // This function creates an object. 99 | // It is only called if nothing is stored in the storage. 100 | // 101 | // In reality it would be template forwarding a variadic number of arguments. 102 | // But we know all possible arguments here. 103 | static void create_object(storage_type& storage, T* arg) 104 | { 105 | // For types that aren't trivially copy constructible, this might involve placement new. 106 | storage.object = non_null_ptr(arg); 107 | } 108 | 109 | // This function destroys an object. 110 | // There is no `destroy_tombstone()` as tombstones must be trivial types. 111 | static void destroy_object(storage_type& storage) noexcept 112 | { 113 | // The object type is trivial, so no need to do anything. 114 | (void)storage; 115 | } 116 | 117 | // This function returns the index of the tombstone, 118 | // or an out of bounds index if it stores an object. 119 | static std::size_t get_tombstone(const storage_type& storage) noexcept 120 | { 121 | // Type punning here is legal, so just check whether we have `nullptr`. 122 | if (storage.tombstone == nullptr) 123 | return 0; // Tombstone with index 0 (and only tombstone). 124 | else 125 | return 1; // No tombstone, return an out-of-bounds index. 126 | } 127 | 128 | // These functions return the object that is being stored. 129 | static reference get_object(storage_type& storage) noexcept 130 | { 131 | return storage.object; 132 | } 133 | static const_reference get_object(const storage_type& storage) noexcept 134 | { 135 | return storage.object; 136 | } 137 | }; 138 | #else 139 | // The pattern where you have a custom type that can store the object plus tombstone and you do 140 | // type punning is common, so the boilerplate can be eliminated. 141 | template 142 | struct tombstone_traits> 143 | // Inherit from `tiny::tombstone_traits_simple`, 144 | // the second type is the `TombstoneType` — the trivial type that is type punned. 145 | : tiny::tombstone_traits_simple, T*> 146 | { 147 | // Still one tombstone. 148 | static constexpr std::size_t tombstone_count = 1; 149 | 150 | // This function creates the specified `TombstoneType` object. 151 | static void create_tombstone_impl(void* memory, std::size_t index) noexcept 152 | { 153 | // Again, we can ignore the index. 154 | assert(index == 0); 155 | ::new (memory) T*(nullptr); 156 | } 157 | 158 | // This function returns the index by passing it the `TombstoneType` object. 159 | static std::size_t get_tombstone_impl(T* tombstone) noexcept 160 | { 161 | // Same implementation as before. 162 | return tombstone == nullptr ? 0 : 1; 163 | } 164 | }; 165 | #endif 166 | } // namespace tiny 167 | } // namespace foonathan 168 | 169 | int main() 170 | { 171 | // Just using `tiny::optional_impl` for simplicity here, 172 | // the type isn't meant to be used directly! 173 | using optional = tiny::optional_impl>; 174 | static_assert(tiny::check_size(), 175 | "no additional storage due to the tombstone"); 176 | 177 | optional opt; 178 | std::cout << "optional is " << (opt.has_value() ? "not empty" : "empty") << ".\n"; 179 | std::cout << '\n'; 180 | 181 | int i; 182 | opt.create_value(&i); 183 | std::cout << "optional is " << (opt.has_value() ? "not empty" : "empty") << ".\n"; 184 | std::cout << "pointer is " << opt.value().get() << '\n'; 185 | } 186 | -------------------------------------------------------------------------------- /test/padding_tiny_storage.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | using namespace foonathan::tiny; 12 | 13 | namespace 14 | { 15 | struct no_padding 16 | { 17 | std::uint8_t a; 18 | std::uint8_t b; 19 | }; 20 | 21 | struct small_padding 22 | { 23 | std::uint8_t a; 24 | std::uint16_t b; 25 | }; 26 | 27 | struct big_padding 28 | { 29 | std::uint8_t a; 30 | std::uint64_t b; 31 | }; 32 | 33 | std::size_t object_count = 0; 34 | 35 | struct non_trivial_padding 36 | { 37 | std::uint8_t a; 38 | std::uint16_t b; 39 | 40 | non_trivial_padding(std::uint8_t value) : a(value), b(value) 41 | { 42 | ++object_count; 43 | } 44 | 45 | non_trivial_padding(const non_trivial_padding& other) : a(other.a), b(other.b) 46 | { 47 | ++object_count; 48 | } 49 | 50 | ~non_trivial_padding() 51 | { 52 | --object_count; 53 | } 54 | }; 55 | } // namespace 56 | 57 | namespace foonathan 58 | { 59 | namespace tiny 60 | { 61 | template <> 62 | struct padding_traits 63 | : padding_traits_aggregate 65 | {}; 66 | 67 | template <> 68 | struct padding_traits 69 | : padding_traits_aggregate 71 | {}; 72 | 73 | template <> 74 | struct padding_traits 75 | : padding_traits_aggregate 77 | {}; 78 | } // namespace tiny 79 | } // namespace foonathan 80 | 81 | namespace 82 | { 83 | template 84 | void verify_big_five(const Storage& s) 85 | { 86 | Storage copy(s); 87 | REQUIRE(copy.object().a == s.object().a); 88 | REQUIRE(copy.object().b == s.object().b); 89 | REQUIRE(copy.tiny() == s.tiny()); 90 | 91 | Storage move(std::move(copy)); 92 | REQUIRE(move.object().a == s.object().a); 93 | REQUIRE(move.object().b == s.object().b); 94 | REQUIRE(move.tiny() == s.tiny()); 95 | 96 | // move assign 97 | copy = Storage(s.object(), s.tiny() / 2); 98 | REQUIRE(copy.object().a == s.object().a); 99 | REQUIRE(copy.object().b == s.object().b); 100 | REQUIRE(copy.tiny() == s.tiny() / 2); 101 | 102 | // copy assign 103 | move = copy; 104 | REQUIRE(move.object().a == s.object().a); 105 | REQUIRE(move.object().b == s.object().b); 106 | REQUIRE(move.tiny() == s.tiny() / 2); 107 | } 108 | } // namespace 109 | 110 | TEST_CASE("padding_tiny_storage") 111 | { 112 | SECTION("no padding") 113 | { 114 | using storage = padding_tiny_storage>; 115 | REQUIRE(!storage::is_compressed::value); 116 | 117 | storage s({0, 0}); 118 | REQUIRE(s.object().a == 0); 119 | REQUIRE(s.object().b == 0); 120 | REQUIRE(s.tiny() == 0); 121 | 122 | s.object().a = 42; 123 | REQUIRE(s.object().a == 42); 124 | REQUIRE(s.object().b == 0); 125 | REQUIRE(s.tiny() == 0); 126 | 127 | s.tiny() = 15; 128 | REQUIRE(s.object().a == 42); 129 | REQUIRE(s.object().b == 0); 130 | REQUIRE(s.tiny() == 15); 131 | 132 | s.object().b = 43; 133 | REQUIRE(s.object().a == 42); 134 | REQUIRE(s.object().b == 43); 135 | REQUIRE(s.tiny() == 15); 136 | 137 | verify_big_five(s); 138 | } 139 | SECTION("small padding") 140 | { 141 | using storage = padding_tiny_storage>; 142 | REQUIRE(storage::is_compressed::value); 143 | 144 | storage s({0, 0}); 145 | REQUIRE(s.object().a == 0); 146 | REQUIRE(s.object().b == 0); 147 | REQUIRE(s.tiny() == 0); 148 | 149 | s.object().a = 42; 150 | REQUIRE(s.object().a == 42); 151 | REQUIRE(s.object().b == 0); 152 | REQUIRE(s.tiny() == 0); 153 | 154 | s.tiny() = 15; 155 | REQUIRE(s.object().a == 42); 156 | REQUIRE(s.object().b == 0); 157 | REQUIRE(s.tiny() == 15); 158 | 159 | s.object().b = 43; 160 | REQUIRE(s.object().a == 42); 161 | REQUIRE(s.object().b == 43); 162 | REQUIRE(s.tiny() == 15); 163 | 164 | verify_big_five(s); 165 | } 166 | SECTION("big padding") 167 | { 168 | using storage 169 | = padding_tiny_storage, tiny_unsigned<48, std::uint64_t>>; 170 | REQUIRE(storage::is_compressed::value); 171 | 172 | storage s({0, 0}); 173 | REQUIRE(s.object().a == 0); 174 | REQUIRE(s.object().b == 0); 175 | REQUIRE(s.at<0>() == 0); 176 | REQUIRE(s.at<1>() == 0); 177 | 178 | s.object().a = 42; 179 | REQUIRE(s.object().a == 42); 180 | REQUIRE(s.object().b == 0); 181 | REQUIRE(s.at<0>() == 0); 182 | REQUIRE(s.at<1>() == 0); 183 | 184 | s.at<0>() = 255; 185 | REQUIRE(s.object().a == 42); 186 | REQUIRE(s.object().b == 0); 187 | REQUIRE(s.at<0>() == 255); 188 | REQUIRE(s.at<1>() == 0); 189 | 190 | s.object().b = 43; 191 | REQUIRE(s.object().a == 42); 192 | REQUIRE(s.object().b == 43); 193 | REQUIRE(s.at<0>() == 255); 194 | REQUIRE(s.at<1>() == 0); 195 | 196 | s.at<1>() = (1ull << 48) - 1; 197 | REQUIRE(s.object().a == 42); 198 | REQUIRE(s.object().b == 43); 199 | REQUIRE(s.at<0>() == 255); 200 | REQUIRE(s.at<1>() == (1ull << 48) - 1); 201 | } 202 | SECTION("non trivial padding") 203 | { 204 | using storage = padding_tiny_storage>; 205 | REQUIRE(storage::is_compressed::value); 206 | 207 | storage s(0); 208 | REQUIRE(s.object().a == 0); 209 | REQUIRE(s.object().b == 0); 210 | REQUIRE(s.tiny() == 0); 211 | 212 | s.object().a = 42; 213 | REQUIRE(s.object().a == 42); 214 | REQUIRE(s.object().b == 0); 215 | REQUIRE(s.tiny() == 0); 216 | 217 | s.tiny() = 15; 218 | REQUIRE(s.object().a == 42); 219 | REQUIRE(s.object().b == 0); 220 | REQUIRE(s.tiny() == 15); 221 | 222 | s.object().b = 43; 223 | REQUIRE(s.object().a == 42); 224 | REQUIRE(s.object().b == 43); 225 | REQUIRE(s.tiny() == 15); 226 | 227 | verify_big_five(s); 228 | } 229 | SECTION("std::string") 230 | { 231 | using storage = padding_tiny_storage>; 232 | REQUIRE(!storage::is_compressed::value); 233 | 234 | storage s("hello!"); 235 | REQUIRE(s.object() == "hello!"); 236 | REQUIRE(s.tiny() == 0); 237 | 238 | s.object().pop_back(); 239 | REQUIRE(s.object() == "hello"); 240 | REQUIRE(s.tiny() == 0); 241 | 242 | s.tiny() = 15; 243 | REQUIRE(s.object() == "hello"); 244 | REQUIRE(s.tiny() == 15); 245 | } 246 | REQUIRE(object_count == 0); 247 | } 248 | -------------------------------------------------------------------------------- /include/foonathan/tiny/enum_traits.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_ENUM_TRAITS_HPP_INCLUDED 6 | #define FOONATHAN_TINY_ENUM_TRAITS_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | namespace foonathan 14 | { 15 | namespace tiny 16 | { 17 | //=== enum traits ===// 18 | /// The default enum traits implementation. 19 | template 20 | class enum_traits_default 21 | { 22 | static_assert(std::is_enum::value, "not an enum"); 23 | using underlying_type = typename std::underlying_type::type; 24 | 25 | public: 26 | /// The type of the enum. 27 | using enum_type = Enum; 28 | 29 | /// Whether or not the traits are specialized for the enum. 30 | /// 31 | /// If this is `false`, no real information is available. 32 | /// Set to `true` in all specializations. 33 | static constexpr auto is_specialized = false; 34 | 35 | /// The first (numerically minimal) valid enum value. 36 | static constexpr Enum min() noexcept 37 | { 38 | return Enum(std::numeric_limits::min()); 39 | } 40 | 41 | /// The last (numerically maximal) valid enum value. 42 | static constexpr Enum max() noexcept 43 | { 44 | return Enum(std::numeric_limits::max()); 45 | } 46 | 47 | /// Whether or not all enum values are contiguously. 48 | static constexpr auto is_contiguous = false; 49 | }; 50 | 51 | /// Enum traits implementation that assumes the enum values are contiguous in the range `[0, 52 | /// MaxValue]`. 53 | template 54 | struct enum_traits_unsigned 55 | { 56 | static_assert(std::is_enum::value, "not an enum"); 57 | 58 | using enum_type = Enum; 59 | 60 | static constexpr auto is_specialized = true; 61 | 62 | static constexpr Enum min() noexcept 63 | { 64 | return Enum(0); 65 | } 66 | 67 | static constexpr Enum max() noexcept 68 | { 69 | return MaxValue; 70 | } 71 | 72 | static constexpr auto is_contiguous = true; 73 | }; 74 | 75 | /// Enum traits implementation that assumes the enum values are contiguous in the range `[0, 76 | /// Count]`. 77 | template 78 | struct enum_traits_unsigned_count 79 | : enum_traits_unsigned::type>(Count) - 1)> 81 | {}; 82 | 83 | /// Enum traits implementation that assumes the enum values are contiguous in the range 84 | /// `[MinValue, MaxValue]`. 85 | template 86 | struct enum_traits_signed 87 | { 88 | static_assert(std::is_enum::value, "not an enum"); 89 | 90 | using enum_type = Enum; 91 | 92 | static constexpr auto is_specialized = true; 93 | 94 | static constexpr Enum min() noexcept 95 | { 96 | return MinValue; 97 | } 98 | static constexpr Enum max() noexcept 99 | { 100 | return MaxValue; 101 | } 102 | 103 | static constexpr auto is_contiguous = true; 104 | }; 105 | 106 | namespace enum_traits_detail 107 | { 108 | template 109 | struct tag 110 | {}; 111 | 112 | struct overload 113 | { 114 | template 115 | operator tag() const noexcept 116 | { 117 | return {}; 118 | } 119 | }; 120 | 121 | template 122 | enum_traits_default enum_traits_for(...); 123 | 124 | template 125 | enum_traits_unsigned_count enum_traits_for(tag<0>); 126 | template 127 | enum_traits_unsigned_count enum_traits_for(tag<1>); 128 | 129 | template 130 | enum_traits_unsigned enum_traits_for(tag<2>); 131 | template 132 | enum_traits_unsigned enum_traits_for(tag<3>); 133 | 134 | template 135 | enum_traits_unsigned_count enum_traits_for(tag<0>); 136 | template 137 | enum_traits_unsigned_count enum_traits_for(tag<1>); 138 | } // namespace enum_traits_detail 139 | 140 | /// The traits of an enum. 141 | /// 142 | /// Specialize it for your own types, 143 | /// either by manually specifying the required members or by inheriting from the provided 144 | /// implementations. 145 | /// 146 | /// Enums with an enumerator `_unsigned_count` or `unsigned_count_` will be treated as unsigned 147 | /// enums with the specified count. Enums with an enumerator `_unsigned_max` or `unsigned_max_` 148 | /// will be treated as unsigned enums with the specified max value. Enums with an enumerator 149 | /// `_flag_count` or `flag_count_` will be treated as unsigned enums with the specified count. 150 | /// They are meant for [tiny::flag_set](). 151 | template 152 | struct enum_traits 153 | : decltype(enum_traits_detail::enum_traits_for(enum_traits_detail::overload{})) 154 | {}; 155 | 156 | //=== enum traits algorithm ===// 157 | /// \exclude 158 | namespace enum_traits_detail 159 | { 160 | template 161 | struct traits_of_impl; 162 | 163 | template 164 | struct traits_of_impl 165 | { 166 | using type = enum_traits; 167 | }; 168 | 169 | template 170 | struct traits_of_impl 171 | { 172 | using type = Traits; 173 | }; 174 | } // namespace enum_traits_detail 175 | 176 | /// If `EnumOrTraits` is an enum type, equivalent to `enum_traits`. 177 | /// Otherwise, expands to `EnumOrTraits` and assumes it has the same members as the 178 | /// `enum_traits`. 179 | template 180 | using traits_of_enum = 181 | typename enum_traits_detail::traits_of_impl::value, 182 | EnumOrTraits>::type; 183 | 184 | /// \returns The size of an enum, that is the number of valid enum values. 185 | /// \requires The enum must be contiguous. 186 | template 187 | constexpr std::size_t enum_size() noexcept 188 | { 189 | using traits = traits_of_enum; 190 | static_assert(traits::is_contiguous, "enum must be contiguous"); 191 | return std::size_t(traits::max()) - std::size_t(traits::min()) + 1; 192 | } 193 | 194 | /// \returns The number of bits required to store a valid enum value. 195 | /// \requires The enum must be contiguous. 196 | template 197 | constexpr std::size_t enum_bit_size() noexcept 198 | { 199 | return detail::ilog2_ceil(enum_size()); 200 | } 201 | 202 | /// \returns Whether or not the given enum value is a valid value. 203 | /// \requires The enum must be contiguous. 204 | /// \group is_valid_enum_value 205 | template 206 | constexpr bool is_valid_enum_value(Enum value) noexcept 207 | { 208 | return is_valid_enum_value>(value); 209 | } 210 | /// \group is_valid_enum_value 211 | template 212 | constexpr bool is_valid_enum_value(Enum value) noexcept 213 | { 214 | static_assert(Traits::is_contiguous, "enum must be contiguous"); 215 | using underlying = typename std::underlying_type::type; 216 | return underlying(Traits::min()) <= underlying(value) 217 | && underlying(value) <= underlying(Traits::max()); 218 | } 219 | } // namespace tiny 220 | } // namespace foonathan 221 | 222 | #endif // FOONATHAN_TINY_ENUM_TRAITS_HPP_INCLUDED 223 | -------------------------------------------------------------------------------- /test/tombstone_traits.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace foonathan::tiny; 14 | 15 | namespace 16 | { 17 | template 18 | struct storage 19 | { 20 | using traits = tombstone_traits; 21 | typename traits::storage_type storage; 22 | 23 | template 24 | typename traits::reference create_object(Args&&... args) 25 | { 26 | traits::create_object(storage, std::forward(args)...); 27 | return traits::get_object(storage); 28 | } 29 | 30 | void destroy_object() 31 | { 32 | traits::destroy_object(storage); 33 | } 34 | 35 | void create_tombstone(std::size_t index) 36 | { 37 | traits::create_tombstone(storage, index); 38 | } 39 | 40 | std::size_t tombstone_count() 41 | { 42 | return traits::tombstone_count; 43 | } 44 | 45 | std::size_t tombstone() 46 | { 47 | return traits::get_tombstone(storage); 48 | } 49 | }; 50 | 51 | template 52 | void verify_object(const typename tombstone_traits::object_type& obj) 53 | { 54 | storage s; 55 | auto&& ref = s.create_object(obj); 56 | REQUIRE(ref == obj); 57 | REQUIRE(s.tombstone() >= s.tombstone_count()); 58 | } 59 | 60 | template 61 | void verify_object(const T& obj) 62 | { 63 | verify_object(obj); 64 | } 65 | 66 | template 67 | void verify_tombstones(std::size_t tc) 68 | { 69 | storage s; 70 | REQUIRE(s.tombstone_count() == tc); 71 | 72 | for (auto i = 0u; i != s.tombstone_count(); ++i) 73 | { 74 | s.create_tombstone(i); 75 | REQUIRE(s.tombstone() == i); 76 | } 77 | } 78 | } // namespace 79 | 80 | TEST_CASE("tombstone_traits default") 81 | { 82 | verify_tombstones(0); 83 | verify_object(std::string("Hello World!")); 84 | verify_object(std::string()); 85 | } 86 | 87 | namespace 88 | { 89 | struct non_null_ptr 90 | { 91 | int* ptr; 92 | 93 | friend bool operator==(non_null_ptr a, non_null_ptr b) noexcept 94 | { 95 | return a.ptr == b.ptr; 96 | } 97 | }; 98 | } // namespace 99 | 100 | namespace foonathan 101 | { 102 | namespace tiny 103 | { 104 | template <> 105 | struct tombstone_traits : tombstone_traits_simple 106 | { 107 | static constexpr std::size_t tombstone_count = 1; 108 | 109 | static void create_tombstone_impl(void* memory, std::size_t) noexcept 110 | { 111 | ::new (memory) int*(nullptr); 112 | } 113 | 114 | static std::size_t get_tombstone_impl(int* ptr) noexcept 115 | { 116 | return ptr == nullptr ? 0 : 1; 117 | } 118 | }; 119 | } // namespace tiny 120 | } // namespace foonathan 121 | 122 | TEST_CASE("tombstone_traits_simple") 123 | { 124 | verify_tombstones(1); 125 | 126 | int i; 127 | verify_object(non_null_ptr{&i}); 128 | } 129 | 130 | namespace 131 | { 132 | struct padded_and_layout 133 | { 134 | std::uint8_t a; 135 | std::uint16_t b; 136 | 137 | friend bool operator==(padded_and_layout lhs, padded_and_layout rhs) 138 | { 139 | return lhs.a == rhs.a && lhs.b == rhs.b; 140 | } 141 | }; 142 | 143 | struct only_padded 144 | { 145 | std::uint8_t a; 146 | std::uint16_t b; 147 | 148 | // no default ctor 149 | explicit only_padded(std::uint8_t a, std::uint16_t b) : a(a), b(b) {} 150 | 151 | // not trivial 152 | only_padded(const only_padded& other) : a(other.a), b(other.b) {} 153 | 154 | friend bool operator==(only_padded lhs, only_padded rhs) 155 | { 156 | return lhs.a == rhs.a && lhs.b == rhs.b; 157 | } 158 | }; 159 | } // namespace 160 | 161 | namespace foonathan 162 | { 163 | namespace tiny 164 | { 165 | template <> 166 | struct padding_traits 167 | : padding_traits_aggregate 169 | {}; 170 | 171 | template <> 172 | struct tombstone_traits : tombstone_traits_padded 173 | {}; 174 | } // namespace tiny 175 | } // namespace foonathan 176 | 177 | TEST_CASE("tombstone_traits padded") 178 | { 179 | SECTION("automatic") 180 | { 181 | verify_tombstones(255); 182 | verify_object(padded_and_layout{0, 0}); 183 | verify_object(padded_and_layout{255, 1642}); 184 | } 185 | SECTION("manual") 186 | { 187 | verify_tombstones(255); 188 | verify_object(only_padded{0, 0}); 189 | verify_object(only_padded{255, 1642}); 190 | } 191 | } 192 | 193 | TEST_CASE("tombstone_traits tiny") 194 | { 195 | SECTION("bool") 196 | { 197 | verify_tombstones(127); 198 | verify_object(true); 199 | verify_object(false); 200 | } 201 | SECTION("tiny_int") 202 | { 203 | verify_tombstones>(15); 204 | for (auto i = 0u; i != 16; ++i) 205 | verify_object>(i); 206 | } 207 | SECTION("enum") 208 | { 209 | enum class foo 210 | { 211 | a, 212 | b, 213 | c, 214 | unsigned_count_, 215 | }; 216 | 217 | verify_tombstones>(63); 218 | verify_object>(foo::a); 219 | verify_object>(foo::b); 220 | verify_object>(foo::c); 221 | } 222 | } 223 | 224 | TEST_CASE("tombstone_traits optional_impl") 225 | { 226 | SECTION("not compressed") 227 | { 228 | using opt_t = optional_impl; 229 | verify_tombstones(tombstone_traits::tombstone_count); 230 | 231 | storage s; 232 | auto& ref = s.create_object(); 233 | REQUIRE(s.tombstone() >= s.tombstone_count()); 234 | REQUIRE(!ref.has_value()); 235 | 236 | ref.create_value("hello world!"); 237 | REQUIRE(s.tombstone() >= s.tombstone_count()); 238 | REQUIRE(ref.has_value()); 239 | REQUIRE(ref.value() == "hello world!"); 240 | 241 | ref.destroy_value(); 242 | REQUIRE(s.tombstone() >= s.tombstone_count()); 243 | REQUIRE(!ref.has_value()); 244 | } 245 | SECTION("compressed") 246 | { 247 | using opt_t = optional_impl; 248 | verify_tombstones(tombstone_traits::tombstone_count - 1); 249 | 250 | storage s; 251 | auto& ref = s.create_object(); 252 | REQUIRE(s.tombstone() >= s.tombstone_count()); 253 | REQUIRE(!ref.has_value()); 254 | 255 | ref.create_value(true); 256 | REQUIRE(s.tombstone() >= s.tombstone_count()); 257 | REQUIRE(ref.has_value()); 258 | REQUIRE(ref.value() == true); 259 | 260 | ref.destroy_value(); 261 | REQUIRE(s.tombstone() >= s.tombstone_count()); 262 | REQUIRE(!ref.has_value()); 263 | } 264 | } 265 | 266 | TEST_CASE("tombstone_traits pointer") 267 | { 268 | SECTION("alignment == 1") 269 | { 270 | verify_tombstones(0); 271 | verify_object("hello world!"); 272 | verify_object(nullptr); 273 | } 274 | SECTION("alignment == 2") 275 | { 276 | verify_tombstones(1); 277 | 278 | std::uint16_t obj; 279 | verify_object(&obj); 280 | verify_object(static_cast(nullptr)); 281 | } 282 | SECTION("alignment == 4") 283 | { 284 | verify_tombstones(3); 285 | 286 | std::uint32_t obj; 287 | verify_object(&obj); 288 | verify_object(static_cast(nullptr)); 289 | } 290 | SECTION("overriden alignment") 291 | { 292 | verify_tombstones*>(7); 293 | 294 | alignas(8) char obj; 295 | verify_object*>(&obj); 296 | verify_object*>(nullptr); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /include/foonathan/tiny/tiny_flag_set.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TINY_FLAG_SET_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TINY_FLAG_SET_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace foonathan 13 | { 14 | namespace tiny 15 | { 16 | namespace detail 17 | { 18 | template 19 | constexpr std::size_t flag_bit_size() noexcept 20 | { 21 | return enum_size(); 22 | } 23 | 24 | template 25 | using flag_storage_type = detail::uint_least_n_t()>; 26 | 27 | template 28 | constexpr flag_storage_type as_flag(Enum e) noexcept 29 | { 30 | return flag_storage_type( 31 | 1ull << static_cast::type>(e)); 32 | } 33 | 34 | template 35 | detail::flag_storage_type combine_flags(Flags... flags) noexcept 36 | { 37 | flag_storage_type storage = 0; 38 | bool for_each[] 39 | = {(storage = flag_storage_type(storage | as_flag(flags)), true)..., true}; 40 | (void)for_each; 41 | return storage; 42 | } 43 | } // namespace detail 44 | 45 | /// Stores a combination of flags. 46 | /// 47 | /// This type is the `object_type` of [tiny::tiny_flag_set](), 48 | /// which means a tiny flag set can be constructed giving it an initial combination of flags. 49 | template 50 | class flag_combo 51 | { 52 | flag_combo(detail::flag_storage_type flags) noexcept : flags_(flags) {} 53 | 54 | detail::flag_storage_type flags_; 55 | 56 | template 57 | friend flag_combo flags(Flags... flags) noexcept; 58 | template 59 | friend flag_combo flags(FirstFlag first, OtherFlags... other) noexcept; 60 | template 61 | friend class tiny_flag_set; 62 | }; 63 | 64 | /// \returns A combination of the specified flags. 65 | /// \group flags 66 | template 67 | flag_combo flags(Flags... flags) noexcept 68 | { 69 | return detail::combine_flags(flags...); 70 | } 71 | /// \group flags 72 | template 73 | flag_combo flags(FirstFlag first, OtherFlags... other) noexcept 74 | { 75 | return detail::combine_flags(first, other...); 76 | } 77 | 78 | /// A tiny set of flags. 79 | /// 80 | /// The `Enum` must be an `unsigned` contiguous enum, i.e. it must specialize the 81 | /// [tiny::enum_traits]() appropriately. Each enum value corresponds to one flag that can either 82 | /// be set or not set. 83 | /// 84 | /// \notes Add a final `flag_count_` or `_count_flag` enum value to get a traits specialization 85 | /// automatically. 86 | /// 87 | /// \notes The enum values must be contigous integers and not powers of two like 88 | /// when you do a flag set manually. 89 | template 90 | class tiny_flag_set 91 | { 92 | using traits = enum_traits; 93 | using enum_type = typename traits::enum_type; 94 | 95 | static_assert(traits::is_contiguous, "flags must be contiguous"); 96 | static_assert(traits::min() == enum_type(0), "flags must be unsigned"); 97 | 98 | static std::size_t get_flag_index(Enum e) noexcept 99 | { 100 | return static_cast(e); 101 | } 102 | 103 | static std::uintmax_t get_flags(flag_combo combo) noexcept 104 | { 105 | return combo.flags_; 106 | } 107 | 108 | public: 109 | using object_type = flag_combo; 110 | 111 | static constexpr std::size_t bit_size() noexcept 112 | { 113 | return detail::flag_bit_size(); 114 | } 115 | 116 | template 117 | class proxy 118 | { 119 | public: 120 | /// \effects Assigns the same flags as in the combination. 121 | const proxy& operator=(flag_combo combo) const noexcept 122 | { 123 | view_.put(get_flags(combo)); 124 | return *this; 125 | } 126 | 127 | /// \returns A proxy acting like a boolean reference to the state of the specified flag. 128 | auto operator[](Enum flag) const noexcept 129 | -> decltype(std::declval()[get_flag_index(flag)]) 130 | { 131 | return view_[get_flag_index(flag)]; 132 | } 133 | 134 | /// \returns An integer where the `i`th bit is set if the `i`th flag is set. 135 | std::uintmax_t get() const noexcept 136 | { 137 | return view_.extract(); 138 | } 139 | 140 | //=== single flag operation ===// 141 | /// \returns Whether or not the specified flag is set. 142 | bool is_set(Enum flag) const noexcept 143 | { 144 | return !!view_[get_flag_index(flag)]; 145 | } 146 | 147 | /// \effects Sets the specified flag to `value`. 148 | void set(Enum flag, bool value) const noexcept 149 | { 150 | view_[get_flag_index(flag)] = value; 151 | } 152 | 153 | /// \effects Sets the specified flag to `true`. 154 | void set(Enum flag) const noexcept 155 | { 156 | set(flag, true); 157 | } 158 | 159 | /// \effects Sets the specified flag to `false`. 160 | void reset(Enum flag) const noexcept 161 | { 162 | set(flag, false); 163 | } 164 | 165 | /// \effects Toggles the specified flag. 166 | void toggle(Enum flag) const noexcept 167 | { 168 | set(flag, !is_set(flag)); 169 | } 170 | 171 | //=== multi flag operations ===// 172 | /// \returns Whether or not any flag is set. 173 | bool any() const noexcept 174 | { 175 | return view_.extract() != 0; 176 | } 177 | 178 | /// \returns Whether or not all flags are set. 179 | bool all() const noexcept 180 | { 181 | return view_.extract() == clear_other_bits<0, bit_size()>(~0ull); 182 | } 183 | 184 | /// \returns Whether or not no flags are set. 185 | bool none() const noexcept 186 | { 187 | return view_.extract() == 0; 188 | } 189 | 190 | /// \effects Sets all flags to `value`. 191 | void set_all(bool value) const noexcept 192 | { 193 | if (value) 194 | view_.put(~0ull); 195 | else 196 | view_.put(0); 197 | } 198 | 199 | /// \effects Sets all flags to `true`. 200 | void set_all() const noexcept 201 | { 202 | set_all(true); 203 | } 204 | 205 | /// \effects Sets all flags to `false`. 206 | void reset_all() const noexcept 207 | { 208 | set_all(false); 209 | } 210 | 211 | /// \effects Toggles all flags. 212 | void toggle_all() const noexcept 213 | { 214 | view_.put(~view_.extract()); 215 | } 216 | 217 | //=== comparison ===// 218 | friend bool operator==(const proxy& lhs, const proxy& rhs) noexcept 219 | { 220 | return lhs.get() == rhs.get(); 221 | } 222 | friend bool operator!=(const proxy& lhs, const proxy& rhs) noexcept 223 | { 224 | return lhs.get() != rhs.get(); 225 | } 226 | 227 | bool operator==(flag_combo rhs) const noexcept 228 | { 229 | return get() == get_flags(rhs); 230 | } 231 | bool operator!=(flag_combo rhs) const noexcept 232 | { 233 | return get() != get_flags(rhs); 234 | } 235 | 236 | friend bool operator==(flag_combo lhs, const proxy& rhs) noexcept 237 | { 238 | return rhs == lhs; 239 | } 240 | friend bool operator!=(flag_combo lhs, const proxy& rhs) noexcept 241 | { 242 | return rhs != lhs; 243 | } 244 | 245 | private: 246 | explicit proxy(BitView view) noexcept : view_(view) {} 247 | 248 | BitView view_; 249 | 250 | friend tiny_type_access; 251 | }; 252 | }; 253 | } // namespace tiny 254 | } // namespace foonathan 255 | 256 | #endif // FOONATHAN_TINY_TINY_FLAG_SET_HPP_INCLUDED 257 | -------------------------------------------------------------------------------- /include/foonathan/tiny/optional_impl.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_OPTIONAL_IMPL_HPP_INCLUDED 6 | #define FOONATHAN_TINY_OPTIONAL_IMPL_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace foonathan 14 | { 15 | namespace tiny 16 | { 17 | /// \exclude 18 | namespace opt_detail 19 | { 20 | template 21 | struct compressed_optional 22 | { 23 | using traits = tombstone_traits; 24 | typename traits::storage_type storage; 25 | 26 | static constexpr std::size_t empty_tombstone() noexcept 27 | { 28 | return traits::tombstone_count - 1u; 29 | } 30 | 31 | void store_value_flag() noexcept {} 32 | 33 | void store_none_flag() noexcept 34 | { 35 | traits::create_tombstone(storage, empty_tombstone()); 36 | } 37 | 38 | bool has_value() const noexcept 39 | { 40 | return traits::get_tombstone(storage) != empty_tombstone(); 41 | } 42 | }; 43 | 44 | template 45 | struct uncompressed_optional 46 | { 47 | using traits = tombstone_traits; 48 | typename traits::storage_type storage; 49 | tiny_storage flag; 50 | 51 | void store_value_flag() noexcept 52 | { 53 | flag.tiny() = true; 54 | } 55 | 56 | void store_none_flag() noexcept 57 | { 58 | flag.tiny() = false; 59 | } 60 | 61 | bool has_value() const noexcept 62 | { 63 | return flag.tiny(); 64 | } 65 | }; 66 | 67 | template 68 | struct compressed_traits; 69 | template 70 | struct uncompressed_traits; 71 | } // namespace opt_detail 72 | 73 | /// The storage implementation of an optional type that uses tombstones whenever possible. 74 | /// 75 | /// It is just a low-level implementation helper for an actual optional. 76 | /// A proper optional type should be built on top of it. 77 | template 78 | class optional_impl 79 | { 80 | using traits = tombstone_traits; 81 | 82 | public: 83 | using value_type = typename traits::object_type; 84 | using is_compressed = std::integral_constant 0u)>; 85 | 86 | //=== constructors ===// 87 | /// \effects Creates an empty optional. 88 | optional_impl() noexcept 89 | { 90 | impl_.store_none_flag(); 91 | } 92 | 93 | /// \effects Does nothing. 94 | /// \notes This will leak the value if there is one stored currently. 95 | ~optional_impl() noexcept = default; 96 | 97 | optional_impl(const optional_impl&) = delete; 98 | optional_impl& operator=(const optional_impl&) = delete; 99 | 100 | //=== mutators ===// 101 | /// \effects Creates a value by forwarding the arguments. 102 | /// \requires `has_value() == false`. 103 | template 104 | void create_value(Args&&... args) 105 | { 106 | DEBUG_ASSERT(!has_value(), detail::precondition_handler{}); 107 | traits::create_object(impl_.storage, static_cast(args)...); 108 | impl_.store_value_flag(); 109 | } 110 | 111 | /// \effects Destoys the currently stored value. 112 | /// \requires `has_value() == true`. 113 | void destroy_value() noexcept 114 | { 115 | DEBUG_ASSERT(has_value(), detail::precondition_handler{}); 116 | traits::destroy_object(impl_.storage); 117 | impl_.store_none_flag(); 118 | } 119 | 120 | //=== accessors ===// 121 | /// \returns Whether or not the optional currently stores a value. 122 | bool has_value() const noexcept 123 | { 124 | return impl_.has_value(); 125 | } 126 | 127 | /// \returns A reference to the value currently stored inside the optional. 128 | /// \requires `has_value() == true`. 129 | /// \group value 130 | auto value() noexcept -> typename traits::reference 131 | { 132 | DEBUG_ASSERT(has_value(), detail::precondition_handler{}); 133 | return traits::get_object(impl_.storage); 134 | } 135 | /// \group value 136 | auto value() const noexcept -> typename traits::const_reference 137 | { 138 | DEBUG_ASSERT(has_value(), detail::precondition_handler{}); 139 | return traits::get_object(impl_.storage); 140 | } 141 | 142 | private: 143 | typename std::conditional, 144 | opt_detail::uncompressed_optional>::type impl_; 145 | 146 | friend opt_detail::compressed_traits; 147 | friend opt_detail::uncompressed_traits; 148 | }; 149 | 150 | namespace opt_detail 151 | { 152 | template 153 | struct compressed_traits 154 | { 155 | using object_type = optional_impl; 156 | using storage_type = optional_impl; 157 | using reference = object_type&; 158 | using const_reference = const object_type&; 159 | 160 | static constexpr std::size_t tombstone_count = tombstone_traits::tombstone_count - 1; 161 | 162 | static void create_tombstone(storage_type& storage, 163 | std::size_t tombstone_index) noexcept 164 | { 165 | tombstone_traits::create_tombstone(storage.impl_.storage, tombstone_index); 166 | } 167 | 168 | // optional_impl only has a default constructor 169 | static void create_object(storage_type& storage) 170 | { 171 | // this overrides the tombstone so it means no tombstone at this level 172 | storage.impl_.store_none_flag(); 173 | } 174 | 175 | static void destroy_object(storage_type&) noexcept {} 176 | 177 | static std::size_t get_tombstone(const storage_type& storage) noexcept 178 | { 179 | // if non-empty optional: doesn't store a tombstone, so returns invalid index 180 | // if empty optional: stores tombstone with index tombstone_count, so invalid index 181 | // otherwise: another tombstone index 182 | return tombstone_traits::get_tombstone(storage.impl_.storage); 183 | } 184 | 185 | static reference get_object(storage_type& storage) noexcept 186 | { 187 | return storage; 188 | } 189 | static const_reference get_object(const storage_type& storage) noexcept 190 | { 191 | return storage; 192 | } 193 | }; 194 | 195 | template 196 | struct uncompressed_traits 197 | { 198 | using object_type = optional_impl; 199 | using storage_type = optional_impl; 200 | using reference = object_type&; 201 | using const_reference = const object_type&; 202 | 203 | static constexpr std::size_t tombstone_count = (1u << (CHAR_BIT - 1)) - 1; 204 | 205 | static void create_tombstone(storage_type& storage, 206 | std::size_t tombstone_index) noexcept 207 | { 208 | storage.impl_.flag.spare_bits().put(tombstone_index + 1); 209 | } 210 | 211 | // optional_impl only has a default constructor 212 | static void create_object(storage_type& storage) 213 | { 214 | // this overrides the tombstone so it means no tombstone at this level 215 | storage.impl_.store_none_flag(); 216 | storage.impl_.flag.spare_bits().put(0); 217 | } 218 | 219 | static void destroy_object(storage_type&) noexcept {} 220 | 221 | static std::size_t get_tombstone(const storage_type& storage) noexcept 222 | { 223 | return static_cast(storage.impl_.flag.spare_bits().extract() - 1); 224 | } 225 | 226 | static reference get_object(storage_type& storage) noexcept 227 | { 228 | return storage; 229 | } 230 | static const_reference get_object(const storage_type& storage) noexcept 231 | { 232 | return storage; 233 | } 234 | }; 235 | } // namespace opt_detail 236 | 237 | /// Specialization of the tombstone traits for [tiny::optional_impl](). 238 | template 239 | struct tombstone_traits> 240 | : std::conditional<(tombstone_traits::tombstone_count > 0), opt_detail::compressed_traits, 241 | opt_detail::uncompressed_traits>::type 242 | {}; 243 | } // namespace tiny 244 | } // namespace foonathan 245 | 246 | #endif // FOONATHAN_TINY_OPTIONAL_IMPL_HPP_INCLUDED 247 | -------------------------------------------------------------------------------- /include/foonathan/tiny/pointer_tiny_storage.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TINY_POINTER_STORAGE_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TINY_POINTER_STORAGE_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace foonathan 12 | { 13 | namespace tiny 14 | { 15 | namespace detail 16 | { 17 | template 18 | class pointer_storage_policy 19 | { 20 | static constexpr auto compressed_size = detail::ilog2_ceil(Alignment); 21 | static constexpr auto total_size = total_bit_size(); 22 | static constexpr auto remaining_size 23 | = total_size < compressed_size ? 0 : total_size - compressed_size; 24 | 25 | using ptr_view = bit_view; 26 | using ptr_cview = bit_view; 27 | 28 | struct compressed_storage 29 | { 30 | std::uintptr_t ptr; 31 | 32 | ptr_view view() noexcept 33 | { 34 | return ptr_view(ptr); 35 | } 36 | ptr_cview view() const noexcept 37 | { 38 | return ptr_cview(ptr); 39 | } 40 | }; 41 | 42 | struct uncompressed_storage 43 | { 44 | std::uintptr_t ptr; 45 | tiny_storage_type storage; 46 | 47 | using storage_view = bit_view, 0, last_bit>; 48 | using storage_cview 49 | = bit_view, 0, last_bit>; 50 | 51 | joined_bit_view view() noexcept 52 | { 53 | return join_bit_views(ptr_view(ptr), storage_view(storage)); 54 | } 55 | joined_bit_view view() const noexcept 56 | { 57 | return join_bit_views(ptr_cview(ptr), storage_cview(storage)); 58 | } 59 | }; 60 | 61 | using storage = typename std::conditional::type; 63 | 64 | storage storage_; 65 | 66 | private: 67 | using is_compressed = std::integral_constant; 68 | 69 | pointer_storage_policy() noexcept = default; 70 | 71 | auto storage_view() noexcept -> decltype(std::declval().view()) 72 | { 73 | return storage_.view(); 74 | } 75 | auto storage_view() const noexcept -> decltype(std::declval().view()) 76 | { 77 | return storage_.view(); 78 | } 79 | 80 | friend basic_tiny_storage, 81 | TinyTypes...>; 82 | 83 | public: 84 | template 85 | void set_pointer(T* ptr) noexcept 86 | { 87 | auto as_int = reinterpret_cast(ptr); 88 | DEBUG_ASSERT((are_cleared_bits<0, compressed_size>(as_int)), 89 | detail::precondition_handler{}, "invalid alignment of pointer"); 90 | auto tiny_bits = extract_bits<0, compressed_size>(storage_.ptr); 91 | storage_.ptr = static_cast(as_int | tiny_bits); 92 | } 93 | 94 | template 95 | T* get_pointer() const noexcept 96 | { 97 | return reinterpret_cast(clear_bits<0, compressed_size>(storage_.ptr)); 98 | } 99 | }; 100 | 101 | template 102 | class pointer_proxy 103 | { 104 | public: 105 | pointer_proxy(int, pointer_storage_policy* storage) noexcept 106 | : storage_(storage) 107 | {} 108 | 109 | operator T*() const noexcept 110 | { 111 | return storage_->template get_pointer(); 112 | } 113 | 114 | const pointer_proxy& operator=(T* pointer) const noexcept 115 | { 116 | storage_->set_pointer(pointer); 117 | return *this; 118 | } 119 | const pointer_proxy& operator+=(std::ptrdiff_t diff) const noexcept 120 | { 121 | storage_->set_pointer(storage_->template get_pointer() + diff); 122 | return *this; 123 | } 124 | const pointer_proxy& operator-=(std::ptrdiff_t diff) const noexcept 125 | { 126 | storage_->set_pointer(storage_->template get_pointer() - diff); 127 | return *this; 128 | } 129 | 130 | const pointer_proxy& operator++() const noexcept 131 | { 132 | return *this += 1; 133 | } 134 | T* operator++(int) const noexcept 135 | { 136 | auto ptr = storage_->template get_pointer(); 137 | *this += 1; 138 | return ptr; 139 | } 140 | 141 | const pointer_proxy& operator--() const noexcept 142 | { 143 | return *this -= 1; 144 | } 145 | T* operator--(int) const noexcept 146 | { 147 | auto ptr = storage_->template get_pointer(); 148 | *this -= 1; 149 | return ptr; 150 | } 151 | 152 | private: 153 | pointer_storage_policy* storage_; 154 | }; 155 | } // namespace detail 156 | 157 | /// Tag type that specifies that an object of type `T` will be aligned for `Alignment`. 158 | /// 159 | /// It is used for [tiny::tiny_pointer_storage]() and [tiny::pointer_variant_impl](). 160 | template 161 | struct aligned_obj 162 | { 163 | using type = T; 164 | static constexpr std::size_t alignment = Alignment; 165 | }; 166 | 167 | namespace detail 168 | { 169 | template 170 | struct alignment_traits 171 | { 172 | using type = T; 173 | static constexpr std::size_t alignment = alignof(T); 174 | }; 175 | 176 | template 177 | struct alignment_traits> 178 | { 179 | using type = T; 180 | static constexpr std::size_t alignment = Alignment; 181 | }; 182 | } // namespace detail 183 | 184 | /// The alignment of an object of the given type. 185 | /// 186 | /// If the type is [tiny::aligned_obj](), the alignment is the alignment specified there. 187 | /// Otherwise it is `alignof(T)`. 188 | template 189 | constexpr std::size_t alignment_of() 190 | { 191 | return detail::alignment_traits::alignment; 192 | } 193 | 194 | /// Stores a pointer to `T` and the specified tiny types. 195 | /// 196 | /// It will use the bits from the pointer that are always zero due to alignment to store the 197 | /// tiny bits whenever possible, using additional storage only if necessary. 198 | /// 199 | /// Pass [tiny::aligned_obj]() instead of `T` if you know that the object you need to point to 200 | /// has a given over-alignment. 201 | template 202 | class pointer_tiny_storage 203 | : public basic_tiny_storage(), TinyTypes...>, 204 | TinyTypes...> 205 | { 206 | static_assert(!std::is_same::type, void>::value, 207 | "void pointers have no alignment, wrap them in aligned_obj instead"); 208 | 209 | public: 210 | using value_type = typename detail::alignment_traits::type; 211 | using pointer_type = value_type*; 212 | 213 | /// Default constructor. 214 | /// \effects Creates a storage where the pointer is `nullptr` and the tiny types have all 215 | /// bits set to zero. 216 | pointer_tiny_storage() noexcept 217 | { 218 | pointer() = nullptr; 219 | } 220 | 221 | /// \effects Creates a storage where the pointer is `ptr` and the tiny types have all bits 222 | /// set to zero. 223 | explicit pointer_tiny_storage(pointer_type ptr) noexcept 224 | { 225 | pointer() = ptr; 226 | } 227 | 228 | /// \effects Creates a storage where the pointer is `ptr` and the tiny types are created 229 | /// from their object types. 230 | pointer_tiny_storage(pointer_type ptr, typename TinyTypes::object_type... tiny) noexcept 231 | : basic_tiny_storage(), TinyTypes...>, 232 | TinyTypes...>(tiny...) 233 | { 234 | pointer() = ptr; 235 | } 236 | 237 | /// \returns A proxy that behaves like a mutable reference to the stored pointer. 238 | detail::pointer_proxy(), TinyTypes...> pointer() noexcept 239 | { 240 | return {0, &this->storage_policy()}; 241 | } 242 | /// \returns The stored pointer. 243 | T* pointer() const noexcept 244 | { 245 | return this->storage_policy().template get_pointer(); 246 | } 247 | }; 248 | } // namespace tiny 249 | } // namespace foonathan 250 | 251 | #endif // FOONATHAN_TINY_TINY_POINTER_STORAGE_HPP_INCLUDED 252 | -------------------------------------------------------------------------------- /include/foonathan/tiny/padding_traits.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_PADDING_TRAITS_HPP_INCLUDED 6 | #define FOONATHAN_TINY_PADDING_TRAITS_HPP_INCLUDED 7 | 8 | #include 9 | #include 10 | 11 | namespace foonathan 12 | { 13 | namespace tiny 14 | { 15 | /// Checks whether the two given types are layout compatible. 16 | /// 17 | /// Two types are layout compatible if they are the same type (ignoring cv), 18 | /// or if they are two layout compatible standard layout types as described [class.mem]/23, 19 | /// i.e. their members in declaration order are pairwise layout compatible. 20 | /// 21 | /// \notes It is not actually possible to implement this type traits, 22 | /// so it just checks that size and alignment is the same as a heuristic. 23 | template 24 | struct is_layout_compatible 25 | : std::integral_constant< 26 | bool, 27 | std::is_same::type, typename std::remove_cv::type>::value 28 | || (std::is_standard_layout::value && std::is_standard_layout::value 29 | && sizeof(T) == sizeof(U) && alignof(T) && alignof(U))> 30 | {}; 31 | 32 | //=== padding_traits ===// 33 | /// Information about the padding bits of a type. 34 | /// 35 | /// A specialization usually simply inherits from [tiny::padding_traits_aggregate](). 36 | /// \requires It must only be specialized for standard layout types. 37 | template 38 | struct padding_traits 39 | { 40 | /// Whether or not this is a specialization that actually provides information. 41 | static constexpr auto is_specialized = false; 42 | 43 | /// \returns A [tiny::bit_view]() to the padding bytes. 44 | /// The integer type must be `unsigned char[sizeof(T)]`. 45 | /// \group padding_view 46 | static bit_view padding_view(unsigned char* memory) noexcept 47 | { 48 | (void)memory; 49 | return {}; 50 | } 51 | /// \group padding_view 52 | static bit_view padding_view( 53 | const unsigned char* memory) noexcept 54 | { 55 | (void)memory; 56 | return {}; 57 | } 58 | }; 59 | 60 | /// \returns The padding view type of the given type. 61 | /// \notes Its `const`-qualification depends on the cv of `T`. 62 | template 63 | using padding_view_t = decltype(padding_traits::type>::padding_view( 64 | std::declval::value, const unsigned char*, 65 | unsigned char*>::type>())); 66 | 67 | /// \returns The padding view for the object. 68 | /// \group padding_of 69 | template 70 | padding_view_t padding_of(T& obj) noexcept 71 | { 72 | return padding_traits::padding_view(reinterpret_cast(&obj)); 73 | } 74 | /// \group padding_of 75 | template 76 | padding_view_t padding_of(const T& obj) noexcept 77 | { 78 | return padding_traits::padding_view(reinterpret_cast(&obj)); 79 | } 80 | 81 | /// \returns The amount of padding bits in the given type. 82 | template 83 | constexpr std::size_t padding_bit_size() noexcept 84 | { 85 | return padding_view_t::size(); 86 | } 87 | 88 | //=== padding_traits_aggregate ===// 89 | /// Traits type containing information about a member of an aggregate type. 90 | template 91 | struct aggregate_member 92 | { 93 | using object_type = T; 94 | using member_type = Member; 95 | 96 | /// \returns The offset of that member inside `T`. 97 | static constexpr std::size_t offset() noexcept 98 | { 99 | return Offset; 100 | } 101 | 102 | /// \returns A reference to the member. 103 | /// \group get 104 | static Member& get(T& obj) noexcept 105 | { 106 | return obj.*Ptr; 107 | } 108 | /// \group get 109 | static const Member& get(const T& obj) noexcept 110 | { 111 | return obj.*Ptr; 112 | } 113 | }; 114 | 115 | /// Creates a [tiny::aggregate_member]() to the member `Name` of type `Type`. 116 | #define FOONATHAN_TINY_MEMBER(Type, Name) \ 117 | aggregate_member().Name), &Type::Name, offsetof(Type, Name)> 118 | 119 | namespace detail 120 | { 121 | template 122 | struct padding_between 123 | { 124 | static constexpr auto begin 125 | = (FirstMember::offset() + sizeof(typename FirstMember::member_type)) * CHAR_BIT; 126 | static constexpr auto end = SecondMember::offset() * CHAR_BIT; 127 | 128 | static constexpr auto has_padding = begin != end; 129 | 130 | template 131 | static auto get_subview(ObjectView object_view) noexcept 132 | -> decltype(object_view.template subview()) 133 | { 134 | return object_view.template subview(); 135 | } 136 | }; 137 | 138 | template 139 | struct padding_between 140 | { 141 | static constexpr auto begin 142 | = (Member::offset() + sizeof(typename Member::member_type)) * CHAR_BIT; 143 | static constexpr auto end = sizeof(typename Member::object_type) * CHAR_BIT; 144 | 145 | static constexpr auto has_padding = begin != end; 146 | 147 | template 148 | static auto get_subview(ObjectView object_view) noexcept 149 | -> decltype(object_view.template subview()) 150 | { 151 | return object_view.template subview(); 152 | } 153 | }; 154 | 155 | template 156 | struct member_list 157 | {}; 158 | 159 | // MembersB is MembersA shifted over by one and with a trailing void 160 | template 161 | struct padding_aggregate_impl 162 | { 163 | template 164 | static auto get_view(ObjectView view) noexcept 165 | -> decltype(view.template subview<0, 0>()) 166 | { 167 | return view.template subview<0, 0>(); 168 | } 169 | }; 170 | template 171 | struct padding_aggregate_impl, member_list> 172 | { 173 | using between = padding_between; 174 | using tail = padding_aggregate_impl, member_list>; 175 | 176 | template ::type> 178 | static auto get_view(ObjectView view) noexcept 179 | -> joined_bit_view 181 | { 182 | return join_bit_views(between::get_subview(view), tail::get_view(view)); 183 | } 184 | template ::type> 186 | static auto get_view(ObjectView view) noexcept -> decltype(tail::get_view(view)) 187 | { 188 | return tail::get_view(view); 189 | } 190 | }; 191 | 192 | template 193 | struct padding_aggregate 194 | : padding_aggregate_impl, member_list> 195 | { 196 | using object_type = typename Head::object_type; 197 | }; 198 | } // namespace detail 199 | 200 | /// Implements the [tiny::padding_traits]() for an aggregate type. 201 | /// 202 | /// Simply specialize the traits for your type and inherit from it, 203 | /// passing `FOONATHAN_TINY_MEMBER(Type, Member)` for every member. 204 | /// This will automatically calculate the padding bytes and provide a view to it. 205 | template 206 | class padding_traits_aggregate 207 | { 208 | static_assert(sizeof...(Members) > 0, "type must not be empty"); 209 | 210 | using impl = detail::padding_aggregate; 211 | using object_type = typename impl::object_type; 212 | 213 | static bit_view byte_view( 214 | unsigned char* memory) noexcept 215 | { 216 | return bit_view(memory); 217 | } 218 | static bit_view byte_view( 219 | const unsigned char* memory) noexcept 220 | { 221 | return bit_view(memory); 222 | } 223 | 224 | public: 225 | static constexpr auto is_specialized = true; 226 | 227 | static auto padding_view(unsigned char* memory) noexcept 228 | -> decltype(impl::get_view(byte_view(memory))) 229 | { 230 | return impl::get_view(byte_view(memory)); 231 | } 232 | 233 | static auto padding_view(const unsigned char* memory) noexcept 234 | -> decltype(impl::get_view(byte_view(memory))) 235 | { 236 | return impl::get_view(byte_view(memory)); 237 | } 238 | }; 239 | 240 | /// Implements the [tiny::padding_traits]() by using a layout compatible type. 241 | /// 242 | /// Simply specialize the traits for your type and inherit from it, 243 | /// passing it the type that is layout compatible and already specialized them. 244 | template 245 | struct padding_traits_layout_compatible : padding_traits 246 | { 247 | static_assert(is_layout_compatible::value, 248 | "LayoutCompatible not layout compatible"); 249 | static_assert(padding_traits::is_specialized, 250 | "LayoutCompatible doesn't have the padding traits either"); 251 | }; 252 | } // namespace tiny 253 | } // namespace foonathan 254 | 255 | #endif // FOONATHAN_TINY_PADDING_TRAITS_HPP_INCLUDED 256 | -------------------------------------------------------------------------------- /include/foonathan/tiny/padding_tiny_storage.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_PADDING_TINY_STORAGE_HPP_INCLUDED 6 | #define FOONATHAN_TINY_PADDING_TINY_STORAGE_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace foonathan 14 | { 15 | namespace tiny 16 | { 17 | namespace detail 18 | { 19 | //=== padded_holder ===// 20 | // the compiler may not copy the padding bytes when copying the type, 21 | // so we need to do that ourselves 22 | template 23 | class padded_holder; 24 | 25 | template 26 | class padded_holder 27 | { 28 | public: 29 | padded_holder() = default; 30 | 31 | template 32 | void construct(Args&&... args) 33 | { 34 | padded_ = Padded(static_cast(args)...); 35 | clear_bits(padding_of(padded_)); 36 | } 37 | 38 | padded_holder(const padded_holder& other) noexcept 39 | { 40 | std::memcpy(&padded_, &other.padded_, sizeof(Padded)); 41 | } 42 | 43 | ~padded_holder() = default; 44 | 45 | padded_holder& operator=(const padded_holder& other) noexcept 46 | { 47 | std::memcpy(&padded_, &other.padded_, sizeof(Padded)); 48 | return *this; 49 | } 50 | 51 | Padded& get() noexcept 52 | { 53 | return padded_; 54 | } 55 | const Padded& get() const noexcept 56 | { 57 | return padded_; 58 | } 59 | 60 | private: 61 | Padded padded_; 62 | }; 63 | 64 | template 65 | class padded_holder 66 | { 67 | public: 68 | padded_holder() = default; 69 | 70 | template 71 | void construct(Args&&... args) 72 | { 73 | ::new (get_pointer()) Padded(static_cast(args)...); 74 | clear_bits(padding_of(get())); 75 | } 76 | 77 | padded_holder(const padded_holder& other) 78 | { 79 | ::new (get_pointer()) Padded(other.get()); 80 | assign_padding(other.get()); 81 | } 82 | 83 | padded_holder(padded_holder&& other) noexcept( 84 | std::is_nothrow_move_constructible::value) 85 | { 86 | ::new (get_pointer()) Padded(static_cast(other.get())); 87 | assign_padding(other.get()); 88 | } 89 | 90 | ~padded_holder() noexcept 91 | { 92 | get().~Padded(); 93 | } 94 | 95 | padded_holder& operator=(const padded_holder& other) 96 | { 97 | get() = other.get(); 98 | assign_padding(other.get()); 99 | return *this; 100 | } 101 | 102 | padded_holder& operator=(padded_holder&& other) noexcept( 103 | std::is_nothrow_move_assignable::value) 104 | { 105 | get() = static_cast(other.get()); 106 | assign_padding(other.get()); 107 | return *this; 108 | } 109 | 110 | Padded& get() noexcept 111 | { 112 | return *static_cast(get_pointer()); 113 | } 114 | const Padded& get() const noexcept 115 | { 116 | return *static_cast(get_pointer()); 117 | } 118 | 119 | private: 120 | void assign_padding(const Padded& other) noexcept 121 | { 122 | auto dest = padding_of(get()); 123 | auto src = padding_of(other); 124 | copy_bits(dest, src); 125 | } 126 | 127 | void* get_pointer() noexcept 128 | { 129 | return &storage_; 130 | } 131 | const void* get_pointer() const noexcept 132 | { 133 | return &storage_; 134 | } 135 | 136 | typename std::aligned_storage::type storage_; 137 | }; 138 | 139 | template 140 | using padded_holder_for = padded_holder::value, Padded>; 141 | 142 | //=== padded_storage_policy ===// 143 | template 144 | class padded_storage_policy 145 | { 146 | static constexpr auto compressed_size = padding_bit_size(); 147 | static constexpr auto total_size = total_bit_size(); 148 | static constexpr auto remaining_size 149 | = total_size > compressed_size ? total_size - compressed_size : 0; 150 | 151 | struct compressed_storage 152 | { 153 | padded_holder_for obj; 154 | 155 | template 156 | compressed_storage(Args&&... args) : obj(static_cast(args)...) 157 | {} 158 | 159 | Padded& object() noexcept 160 | { 161 | return obj.get(); 162 | } 163 | const Padded& object() const noexcept 164 | { 165 | return obj.get(); 166 | } 167 | 168 | padding_view_t view() noexcept 169 | { 170 | return padding_of(obj.get()); 171 | } 172 | padding_view_t view() const noexcept 173 | { 174 | return padding_of(obj.get()); 175 | } 176 | }; 177 | 178 | struct uncompressed_storage 179 | { 180 | padded_holder_for obj; 181 | tiny_storage_type storage; 182 | 183 | template 184 | uncompressed_storage(Args&&... args) : obj(static_cast(args)...) 185 | {} 186 | 187 | using storage_view = bit_view, 0, last_bit>; 188 | using storage_cview 189 | = bit_view, 0, last_bit>; 190 | 191 | Padded& object() noexcept 192 | { 193 | return obj.get(); 194 | } 195 | const Padded& object() const noexcept 196 | { 197 | return obj.get(); 198 | } 199 | 200 | joined_bit_view, storage_view> view() noexcept 201 | { 202 | return join_bit_views(padding_of(obj.get()), storage_view(storage)); 203 | } 204 | joined_bit_view, storage_cview> view() const noexcept 205 | { 206 | return join_bit_views(padding_of(obj.get()), storage_cview(storage)); 207 | } 208 | }; 209 | 210 | using storage = typename std::conditional::type; 212 | 213 | storage storage_; 214 | 215 | private: 216 | using is_compressed = std::integral_constant; 217 | 218 | padded_storage_policy() = default; 219 | 220 | auto storage_view() noexcept -> decltype(std::declval().view()) 221 | { 222 | return storage_.view(); 223 | } 224 | auto storage_view() const noexcept -> decltype(std::declval().view()) 225 | { 226 | return storage_.view(); 227 | } 228 | 229 | friend basic_tiny_storage, TinyTypes...>; 230 | 231 | public: 232 | template 233 | void construct(Args&&... args) 234 | { 235 | storage_.obj.construct(static_cast(args)...); 236 | } 237 | 238 | Padded& object() noexcept 239 | { 240 | return storage_.obj.get(); 241 | } 242 | const Padded& object() const noexcept 243 | { 244 | return storage_.obj.get(); 245 | } 246 | }; 247 | } // namespace detail 248 | 249 | /// Stores an object of the specified type and the tiny types. 250 | /// 251 | /// The tiny types are stored in padding bits of the object whenever possible. 252 | template 253 | class padding_tiny_storage 254 | : public basic_tiny_storage, TinyTypes...> 255 | { 256 | public: 257 | using padded_type = Padded; 258 | 259 | /// Default constructor. 260 | /// \effects Creates a storage where the object is default constructed and the tiny types 261 | /// have all bits set to zero. 262 | padding_tiny_storage() 263 | { 264 | this->storage_policy().construct(); 265 | } 266 | 267 | /// \effects Creates a storage where the object is `obj` and the tiny types have all bits 268 | /// set to zero. 269 | explicit padding_tiny_storage(padded_type obj) 270 | { 271 | this->storage_policy().construct(static_cast(obj)); 272 | } 273 | 274 | /// \effects Creates a storage where the object `obj` and the tiny types are created from 275 | /// their object types. 276 | padding_tiny_storage(padded_type obj, typename TinyTypes::object_type... tiny) 277 | : padding_tiny_storage(detail::make_index_sequence{}, 278 | static_cast(obj), tiny...) 279 | {} 280 | 281 | /// \returns A reference to the object. 282 | /// \group object 283 | padded_type& object() noexcept 284 | { 285 | return this->storage_policy().object(); 286 | } 287 | /// \group object 288 | const padded_type& object() const noexcept 289 | { 290 | return this->storage_policy().object(); 291 | } 292 | 293 | private: 294 | template 295 | padding_tiny_storage(detail::index_sequence, padded_type obj, 296 | typename TinyTypes::object_type... tiny) 297 | { 298 | this->storage_policy().construct(static_cast(obj)); 299 | 300 | bool for_each[] = {(this->template at() = tiny, true)..., true}; 301 | (void)for_each; 302 | } 303 | }; 304 | } // namespace tiny 305 | } // namespace foonathan 306 | 307 | #endif // FOONATHAN_TINY_PADDING_TINY_STORAGE_HPP_INCLUDED 308 | -------------------------------------------------------------------------------- /include/foonathan/tiny/tagged_union_impl.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TAGGED_UNION_IMPL_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TAGGED_UNION_IMPL_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace foonathan 14 | { 15 | namespace tiny 16 | { 17 | /// \exclude 18 | namespace tagged_union_detail 19 | { 20 | template 21 | union types_storage; 22 | } // namespace tagged_union_detail 23 | 24 | template 25 | class tagged_union_impl; 26 | 27 | /// A list of types that are being stored in a [tiny::tagged_union_impl](). 28 | template 29 | struct union_types 30 | { 31 | /// \exclude 32 | using tag = tiny_int_range<0, std::intmax_t(sizeof...(T)), std::size_t>; 33 | 34 | /// \exclude 35 | using storage = tagged_union_detail::types_storage, 0, T...>; 36 | }; 37 | 38 | /// The tag of a [tiny::tagged_union_impl](). 39 | /// 40 | /// Every type of the union must have an object of this type as its first member. 41 | /// It stores the tag as well as some other tiny types that can fit into the same space. 42 | template 43 | class tagged_union_tag 44 | { 45 | using tiny_tag = typename UnionTypes::tag; 46 | using storage_type = tiny_storage_type_for; 47 | using tag_view = bit_view; 48 | using tag_cview = bit_view; 49 | using storage_view = bit_view; 50 | using storage_cview = bit_view; 51 | 52 | storage_type storage_; 53 | 54 | public: 55 | tagged_union_tag() noexcept = default; 56 | 57 | /// The amount of bits that are not filled by the tag. 58 | static constexpr std::size_t spare_bits = storage_view::size(); 59 | 60 | /// \returns A [tiny::basic_tiny_storage_view]() that allows access to the specific tiny 61 | /// types stored in the spare bits of the tag. 62 | /// \notes For consistent result the tiny types should always be the same on every 63 | /// call. 64 | /// \group tiny_view 65 | template 66 | auto tiny_view(tiny_types) noexcept 67 | -> basic_tiny_storage_view 68 | { 69 | return storage_view(storage_); 70 | } 71 | /// \group tiny_view 72 | template 73 | auto tiny_view(tiny_types) const noexcept 74 | -> basic_tiny_storage_view 75 | { 76 | return storage_cview(storage_); 77 | } 78 | 79 | /// \returns A [tiny::basic_tiny_storage_view]() that uses the spare bits of the tag and 80 | /// additional storage. 81 | /// \notes For consistent result the tiny types should always be the same on every 82 | /// call. 83 | /// \group tiny_view_storage 84 | template 85 | auto tiny_view(tiny_types, BitView additional_storage) noexcept 86 | -> basic_tiny_storage_view, TinyTypes...> 87 | { 88 | return join_bit_views(storage_view(storage_), additional_storage); 89 | } 90 | /// \group tiny_view_storage 91 | template 92 | auto tiny_view(tiny_types, BitView additional_storage) const noexcept 93 | -> basic_tiny_storage_view, TinyTypes...> 94 | { 95 | return join_bit_views(storage_cview(storage_), additional_storage); 96 | } 97 | 98 | private: 99 | void set_tag(std::size_t tag) noexcept 100 | { 101 | make_tiny_proxy(tag_view(storage_)) = tag; 102 | } 103 | std::size_t get_tag() const noexcept 104 | { 105 | return make_tiny_proxy(tag_cview(storage_)); 106 | } 107 | 108 | template 109 | friend union tagged_union_detail::types_storage; 110 | template 111 | friend class tagged_union_impl; 112 | }; 113 | 114 | /// \exclude 115 | namespace tagged_union_detail 116 | { 117 | template 118 | struct type_tag 119 | {}; 120 | 121 | template 122 | union types_storage; 123 | 124 | template 125 | union types_storage 126 | { 127 | static_assert(std::is_standard_layout::value, 128 | "all types of the tagged union must be standard layout"); 129 | 130 | tagged_union_tag tag; 131 | Head head; 132 | types_storage tail; 133 | 134 | types_storage() {} 135 | 136 | template 137 | void construct(type_tag, Args&&... args) 138 | { 139 | ::new (static_cast(&head)) Head(static_cast(args)...); 140 | tag.set_tag(I); 141 | } 142 | template 143 | void construct(type_tag tag, Args&&... args) 144 | { 145 | tail.construct(tag, static_cast(args)...); 146 | } 147 | 148 | bool has_value(type_tag) const noexcept 149 | { 150 | return tag.get_tag() == I; 151 | } 152 | template 153 | bool has_value(type_tag tag) const noexcept 154 | { 155 | return tail.has_value(tag); 156 | } 157 | 158 | Head& get(type_tag) noexcept 159 | { 160 | return head; 161 | } 162 | const Head& get(type_tag) const noexcept 163 | { 164 | return head; 165 | } 166 | template 167 | T& get(type_tag tag) noexcept 168 | { 169 | return tail.get(tag); 170 | } 171 | template 172 | const T& get(type_tag tag) const noexcept 173 | { 174 | return tail.get(tag); 175 | } 176 | }; 177 | 178 | template 179 | union types_storage 180 | { 181 | tagged_union_tag tag; 182 | 183 | types_storage() = default; 184 | 185 | template 186 | void construct(type_tag, Args&&...) 187 | { 188 | static_assert(sizeof(T) != sizeof(T), "type not stored in tagged_union"); 189 | } 190 | 191 | template 192 | bool has_value(type_tag) const noexcept 193 | { 194 | return false; 195 | } 196 | 197 | template 198 | T& get(type_tag) noexcept 199 | { 200 | static_assert(sizeof(T) != sizeof(T), "type not stored in tagged_union"); 201 | return DEBUG_UNREACHABLE(detail::assert_handler{}); 202 | } 203 | template 204 | const T& get(type_tag) const noexcept 205 | { 206 | static_assert(sizeof(T) != sizeof(T), "type not stored in tagged_union"); 207 | return DEBUG_UNREACHABLE(detail::assert_handler{}); 208 | } 209 | }; 210 | 211 | template 212 | using types_storage_for = typename UnionTypes::storage; 213 | } // namespace tagged_union_detail 214 | 215 | /// An intrusive tagged union implementation helper. 216 | /// 217 | /// It is just a low-level implementation helper. 218 | /// A proper union type should be built on top of it. 219 | /// 220 | /// The union can either store an object of type `T` or is invalid and stores nothing. 221 | /// If it is invalid, only a new value can be put inside of it; it can't answer whether it 222 | /// stores an object of type `T`. This is done to prevent using an additional "empty union" tag 223 | /// value. 224 | /// 225 | /// If you want to implement a union that can be empty, use [tiny::tagged_union_empty]() as 226 | /// dummy type. 227 | /// 228 | /// \requires Every type of the tagged union must be a standard layout type and have the 229 | /// [tiny::tagged_union_tag]() as first member. 230 | template 231 | class tagged_union_impl 232 | { 233 | public: 234 | using value_types = UnionTypes; 235 | 236 | //=== constructors ===// 237 | /// \effects Creates an invalid union. 238 | tagged_union_impl() = default; 239 | 240 | /// \effects Does nothing. 241 | /// \notes This will leak the currently stored value. 242 | ~tagged_union_impl() = default; 243 | 244 | tagged_union_impl(const tagged_union_impl&) = delete; 245 | 246 | tagged_union_impl& operator=(const tagged_union_impl&) = delete; 247 | 248 | //=== mutators ===// 249 | /// \effects Creates a value of the specified type by forwarding the arguments. 250 | /// \requires `T` is a type that can be stored and the union is currently in the invalid 251 | /// state. 252 | template 253 | void create_value(Args&&... args) 254 | { 255 | storage_.construct(tagged_union_detail::type_tag{}, static_cast(args)...); 256 | } 257 | 258 | /// \effects Destroys the currently stored value of the specified type 259 | /// and puts it into the invalid state. 260 | /// \requires `has_value() == true`. 261 | template 262 | void destroy_value() noexcept 263 | { 264 | value().~T(); 265 | } 266 | 267 | //=== accessors ===// 268 | /// \returns The tag of the object currently stored. 269 | /// \requires The union is not in the invalid state. 270 | std::size_t tag() const noexcept 271 | { 272 | return storage_.tag.get_tag(); 273 | } 274 | 275 | /// \returns Whether or not the union currently stores a value of the specified type. 276 | /// \requires The union is not in the invalid state. 277 | template 278 | bool has_value() const noexcept 279 | { 280 | return storage_.has_value(tagged_union_detail::type_tag{}); 281 | } 282 | 283 | /// \returns A reference to the value of the specified type that is currently being stored. 284 | /// \requires `has_value() == true`. 285 | /// \group value 286 | template 287 | T& value() noexcept 288 | { 289 | DEBUG_ASSERT(has_value(), detail::precondition_handler{}); 290 | return storage_.get(tagged_union_detail::type_tag{}); 291 | } 292 | 293 | /// \group value 294 | template 295 | const T& value() const noexcept 296 | { 297 | DEBUG_ASSERT(has_value(), detail::precondition_handler{}); 298 | return storage_.get(tagged_union_detail::type_tag{}); 299 | } 300 | 301 | private: 302 | tagged_union_detail::types_storage_for storage_; 303 | }; 304 | 305 | /// Dummy type to allow an empty [tiny::tagged_union_impl](). 306 | template 307 | struct tagged_union_empty 308 | { 309 | tagged_union_tag tag; 310 | }; 311 | } // namespace tiny 312 | } // namespace foonathan 313 | 314 | #endif // FOONATHAN_TINY_TAGGED_UNION_IMPL_HPP_INCLUDED 315 | -------------------------------------------------------------------------------- /test/bit_view.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace foonathan::tiny; 10 | 11 | namespace 12 | { 13 | using test_integer = std::uint32_t; 14 | 15 | template 16 | void test_bit_view(bit_view view, std::uintmax_t value, const std::string& str) 17 | { 18 | REQUIRE(view.size() == str.size()); 19 | 20 | std::string stored_str; 21 | for (auto i = 0u; i != view.size(); ++i) 22 | if (view[i]) 23 | stored_str += '1'; 24 | else 25 | stored_str += '0'; 26 | REQUIRE(stored_str == str); 27 | 28 | REQUIRE(view.extract() == value); 29 | } 30 | } // namespace 31 | 32 | TEST_CASE("bit_view") 33 | { 34 | test_integer integer = 0u; 35 | 36 | SECTION("basic") 37 | { 38 | bit_view view(integer); 39 | test_bit_view(view, 0u, "0000000000"); 40 | REQUIRE(integer == 0u); 41 | 42 | integer = 0xFF; 43 | test_bit_view(view, 0xFF, "1111111100"); 44 | REQUIRE(integer == 0xFF); 45 | 46 | integer += 2048; 47 | test_bit_view(view, 0xFF, "1111111100"); 48 | 49 | view[0] = false; 50 | test_bit_view(view, 0xFE, "0111111100"); 51 | REQUIRE(integer == 2048 + 0xFE); 52 | 53 | view.put(0xAA); 54 | test_bit_view(view, 0xAA, "0101010100"); 55 | REQUIRE(integer == 2048 + 0xAA); 56 | 57 | SECTION("subview") 58 | { 59 | auto sub = view.subview<2, 6>(); 60 | test_bit_view(sub, 0xA, "0101"); 61 | } 62 | } 63 | SECTION("no modification outside") 64 | { 65 | integer = UINT32_MAX; 66 | bit_view view(integer); 67 | test_bit_view(view, 15, "1111"); 68 | 69 | SECTION("put") 70 | { 71 | view.put(0); 72 | test_bit_view(view, 0, "0000"); 73 | REQUIRE(integer == 0xFFFFFFF0); 74 | } 75 | SECTION("operator[]") 76 | { 77 | view[0] = false; 78 | view[1] = false; 79 | view[2] = false; 80 | view[3] = false; 81 | test_bit_view(view, 0, "0000"); 82 | REQUIRE(integer == 0xFFFFFFF0); 83 | } 84 | } 85 | SECTION("align middle") 86 | { 87 | bit_view view(integer); 88 | test_bit_view(view, 0, "0000"); 89 | 90 | SECTION("put") 91 | { 92 | view.put(15); 93 | test_bit_view(view, 15, "1111"); 94 | REQUIRE(integer == 15u << 4); 95 | } 96 | SECTION("operator[]") 97 | { 98 | view[0] = true; 99 | view[1] = true; 100 | view[2] = true; 101 | view[3] = true; 102 | test_bit_view(view, 15, "1111"); 103 | REQUIRE(integer == 15u << 4); 104 | } 105 | } 106 | SECTION("align end") 107 | { 108 | integer = UINT32_MAX; 109 | bit_view view(integer); 110 | test_bit_view(view, UINT16_MAX, "1111111111111111"); 111 | 112 | SECTION("put") 113 | { 114 | view.put(0); 115 | test_bit_view(view, 0, "0000000000000000"); 116 | integer = UINT16_MAX; 117 | } 118 | SECTION("operator[]") 119 | { 120 | for (auto i = 0u; i != 16u; ++i) 121 | view[i] = false; 122 | test_bit_view(view, 0, "0000000000000000"); 123 | integer = UINT16_MAX; 124 | } 125 | } 126 | SECTION("whole") 127 | { 128 | bit_view view(integer); 129 | view.put(std::uintmax_t(-1)); 130 | REQUIRE(integer == UINT32_MAX); 131 | } 132 | } 133 | 134 | TEST_CASE("bit_view array") 135 | { 136 | test_integer array[3] = {0u, 0u, 0u}; 137 | 138 | SECTION("basic") 139 | { 140 | SECTION("two array elements") 141 | { 142 | bit_view view(array); 143 | test_bit_view(view, 0u, "0000000000000000"); 144 | REQUIRE(array[0] == 0u); 145 | REQUIRE(array[1] == 0u); 146 | REQUIRE(array[2] == 0u); 147 | 148 | array[0] = 0xFF000000; 149 | array[1] = 0xC; 150 | test_bit_view(view, 0xCFF, "1111111100110000"); 151 | REQUIRE(array[0] == 0xFF000000); 152 | REQUIRE(array[1] == 0xC); 153 | REQUIRE(array[2] == 0u); 154 | 155 | array[0] |= 0xFF0000; 156 | array[1] |= 0xFF00; 157 | test_bit_view(view, 0xCFF, "1111111100110000"); 158 | 159 | view[0] = false; 160 | test_bit_view(view, 0xCFE, "0111111100110000"); 161 | REQUIRE(array[0] == 0xFEFF0000); 162 | REQUIRE(array[1] == 0xFF0C); 163 | REQUIRE(array[2] == 0u); 164 | 165 | view.put(0xAAAA); 166 | test_bit_view(view, 0xAAAA, "0101010101010101"); 167 | REQUIRE(array[0] == 0xAAFF0000); 168 | REQUIRE(array[1] == 0xFFAA); 169 | REQUIRE(array[2] == 0u); 170 | 171 | SECTION("subview") 172 | { 173 | auto sub = view.subview<6, 10>(); 174 | test_bit_view(sub, 0xA, "0101"); 175 | } 176 | } 177 | SECTION("three array elements") 178 | { 179 | bit_view view(array); 180 | test_bit_view(view, 0u, "000000000000000000000000000000000000"); 181 | REQUIRE(array[0] == 0u); 182 | REQUIRE(array[1] == 0u); 183 | REQUIRE(array[2] == 0u); 184 | 185 | array[0] = 0x80000000; 186 | array[1] = 0xAAAAAAAA; 187 | array[2] = 0xF2; 188 | test_bit_view(view, 0xAAAAAAAAA, "010101010101010101010101010101010101"); 189 | 190 | array[1] |= 0x55555555; 191 | test_bit_view(view, 0xBFFFFFFFE, "011111111111111111111111111111111101"); 192 | 193 | view[0] = true; 194 | test_bit_view(view, 0xBFFFFFFFF, "111111111111111111111111111111111101"); 195 | REQUIRE(array[0] == 0xC0000000); 196 | REQUIRE(array[1] == 0xFFFFFFFF); 197 | REQUIRE(array[2] == 0xF2); 198 | 199 | view.put(0x5555); 200 | test_bit_view(view, 0x5555, "101010101010101000000000000000000000"); 201 | REQUIRE(array[0] == 0x40000000); 202 | REQUIRE(array[1] == 0x1555); 203 | REQUIRE(array[2] == 0xF0); 204 | 205 | SECTION("subview") 206 | { 207 | auto sub = view.subview<0, 4>(); 208 | test_bit_view(sub, 0x5, "1010"); 209 | } 210 | } 211 | } 212 | SECTION("no modification outside") 213 | { 214 | array[0] = UINT32_MAX; 215 | array[1] = UINT32_MAX; 216 | array[2] = UINT32_MAX; 217 | 218 | SECTION("one array element") 219 | { 220 | bit_view view(array); 221 | test_bit_view(view, 0xFF, "11111111"); 222 | 223 | SECTION("put") 224 | { 225 | view.put(0); 226 | test_bit_view(view, 0, "00000000"); 227 | REQUIRE(array[0] == UINT32_MAX); 228 | REQUIRE(array[1] == 0xFFFFF00F); 229 | REQUIRE(array[2] == UINT32_MAX); 230 | } 231 | SECTION("operator[]") 232 | { 233 | view[0] = false; 234 | view[1] = false; 235 | view[2] = false; 236 | view[3] = false; 237 | test_bit_view(view, 0xF0, "00001111"); 238 | REQUIRE(array[0] == UINT32_MAX); 239 | REQUIRE(array[1] == 0xFFFFFF0F); 240 | REQUIRE(array[2] == UINT32_MAX); 241 | } 242 | } 243 | SECTION("two array elements") 244 | { 245 | bit_view view(array); 246 | test_bit_view(view, 15, "1111"); 247 | 248 | SECTION("put") 249 | { 250 | view.put(0); 251 | test_bit_view(view, 0, "0000"); 252 | REQUIRE(array[0] == 0x3FFFFFFF); 253 | REQUIRE(array[1] == 0xFFFFFFFC); 254 | REQUIRE(array[2] == UINT32_MAX); 255 | } 256 | SECTION("operator[]") 257 | { 258 | view[0] = false; 259 | view[1] = false; 260 | view[2] = false; 261 | view[3] = false; 262 | test_bit_view(view, 0, "0000"); 263 | REQUIRE(array[0] == 0x3FFFFFFF); 264 | REQUIRE(array[1] == 0xFFFFFFFC); 265 | REQUIRE(array[2] == UINT32_MAX); 266 | } 267 | } 268 | SECTION("three array elements") 269 | { 270 | bit_view view(array); 271 | test_bit_view(view, 0xFFFFFFFFFF, "1111111111111111111111111111111111111111"); 272 | 273 | SECTION("put") 274 | { 275 | view.put(0); 276 | test_bit_view(view, 0, "0000000000000000000000000000000000000000"); 277 | REQUIRE(array[0] == 0x0FFFFFFF); 278 | REQUIRE(array[1] == 0x00000000); 279 | REQUIRE(array[2] == 0xFFFFFFF0); 280 | } 281 | SECTION("operator[]") 282 | { 283 | view[0] = false; 284 | view[1] = false; 285 | view[2] = false; 286 | view[3] = false; 287 | view[8] = false; 288 | view[9] = false; 289 | view[10] = false; 290 | view[11] = false; 291 | view[36] = false; 292 | view[37] = false; 293 | view[38] = false; 294 | view[39] = false; 295 | test_bit_view(view, 0x0FFFFFF0F0, "0000111100001111111111111111111111110000"); 296 | REQUIRE(array[0] == 0x0FFFFFFF); 297 | REQUIRE(array[1] == 0xFFFFFF0F); 298 | REQUIRE(array[2] == 0xFFFFFFF0); 299 | } 300 | } 301 | } 302 | } 303 | 304 | TEST_CASE("joined_bit_view") 305 | { 306 | int array[2] = {0, 0}; 307 | int first = 0; 308 | int second = 0; 309 | 310 | using view_t 311 | = joined_bit_view, bit_view, bit_view>; 312 | using cview_t 313 | = joined_bit_view, bit_view, bit_view>; 314 | 315 | view_t view(first, array, second); 316 | test_bit_view(view, 0, "000000000000"); 317 | 318 | cview_t cview(view); 319 | test_bit_view(cview, 0, "000000000000"); 320 | 321 | first = 0xFF; 322 | test_bit_view(view, 0xF, "111100000000"); 323 | 324 | view.put(0x7F3); 325 | test_bit_view(view, 0x7F3, "110011111110"); 326 | REQUIRE(first == 0xF3); 327 | REQUIRE(array[0] == 0xF); 328 | REQUIRE(second == 0x7); 329 | 330 | view = join_bit_views(make_bit_view<0, 4>(second), make_bit_view<0, 4>(array), 331 | make_bit_view<0, 4>(first)); 332 | test_bit_view(view, 0x3F7, "111011111100"); 333 | 334 | view[3] = true; 335 | test_bit_view(view, 0x3FF, "111111111100"); 336 | 337 | bit_view sub_first = view.subview<0, 4>(); 338 | test_bit_view(sub_first, 0xF, "1111"); 339 | 340 | bit_view sub_second = view.subview<4, 8>(); 341 | test_bit_view(sub_second, 0xF, "1111"); 342 | 343 | bit_view sub_third = view.subview<8, 12>(); 344 | test_bit_view(sub_third, 0x3, "1100"); 345 | 346 | bit_view sub_sub_first = view.subview<0, 2>(); 347 | test_bit_view(sub_sub_first, 0x3, "11"); 348 | 349 | auto second_third = view.subview<4, 10>(); 350 | test_bit_view(second_third, 0x3F, "111111"); 351 | } 352 | 353 | TEST_CASE("bit_view convenience") 354 | { 355 | unsigned value = 0; 356 | 357 | value = put_bits<0, 1>(value, 1); 358 | REQUIRE(value == 1); 359 | 360 | value = put_bits<1, 3>(value, 7); 361 | REQUIRE(value == 7); 362 | 363 | REQUIRE(extract_bits<1, 3>(value) == 3); 364 | 365 | REQUIRE(are_cleared_bits<3, 5>(value)); 366 | REQUIRE(are_only_bits<0, 3>(value)); 367 | REQUIRE(are_only_bits<0, 5>(value)); 368 | REQUIRE(are_only_bits<0, last_bit>(value)); 369 | 370 | value = clear_bits<1, 2>(value); 371 | REQUIRE(value == 5); 372 | 373 | value = clear_other_bits<1, 2>(value); 374 | REQUIRE(value == 0); 375 | } 376 | -------------------------------------------------------------------------------- /test/tiny_types.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace foonathan::tiny; 15 | 16 | namespace 17 | { 18 | using tiny_storage = int; 19 | 20 | template 21 | typename TinyType::template proxy> make_proxy( 22 | tiny_storage& storage) 23 | { 24 | return make_tiny_proxy(make_bit_view<0, TinyType::bit_size()>(storage)); 25 | } 26 | template 27 | typename TinyType::template proxy> 28 | make_cproxy(const tiny_storage& storage) 29 | { 30 | return make_tiny_proxy(make_bit_view<0, TinyType::bit_size()>(storage)); 31 | } 32 | } // namespace 33 | 34 | namespace 35 | { 36 | template 37 | void verify_bool(Proxy proxy, bool value) 38 | { 39 | if (value) 40 | REQUIRE(proxy); 41 | else 42 | REQUIRE(!proxy); 43 | 44 | REQUIRE(proxy == proxy); 45 | REQUIRE_FALSE(proxy != proxy); 46 | 47 | REQUIRE(proxy == value); 48 | REQUIRE(value == proxy); 49 | REQUIRE_FALSE(proxy != value); 50 | REQUIRE_FALSE(value != proxy); 51 | } 52 | } // namespace 53 | 54 | TEST_CASE("tiny_bool") 55 | { 56 | tiny_storage storage = 0; 57 | 58 | auto cproxy = make_cproxy(storage); 59 | verify_bool(cproxy, false); 60 | 61 | auto proxy = make_proxy(storage); 62 | verify_bool(proxy, false); 63 | 64 | proxy = true; 65 | verify_bool(proxy, true); 66 | } 67 | 68 | namespace 69 | { 70 | template 71 | void verify_enum(Proxy proxy, Enum value) 72 | { 73 | REQUIRE(static_cast(proxy) == value); 74 | 75 | REQUIRE(proxy == proxy); 76 | REQUIRE_FALSE(proxy != proxy); 77 | 78 | REQUIRE(proxy == value); 79 | REQUIRE(value == proxy); 80 | REQUIRE_FALSE(proxy != value); 81 | REQUIRE_FALSE(value != proxy); 82 | } 83 | } // namespace 84 | 85 | TEST_CASE("tiny_enum") 86 | { 87 | enum class e 88 | { 89 | a, 90 | b, 91 | c, 92 | d, 93 | _unsigned_max = d, 94 | }; 95 | using type = tiny_enum; 96 | tiny_storage storage = 0; 97 | 98 | auto cproxy = make_cproxy(storage); 99 | verify_enum(cproxy, e::a); 100 | 101 | auto proxy = make_proxy(storage); 102 | verify_enum(proxy, e::a); 103 | 104 | proxy = e::b; 105 | verify_enum(proxy, e::b); 106 | 107 | proxy = e::c; 108 | verify_enum(proxy, e::c); 109 | 110 | proxy = e::d; 111 | verify_enum(proxy, e::d); 112 | } 113 | 114 | namespace 115 | { 116 | template 117 | void verify_unsigned(Proxy proxy, Int value) 118 | { 119 | REQUIRE(static_cast(proxy) == value); 120 | 121 | REQUIRE(proxy == proxy); 122 | REQUIRE_FALSE(proxy != proxy); 123 | 124 | REQUIRE(proxy == value); 125 | REQUIRE(value == proxy); 126 | REQUIRE_FALSE(proxy != value); 127 | REQUIRE_FALSE(value != proxy); 128 | 129 | REQUIRE(+proxy == value); 130 | REQUIRE(proxy + 0 == value + 0); 131 | REQUIRE(proxy - 0 == value); 132 | REQUIRE(proxy * 0 == value * 0); 133 | REQUIRE(proxy / 1 == value); 134 | REQUIRE(proxy % 1 == 0); 135 | } 136 | } // namespace 137 | 138 | TEST_CASE("tiny_unsigned") 139 | { 140 | using type = tiny_unsigned<7>; 141 | tiny_storage storage = 0; 142 | 143 | auto cproxy = make_cproxy(storage); 144 | verify_unsigned(cproxy, 0u); 145 | 146 | auto proxy = make_proxy(storage); 147 | verify_unsigned(proxy, 0u); 148 | 149 | SECTION("all values") 150 | { 151 | for (auto i = 0u; i <= 127u; ++i) 152 | { 153 | proxy = i; 154 | verify_unsigned(proxy, i); 155 | } 156 | } 157 | SECTION("arithmetic operation") 158 | { 159 | proxy += 17; 160 | verify_unsigned(proxy, 17u); 161 | 162 | proxy -= 4; 163 | verify_unsigned(proxy, 13u); 164 | 165 | proxy *= 2; 166 | verify_unsigned(proxy, 26u); 167 | 168 | proxy /= 3; 169 | verify_unsigned(proxy, 8u); 170 | 171 | proxy %= 3; 172 | verify_unsigned(proxy, 2u); 173 | 174 | auto value = proxy++; 175 | verify_unsigned(proxy, 3u); 176 | REQUIRE(value == 2); 177 | 178 | value = ++proxy; 179 | verify_unsigned(proxy, 4u); 180 | REQUIRE(value == 4); 181 | 182 | value = proxy--; 183 | verify_unsigned(proxy, 3u); 184 | REQUIRE(value == 4); 185 | 186 | value = --proxy; 187 | verify_unsigned(proxy, 2u); 188 | REQUIRE(value == 2); 189 | } 190 | } 191 | 192 | namespace 193 | { 194 | template 195 | void verify_int(Proxy proxy, Int value) 196 | { 197 | verify_unsigned(proxy, value); 198 | REQUIRE(-(-proxy) == value); 199 | } 200 | } // namespace 201 | 202 | TEST_CASE("tiny_int") 203 | { 204 | using type = tiny_int<7>; 205 | tiny_storage storage = 0; 206 | 207 | auto cproxy = make_cproxy(storage); 208 | verify_int(cproxy, 0); 209 | 210 | auto proxy = make_proxy(storage); 211 | verify_int(proxy, 0); 212 | 213 | SECTION("all values") 214 | { 215 | for (auto i = -64; i <= 63; ++i) 216 | { 217 | proxy = i; 218 | verify_int(proxy, i); 219 | } 220 | } 221 | SECTION("arithmetic operation") 222 | { 223 | proxy += 13; 224 | verify_int(proxy, 13); 225 | 226 | proxy -= 17; 227 | verify_int(proxy, -4); 228 | 229 | proxy *= -4; 230 | verify_int(proxy, 16); 231 | 232 | proxy /= 3; 233 | verify_int(proxy, 5); 234 | 235 | proxy %= 2; 236 | verify_int(proxy, 1); 237 | 238 | auto value = proxy++; 239 | verify_int(proxy, 2); 240 | REQUIRE(value == 1); 241 | 242 | value = ++proxy; 243 | verify_int(proxy, 3); 244 | REQUIRE(value == 3); 245 | 246 | value = proxy--; 247 | verify_int(proxy, 2); 248 | REQUIRE(value == 3); 249 | 250 | value = --proxy; 251 | verify_int(proxy, 1); 252 | REQUIRE(value == 1); 253 | } 254 | } 255 | 256 | TEST_CASE("tiny_int_range") 257 | { 258 | SECTION("signed") 259 | { 260 | using type = tiny_int_range<-10, 10>; 261 | tiny_storage storage = 0; 262 | 263 | auto cproxy = make_cproxy(storage); 264 | verify_int(cproxy, -10); 265 | 266 | auto proxy = make_proxy(storage); 267 | verify_int(proxy, -10); 268 | 269 | SECTION("all values") 270 | { 271 | for (auto i = -10; i <= 10; ++i) 272 | { 273 | proxy = i; 274 | verify_int(proxy, i); 275 | } 276 | } 277 | SECTION("arithmetic operation") 278 | { 279 | proxy += 13; 280 | verify_int(proxy, 3); 281 | 282 | proxy -= 7; 283 | verify_int(proxy, -4); 284 | 285 | proxy *= -2; 286 | verify_int(proxy, 8); 287 | 288 | proxy /= 3; 289 | verify_int(proxy, 2); 290 | 291 | proxy %= 2; 292 | verify_int(proxy, 0); 293 | 294 | auto value = proxy++; 295 | verify_int(proxy, 1); 296 | REQUIRE(value == 0); 297 | 298 | value = ++proxy; 299 | verify_int(proxy, 2); 300 | REQUIRE(value == 2); 301 | 302 | value = proxy--; 303 | verify_int(proxy, 1); 304 | REQUIRE(value == 2); 305 | 306 | value = --proxy; 307 | verify_int(proxy, 0); 308 | REQUIRE(value == 0); 309 | } 310 | } 311 | SECTION("unsigned") 312 | { 313 | using type = tiny_int_range<10, 100, unsigned>; 314 | tiny_storage storage = 0; 315 | 316 | auto cproxy = make_cproxy(storage); 317 | verify_unsigned(cproxy, 10u); 318 | 319 | auto proxy = make_proxy(storage); 320 | verify_unsigned(proxy, 10u); 321 | 322 | SECTION("all values") 323 | { 324 | for (auto i = 10u; i <= 100; ++i) 325 | { 326 | proxy = i; 327 | verify_unsigned(proxy, i); 328 | } 329 | } 330 | SECTION("arithmetic operation") 331 | { 332 | proxy += 43; 333 | verify_unsigned(proxy, 53u); 334 | 335 | proxy -= 40; 336 | verify_unsigned(proxy, 13u); 337 | 338 | proxy *= 7; 339 | verify_unsigned(proxy, 91u); 340 | 341 | proxy /= 3; 342 | verify_unsigned(proxy, 30u); 343 | 344 | proxy %= 19; 345 | verify_unsigned(proxy, 11u); 346 | 347 | auto value = proxy++; 348 | verify_unsigned(proxy, 12u); 349 | REQUIRE(value == 11u); 350 | 351 | value = ++proxy; 352 | verify_unsigned(proxy, 13u); 353 | REQUIRE(value == 13u); 354 | 355 | value = proxy--; 356 | verify_unsigned(proxy, 12u); 357 | REQUIRE(value == 13u); 358 | 359 | value = --proxy; 360 | verify_unsigned(proxy, 11u); 361 | REQUIRE(value == 11u); 362 | } 363 | } 364 | } 365 | 366 | namespace 367 | { 368 | enum class test_flags 369 | { 370 | a, 371 | b, 372 | c, 373 | 374 | flag_count_, 375 | }; 376 | 377 | template 378 | void verify_flag_set(Proxy proxy, bool a_set, bool b_set, bool c_set) 379 | { 380 | REQUIRE(proxy[test_flags::a] == a_set); 381 | REQUIRE(proxy[test_flags::b] == b_set); 382 | REQUIRE(proxy[test_flags::c] == c_set); 383 | 384 | REQUIRE(proxy.get() == std::uintmax_t((c_set << 2) | (b_set << 1) | (a_set << 0))); 385 | 386 | REQUIRE(proxy.is_set(test_flags::a) == a_set); 387 | REQUIRE(proxy.is_set(test_flags::b) == b_set); 388 | REQUIRE(proxy.is_set(test_flags::c) == c_set); 389 | 390 | REQUIRE(proxy.any() == (a_set || b_set || c_set)); 391 | REQUIRE(proxy.all() == (a_set && b_set && c_set)); 392 | REQUIRE(proxy.none() == (!a_set && !b_set && !c_set)); 393 | 394 | REQUIRE(proxy == proxy); 395 | REQUIRE_FALSE(proxy != proxy); 396 | 397 | if (proxy.any()) 398 | { 399 | REQUIRE(proxy != flags()); 400 | REQUIRE(flags() != proxy); 401 | REQUIRE_FALSE(proxy == flags()); 402 | REQUIRE_FALSE(flags() == proxy); 403 | } 404 | else 405 | { 406 | REQUIRE(proxy == flags()); 407 | REQUIRE(flags() == proxy); 408 | REQUIRE_FALSE(proxy != flags()); 409 | REQUIRE_FALSE(flags() != proxy); 410 | } 411 | } 412 | } // namespace 413 | 414 | TEST_CASE("tiny_flag_set") 415 | { 416 | tiny_storage storage = 0; 417 | 418 | auto cproxy = make_cproxy>(storage); 419 | verify_flag_set(cproxy, false, false, false); 420 | 421 | auto proxy = make_proxy>(storage); 422 | verify_flag_set(proxy, false, false, false); 423 | 424 | proxy = flags(test_flags::a, test_flags::c); 425 | verify_flag_set(proxy, true, false, true); 426 | 427 | SECTION("single flag operation") 428 | { 429 | proxy[test_flags::b] = true; 430 | verify_flag_set(proxy, true, true, true); 431 | 432 | proxy.reset(test_flags::a); 433 | verify_flag_set(proxy, false, true, true); 434 | 435 | proxy.set(test_flags::a); 436 | verify_flag_set(proxy, true, true, true); 437 | 438 | proxy.toggle(test_flags::a); 439 | verify_flag_set(proxy, false, true, true); 440 | 441 | proxy.toggle(test_flags::a); 442 | verify_flag_set(proxy, true, true, true); 443 | } 444 | SECTION("multi flag operation") 445 | { 446 | proxy.toggle_all(); 447 | verify_flag_set(proxy, false, true, false); 448 | 449 | proxy.toggle_all(); 450 | verify_flag_set(proxy, true, false, true); 451 | 452 | SECTION("set_all") 453 | { 454 | proxy.set_all(); 455 | verify_flag_set(proxy, true, true, true); 456 | } 457 | SECTION("reset_all") 458 | { 459 | proxy.reset_all(); 460 | verify_flag_set(proxy, false, false, false); 461 | } 462 | } 463 | } 464 | -------------------------------------------------------------------------------- /example/tiny_storage.cpp: -------------------------------------------------------------------------------- 1 | // This example demonstrates how to use the tiny types and padding traits. 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include // for tiny::check_size 8 | #include // for tiny::padding_tiny_storage 9 | #include // for tiny::pointer_tiny_storage 10 | #include // for tiny::tiny_bool 11 | #include // for tiny::tiny_enum 12 | #include // for tiny::tiny_int_range 13 | 14 | namespace tiny = foonathan::tiny; 15 | 16 | //=== maybe_owning_ptr ===// 17 | // A smart pointer that sometimes owns the `T` and sometimes it doesn't. 18 | // (Not a complete implementation) 19 | template 20 | class maybe_owning_ptr 21 | { 22 | // Conceptually we need to store 23 | // 24 | // T* ptr_; 25 | // bool should_delete_; 26 | // 27 | // However, due to alignment that would be equivalent to storing two pointers, 28 | // instead of storing a single pointer and one bit. 29 | // 30 | // So instead: 31 | tiny::pointer_tiny_storage storage_; 32 | // This stores a pointer to a `T` and a (tiny) `bool`. 33 | // 34 | // If we have a type with an alignment greater `1`, 35 | // we know that the addresses stored in the pointer will be a multiple of at two. 36 | // So instead of storing `0` in the final bit, we store our `bool` instead. 37 | // 38 | // If we have a type with an alignment of `1`, it will store the `bool` separately. 39 | // 40 | // `tiny::pointer_tiny_storage` could also store multiple tiny types at once. 41 | 42 | public: 43 | explicit maybe_owning_ptr(T* non_owning) noexcept 44 | // don't delete the raw pointer 45 | : storage_(non_owning, false) 46 | {} 47 | 48 | explicit maybe_owning_ptr(std::unique_ptr ptr) noexcept 49 | // do delete the unique ptr 50 | : storage_(ptr.release(), true) 51 | {} 52 | 53 | ~maybe_owning_ptr() noexcept 54 | { 55 | // delete if owning 56 | if (is_owning()) 57 | delete get(); 58 | } 59 | 60 | // for simplicity here 61 | maybe_owning_ptr(const maybe_owning_ptr&) = delete; 62 | maybe_owning_ptr& operator=(const maybe_owning_ptr&) = delete; 63 | 64 | bool is_owning() const noexcept 65 | { 66 | // Whether or not it is owning is stored in the tiny type (as there is only one, it is 67 | // unambiguous). 68 | // 69 | // We could also use `storage_.template at<0>()` (first tiny type), 70 | // or `storage_[tiny::tiny_bool{}]` (we want the tiny boolean). 71 | // 72 | // Important: this returns a proxy type for implementation reasons, not a bool. 73 | // In particular you can't get the address. 74 | return storage_.tiny(); 75 | } 76 | 77 | T* get() const noexcept 78 | { 79 | // Return the pointer. 80 | // This will clear all bits used for storage so we have a valid pointer. 81 | // 82 | // Again, this will return a proxy. 83 | return storage_.pointer(); 84 | } 85 | 86 | T& operator*() const noexcept 87 | { 88 | return *get(); 89 | } 90 | T* operator->() const noexcept 91 | { 92 | return get(); 93 | } 94 | }; 95 | 96 | void use_maybe_owning() 97 | { 98 | std::cout << "=== maybe_owning_ptr ===\n\n"; 99 | 100 | // `tiny::check_size` is just an utility for checking sizes that also shows the actual size if 101 | // it didn't match. 102 | // (try changing the expected size) 103 | static_assert( 104 | tiny::check_size, sizeof(void*)>(), 105 | "std::uint32_t has an alignment of 4, so we can easily fit one bool inside of it"); 106 | static_assert(tiny::check_size, 2 * sizeof(void*)>(), 107 | "char has an alignment of 1, so every address would be valid"); 108 | 109 | std::uint32_t i = 0; 110 | 111 | maybe_owning_ptr non_owning(&i); 112 | std::cout << "Address is: " << non_owning.get() << '\n'; 113 | std::cout << "Is owning? " << std::boolalpha << non_owning.is_owning() << '\n'; 114 | std::cout << '\n'; 115 | 116 | maybe_owning_ptr owning(std::unique_ptr(new std::uint32_t)); 117 | std::cout << "Address is: " << owning.get() << '\n'; 118 | std::cout << "Is owning? " << std::boolalpha << owning.is_owning() << '\n'; 119 | std::cout << '\n'; 120 | 121 | std::cout << '\n'; 122 | } 123 | 124 | //=== gregorian_date ===// 125 | // Months in a year. 126 | enum class month 127 | { 128 | jan, 129 | feb, 130 | mar, 131 | apr, 132 | may, 133 | jun, 134 | jul, 135 | aug, 136 | sep, 137 | oct, 138 | nov, 139 | dec, 140 | 141 | // Informs tiny that this is an enum with enumerators in the range `[0, _unsigned_count)`. 142 | // See `enum_traits.hpp` for details. 143 | _unsigned_count, 144 | }; 145 | 146 | // Suppose we don't care about the year, so we only need to store month + day. 147 | // There are 365.24219 days in a year, so we need 8.51270961388 bits to store every day. 148 | // 149 | // A naive implementation: 150 | // 151 | // month m; 152 | // std::uint8_t day; 153 | // 154 | // This uses `2 * sizeof(int) * CHAR_BIT` (so usually 64 bits), as the underlying type of `month` is 155 | // `int`! 156 | // 157 | // The more space efficient implementation: 158 | class gregorian_day_of_year 159 | { 160 | // We again use tiny types: 161 | // `tiny::tiny_enum` stores the specified enumeration in a space efficient way, 162 | // and `tiny::tiny_int_range` can store the specified integers. 163 | // 164 | // This time we don't have any storage already available, so we use `tiny::tiny_storage`, 165 | // which stores just tiny types. 166 | // 167 | // In this case it needs to store `4` bits for the month and `5` bits for the day, 168 | // so 2 bytes in total. 169 | tiny::tiny_storage, tiny::tiny_int_range<1, 31>> storage_; 170 | 171 | public: 172 | // Simple constructor ignoring validation. 173 | explicit gregorian_day_of_year(::month m, int day) noexcept : storage_(m, day) {} 174 | 175 | ::month month() const noexcept 176 | { 177 | // Again, this actually returns a proxy. 178 | // Alternatively you could use `storage_[tiny::tiny_enum{}]`. 179 | return storage_.at<0>(); 180 | } 181 | 182 | int day() const noexcept 183 | { 184 | // Again, this actually returns a proxy. 185 | // Alternatively you could use `storage_[tiny::tiny_int_range<1, 31>{}]`. 186 | return storage_.at<1>(); 187 | } 188 | 189 | void set(::month m, int day) 190 | { 191 | // Assignment just works and will do a range check 192 | // (but not verify the month and day combination itself obviously). 193 | // 194 | // This time the other access method is used because why not. 195 | // (It is nicer if we have a typedef). 196 | storage_[tiny::tiny_enum<::month>{}] = m; 197 | storage_[tiny::tiny_int_range<1, 31>{}] = day; 198 | // (In case you're wondering, the tiny types themselves are empty, so the default 199 | // constructor does nothing) 200 | } 201 | 202 | // Note that writing access functions is necessary because the tiny storage interface isn't 203 | // ideal. It is just an implementation detail. 204 | 205 | // See below. 206 | friend tiny::padding_traits; 207 | }; 208 | 209 | void use_gregorian_day_of_year() 210 | { 211 | std::cout << "=== gregorian_day_of_year ===\n\n"; 212 | 213 | static_assert(tiny::check_size(), "should use 9 bits"); 214 | 215 | gregorian_day_of_year doy(month::jan, 1); 216 | std::cout << "Is january? " << std::boolalpha << (doy.month() == month::jan) << '\n'; 217 | std::cout << "Day: " << doy.day() << '\n'; 218 | std::cout << '\n'; 219 | 220 | doy.set(month::may, 5); 221 | std::cout << "Is january? " << std::boolalpha << (doy.month() == month::jan) << '\n'; 222 | std::cout << "Day: " << doy.day() << '\n'; 223 | std::cout << '\n'; 224 | 225 | std::cout << "\n"; 226 | } 227 | 228 | // Now we want to store a year as well. 229 | // Having learned nothing from the Y2K, we store the year in the range `[0, 99]` only. 230 | // This requires an additional 7 bit, so we have 16 bits in total. 231 | // 232 | // But the `gregorian_day_of_year` is 2 bytes already, so we need 3 bytes in total, 233 | // even though there only one bit of the second byte is used! 234 | // 235 | // No problem, we just need to tell tiny that we have 7 unused bits. 236 | // We do that by specializing the `tiny::padding_traits`. 237 | namespace foonathan 238 | { 239 | namespace tiny 240 | { 241 | template <> 242 | struct padding_traits 243 | { 244 | // We have a custom specialization. 245 | static constexpr auto is_specialized = true; 246 | 247 | // This function returns a `tiny::bit_view` (a view into a subset of bits), 248 | // for the (possibly disjoint) range of memory we're not using. 249 | // Here we just forward to the `spare_bits()` of our storage. 250 | static auto padding_view(unsigned char* memory) noexcept 251 | -> decltype(std::declval().storage_.spare_bits()) 252 | { 253 | // reinterpret_cast here is always fine. 254 | return reinterpret_cast(memory)->storage_.spare_bits(); 255 | } 256 | static auto padding_view(const unsigned char* memory) noexcept 257 | -> decltype(std::declval().storage_.spare_bits()) 258 | { 259 | return reinterpret_cast(memory)->storage_.spare_bits(); 260 | } 261 | }; 262 | } // namespace tiny 263 | } // namespace foonathan 264 | 265 | // Now we can write the `gregorian_date`. 266 | class gregorian_date 267 | { 268 | // This time we use the padding bits of `gregorian_day_of_year` to store our tiny integer. 269 | // (The class also takes care that the padding bits are initialized and copied properly). 270 | tiny::padding_tiny_storage> storage_; 271 | 272 | public: 273 | explicit gregorian_date(gregorian_day_of_year doy, int year) : storage_(doy, year) {} 274 | 275 | const gregorian_day_of_year& day_of_year() const noexcept 276 | { 277 | // This time we can actually return a true reference, not a proxy. 278 | return storage_.object(); 279 | } 280 | 281 | int year() const noexcept 282 | { 283 | // But the year is a proxy again. 284 | return storage_.tiny(); 285 | } 286 | }; 287 | 288 | void use_gregorian_date() 289 | { 290 | std::cout << "=== gregorian_date ===\n\n"; 291 | 292 | static_assert(tiny::check_size(), "this is ideal!"); 293 | 294 | gregorian_date date(gregorian_day_of_year(month::jan, 1), 70); 295 | std::cout << "Is january? " << std::boolalpha << (date.day_of_year().month() == month::jan) 296 | << '\n'; 297 | std::cout << "Day: " << date.day_of_year().day() << '\n'; 298 | std::cout << "Year: " << date.year() << '\n'; 299 | std::cout << '\n'; 300 | 301 | std::cout << '\n'; 302 | } 303 | 304 | //=== padding_traits for aggregates ===// 305 | // If we have a simple aggregate we can specialize the padding traits a lot easier. 306 | struct some_aggregate 307 | { 308 | bool a; 309 | // 7 bytes padding 310 | std::uint64_t b; 311 | std::uint8_t c; 312 | // 3 bytes padding 313 | std::uint32_t d; 314 | // 4 bytes padding 315 | }; 316 | 317 | namespace foonathan 318 | { 319 | namespace tiny 320 | { 321 | // Just inherit from `tiny::padding_traits_aggregate` and list all members. 322 | // Meta-programming calculates the padding for you. 323 | template <> 324 | struct padding_traits 325 | : tiny::padding_traits_aggregate 328 | {}; 329 | } // namespace tiny 330 | } // namespace foonathan 331 | 332 | void use_padding_aggregate() 333 | { 334 | std::cout << "=== padding_traits aggregate ===\n\n"; 335 | 336 | std::cout << "some_aggregate has padding of: " << tiny::padding_bit_size() 337 | << '\n'; 338 | 339 | std::cout << '\n'; 340 | } 341 | 342 | //=== run all examples ===// 343 | int main() 344 | { 345 | use_maybe_owning(); 346 | use_gregorian_day_of_year(); 347 | use_gregorian_date(); 348 | use_padding_aggregate(); 349 | } 350 | -------------------------------------------------------------------------------- /include/foonathan/tiny/tiny_int.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Jonathan Müller 2 | // This file is subject to the license terms in the LICENSE file 3 | // found in the top-level directory of this distribution. 4 | 5 | #ifndef FOONATHAN_TINY_TINY_INT_HPP_INCLUDED 6 | #define FOONATHAN_TINY_TINY_INT_HPP_INCLUDED 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace foonathan 15 | { 16 | namespace tiny 17 | { 18 | /// A `TinyType` implementation of a small unsigned integer type. 19 | /// 20 | /// The integer type has the specified size in bits and uses the specified type as the object 21 | /// type. Overflow is checked in debug mode whenever the integer is stored. 22 | /// 23 | /// \require `Integer` must be an unsigned integer type with at least `Bits` bits. 24 | template 25 | class tiny_unsigned 26 | { 27 | static_assert(std::is_integral::value && std::is_unsigned::value, 28 | "integer must be unsigned"); 29 | static_assert(Bits <= sizeof(Integer) * CHAR_BIT, "integer type overflow"); 30 | 31 | public: 32 | using object_type = Integer; 33 | 34 | static constexpr std::size_t bit_size() noexcept 35 | { 36 | return Bits; 37 | } 38 | 39 | template 40 | class proxy 41 | { 42 | public: 43 | operator object_type() const noexcept 44 | { 45 | return get(); 46 | } 47 | 48 | const proxy& operator=(object_type value) const noexcept 49 | { 50 | DEBUG_ASSERT((are_only_bits<0, bit_size()>(value)), detail::precondition_handler{}, 51 | "overflow in tiny unsigned"); 52 | view_.put(value); 53 | return *this; 54 | } 55 | 56 | const proxy& operator+=(object_type i) const noexcept 57 | { 58 | return *this = static_cast(get() + i); 59 | } 60 | const proxy& operator-=(object_type i) const noexcept 61 | { 62 | return *this = static_cast(get() - i); 63 | } 64 | const proxy& operator*=(object_type i) const noexcept 65 | { 66 | return *this = static_cast(get() * i); 67 | } 68 | const proxy& operator/=(object_type i) const noexcept 69 | { 70 | return *this = static_cast(get() / i); 71 | } 72 | const proxy& operator%=(object_type i) const noexcept 73 | { 74 | return *this = static_cast(get() % i); 75 | } 76 | 77 | const proxy& operator++() const noexcept 78 | { 79 | return *this += 1; 80 | } 81 | object_type operator++(int) const noexcept 82 | { 83 | auto copy = get(); 84 | ++*this; 85 | return copy; 86 | } 87 | const proxy& operator--() const noexcept 88 | { 89 | return *this -= 1; 90 | } 91 | object_type operator--(int) const noexcept 92 | { 93 | auto copy = get(); 94 | --*this; 95 | return copy; 96 | } 97 | 98 | private: 99 | explicit proxy(BitView view) noexcept : view_(view) {} 100 | 101 | object_type get() const noexcept 102 | { 103 | return static_cast(view_.extract()); 104 | } 105 | 106 | BitView view_; 107 | 108 | friend tiny_type_access; 109 | }; 110 | }; 111 | 112 | /// A `TinyType` implementation of a small signed integer type. 113 | /// 114 | /// The integer type has the specified size in bits and uses the specified type as the object 115 | /// type. Overflow is checked in debug mode whenever the integer is stored. 116 | /// 117 | /// \require `Integer` must be an signed integer type with at least `Bits` bits. 118 | template 119 | class tiny_int 120 | { 121 | static_assert(std::is_integral::value && std::is_signed::value, 122 | "integer must be signed"); 123 | static_assert(Bits <= sizeof(Integer) * CHAR_BIT, "integer type overflow"); 124 | 125 | public: 126 | using object_type = Integer; 127 | 128 | static constexpr std::size_t bit_size() noexcept 129 | { 130 | return Bits; 131 | } 132 | 133 | template 134 | class proxy 135 | { 136 | public: 137 | operator object_type() const noexcept 138 | { 139 | return get(); 140 | } 141 | 142 | const proxy& operator=(object_type value) const noexcept 143 | { 144 | assign(value); 145 | return *this; 146 | } 147 | 148 | const proxy& operator+=(object_type i) const noexcept 149 | { 150 | return *this = static_cast(get() + i); 151 | } 152 | const proxy& operator-=(object_type i) const noexcept 153 | { 154 | return *this = static_cast(get() - i); 155 | } 156 | const proxy& operator*=(object_type i) const noexcept 157 | { 158 | return *this = static_cast(get() * i); 159 | } 160 | const proxy& operator/=(object_type i) const noexcept 161 | { 162 | return *this = static_cast(get() / i); 163 | } 164 | const proxy& operator%=(object_type i) const noexcept 165 | { 166 | return *this = static_cast(get() % i); 167 | } 168 | 169 | const proxy& operator++() const noexcept 170 | { 171 | return *this += 1u; 172 | } 173 | object_type operator++(int) const noexcept 174 | { 175 | auto copy = get(); 176 | ++*this; 177 | return copy; 178 | } 179 | const proxy& operator--() const noexcept 180 | { 181 | return *this -= 1u; 182 | } 183 | object_type operator--(int) const noexcept 184 | { 185 | auto copy = get(); 186 | --*this; 187 | return copy; 188 | } 189 | 190 | private: 191 | explicit proxy(BitView view) noexcept : view_(view) {} 192 | 193 | // note: assuming two's complement representation here 194 | using unsigned_type = typename std::make_unsigned::type; 195 | 196 | object_type get() const noexcept 197 | { 198 | // look at the most significant bit, which determines the sign 199 | if (!view_[Bits - 1]) 200 | // bit not set, positive value 201 | // so just return that 202 | return static_cast(view_.extract()); 203 | else 204 | { 205 | // bit set, negative value 206 | auto rep = static_cast(view_.extract()); 207 | 208 | // take complement and add one to get the absolute value 209 | // and clear any overflow bits 210 | auto absolute_value = static_cast(~rep + 1); 211 | // clear any overflow bits 212 | absolute_value = clear_other_bits<0, Bits>(absolute_value); 213 | // return absolute value properly negated 214 | return static_cast(-static_cast(absolute_value)); 215 | } 216 | } 217 | 218 | static constexpr auto min = object_type(-(1ll << (Bits - 1))); 219 | static constexpr auto max = object_type((1ll << (Bits - 1)) - 1); 220 | 221 | void assign(object_type value) const noexcept 222 | { 223 | // can't do an overflow check by looking at the bits, 224 | // as negative values have ones in the higher bits 225 | DEBUG_ASSERT(min <= value, detail::precondition_handler{}, "overflow in tiny int"); 226 | DEBUG_ASSERT(value <= max, detail::precondition_handler{}, "overflow in tiny int"); 227 | 228 | // we can however, simply store the `Bits` lower bits, 229 | // as truncation will preserve signed-ness 230 | auto rep = reinterpret_cast(value); 231 | view_.put(static_cast(rep)); 232 | } 233 | 234 | BitView view_; 235 | 236 | friend tiny_type_access; 237 | }; 238 | }; 239 | 240 | /// \exclude 241 | namespace tiny_int_detail 242 | { 243 | template 244 | constexpr std::size_t bits_for() noexcept 245 | { 246 | static_assert(Min <= Max, "invalid range"); 247 | return detail::ilog2_ceil(static_cast(Max - Min)); 248 | } 249 | } // namespace tiny_int_detail 250 | 251 | /// An integer that can hold the values in the range `[Min, Max]`. 252 | /// 253 | /// It internally stores the offset from `Min`. 254 | /// This means that the all bits zero value corresponds to `Min`, and not `0` like the other 255 | /// integers. 256 | template 257 | class tiny_int_range 258 | { 259 | static_assert(std::is_integral::value, "must be an integer"); 260 | static_assert(std::numeric_limits::min() <= Min 261 | && Max <= std::numeric_limits::max(), 262 | "integer can't handle range"); 263 | 264 | public: 265 | using object_type = Integer; 266 | 267 | static constexpr std::size_t bit_size() noexcept 268 | { 269 | return tiny_int_detail::bits_for(); 270 | } 271 | 272 | template 273 | class proxy 274 | { 275 | public: 276 | operator object_type() const noexcept 277 | { 278 | return get(); 279 | } 280 | 281 | const proxy& operator=(object_type value) const noexcept 282 | { 283 | DEBUG_ASSERT(Min <= value, detail::precondition_handler{}, 284 | "overflow in tiny_int_range"); 285 | DEBUG_ASSERT(value <= Max, detail::precondition_handler{}, 286 | "overflow in tiny_int_range"); 287 | auto offset = std::intmax_t(value) - Min; 288 | DEBUG_ASSERT(offset >= 0, detail::assert_handler{}); 289 | view_.put(static_cast(offset)); 290 | return *this; 291 | } 292 | 293 | const proxy& operator+=(object_type i) const noexcept 294 | { 295 | return *this = static_cast(get() + i); 296 | } 297 | const proxy& operator-=(object_type i) const noexcept 298 | { 299 | return *this = static_cast(get() - i); 300 | } 301 | const proxy& operator*=(object_type i) const noexcept 302 | { 303 | return *this = static_cast(get() * i); 304 | } 305 | const proxy& operator/=(object_type i) const noexcept 306 | { 307 | return *this = static_cast(get() / i); 308 | } 309 | const proxy& operator%=(object_type i) const noexcept 310 | { 311 | return *this = static_cast(get() % i); 312 | } 313 | 314 | const proxy& operator++() const noexcept 315 | { 316 | return *this += 1; 317 | } 318 | object_type operator++(int) const noexcept 319 | { 320 | auto copy = get(); 321 | ++*this; 322 | return copy; 323 | } 324 | const proxy& operator--() const noexcept 325 | { 326 | return *this -= 1; 327 | } 328 | object_type operator--(int) const noexcept 329 | { 330 | auto copy = get(); 331 | --*this; 332 | return copy; 333 | } 334 | 335 | private: 336 | explicit proxy(BitView view) noexcept : view_(view) {} 337 | 338 | object_type get() const noexcept 339 | { 340 | return static_cast(std::intmax_t(view_.extract()) + Min); 341 | } 342 | 343 | BitView view_; 344 | 345 | friend tiny_type_access; 346 | }; 347 | }; 348 | } // namespace tiny 349 | } // namespace foonathan 350 | 351 | #endif // FOONATHAN_TINY_TINY_INT_HPP_INCLUDED 352 | --------------------------------------------------------------------------------