├── .gitignore ├── .gitattributes ├── LICENSE ├── CMakeLists.txt ├── README.md ├── example └── main.cpp ├── tests └── test.cpp └── include └── pod_reflection └── pod_reflection.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ElDesalmado 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11 FATAL_ERROR) 2 | 3 | project(pod_reflection) 4 | 5 | option(BUILD_EXAMPLE "Compile pod_reflection example?" ON) 6 | 7 | get_directory_property(hasParent PARENT_DIRECTORY) 8 | 9 | if (hasParent) 10 | set(BUILD_EXAMPLE OFF) 11 | endif (hasParent) 12 | 13 | add_library(pod_reflection INTERFACE) 14 | 15 | target_include_directories(pod_reflection INTERFACE include/ 16 | ) 17 | 18 | add_library(eld::pod_reflection ALIAS pod_reflection) 19 | 20 | if (BUILD_EXAMPLE) 21 | 22 | add_executable(sb.reflection_11 example/main.cpp) 23 | target_link_libraries(sb.reflection_11 PRIVATE pod_reflection) 24 | set_target_properties(sb.reflection_11 25 | PROPERTIES 26 | CXX_STANDARD 11 27 | CXX_STANDARD_REQUIRED TRUE 28 | ) 29 | 30 | add_executable(sb.reflection_14 example/main.cpp) 31 | target_link_libraries(sb.reflection_14 PRIVATE pod_reflection) 32 | set_target_properties(sb.reflection_14 33 | PROPERTIES 34 | CXX_STANDARD 14 35 | CXX_STANDARD_REQUIRED TRUE 36 | ) 37 | 38 | add_executable(test.reflection_11 tests/test.cpp) 39 | target_link_libraries(test.reflection_11 PRIVATE pod_reflection) 40 | set_target_properties(test.reflection_11 41 | PROPERTIES 42 | CXX_STANDARD 11 43 | CXX_STANDARD_REQUIRED TRUE 44 | ) 45 | 46 | add_executable(test.reflection_14 tests/test.cpp) 47 | target_link_libraries(test.reflection_14 PRIVATE pod_reflection) 48 | set_target_properties(test.reflection_14 49 | PROPERTIES 50 | CXX_STANDARD 14 51 | CXX_STANDARD_REQUIRED TRUE 52 | ) 53 | 54 | endif (BUILD_EXAMPLE) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pod_reflection 2 | This is a simple C++ one-header STL based library implementing reflection for POD data types. 3 | 4 | ## Limitations: 5 | C++ itself does not provide adequate means for reflection, so this library's 6 | implementation is based around various template metaprogramming tools and hacks. 7 | Though this code is written according to the standard, there are some C++ features 8 | that make it impossible to use this library. Here is the list of known limitations: 9 | - **Structs with default values for elements:** in C++ 11 structure is not considered as a POD in case it has default initializations 10 | for any of its elements. In C++ 14 a structure is allowed to have default values for elements 11 | and to remain a POD. 12 | - **Structs with bitfields:** It is impossible to get an address of a bitfield member of class in C++. 13 | Accessing a first bitfield element of an addressable location is possible, 14 | but is still considered UB. So it is restricted to use (no valid results will be provided) 15 | `eld::get` and `eld::for_each` on PODS that contain bitfields. 16 | - **Structs with fixed size arrays**: it seems to be impossible to deduce that an element is 17 | a fixed size array because arrays can only be initialized only with brace initialization. 18 | Thus an attempt to deduce an element will yield a type of an array element. 19 | **Structs with fixed size array elements are prohibited from usage with 20 | `eld::get`, `eld::for_each`** 21 | 22 | 23 | 24 | ## Public API: 25 | The following public functions are implemented within the library: 26 | *include/pod_reflection/pod_reflection.h* - new header to be used. 27 | 28 | - `eld::pod_size` - get size of a POD structure at compile time 29 | - `eld::pod_element_t` - get a type of an Ith element of a POD 30 | structure. Tuple with feed types `TupleFeed` is required to deduce the Ith element type. 31 | - `deduced& get(POD& pod)` - extract the Ith element of POD using `TupleFeed`. 32 | This function requires testing for a particular compiler since it utilizes UB (`reinterpret_cast` from an offset).- 33 | - `int eld::for_each(POD& pod, Callable &&callable)` - one of the most useful 34 | functions in practice. For each element in POD object calls a callable object. The most obvious use case 35 | as can be seen from example - is to log (print) the contents of a POD structure. 36 | The most useful case which this library can be used - **automatic endian conversion** for each 37 | of the POD's element. 38 | 39 | 40 | ### Example usage: 41 | See *example/main.cpp*. 42 | In your CMakeLists.txt declare: `target_link_libraries(YourTarget PRIVATE eld::pod_reflection)` 43 | 44 | 45 | ### Contributing: 46 | This library requires automated testing with CI integration. Any help is appreciated. -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct abc 8 | { 9 | int a; 10 | float b; 11 | char c, 12 | d; 13 | int e; 14 | double f; 15 | std::string g; 16 | }; 17 | 18 | struct dce 19 | { 20 | int a; 21 | double b; 22 | abc c; 23 | }; 24 | 25 | struct none 26 | { 27 | }; 28 | 29 | // This might be an endian converting class! 30 | struct Printer 31 | { 32 | template 33 | void operator()(const T &t) 34 | { 35 | std::cout << t << " "; 36 | } 37 | }; 38 | 39 | template 40 | using array_i = std::array; 41 | 42 | enum class Enoom : uint8_t 43 | { 44 | foo = 42 45 | }; 46 | 47 | struct with_enum 48 | { 49 | Enoom enoom; 50 | }; 51 | 52 | template 53 | using is_fundamental = std::is_fundamental; 54 | 55 | int main() 56 | { 57 | std::cout << "struct abc has " << eld::pod_size() << " elements" << std::endl; 58 | std::cout << "struct dce has " << eld::pod_size() << " elements" << std::endl; 59 | std::cout << "struct none has " << eld::pod_size() << " elements" << std::endl; 60 | 61 | using TupleFeed = eld::extend_feed; 62 | 63 | if (std::is_same>::value) 64 | std::cout << "6th element in struct abc is std::string" << std::endl; 65 | 66 | abc toPrint{4, 8, 'o', 'c', 15, 16.2342, "eanic"}; 67 | 68 | 69 | eld::for_each(toPrint, Printer()); 70 | std::cout << std::endl; 71 | none n{}; 72 | eld::for_each(n, Printer()); 73 | eld::detail::make_index_sequence<0> zeroSeq{}; 74 | 75 | ////////////////////////////////////////////////////////// 76 | // calculating offsets! 77 | ////////////////////////////////////////////////////////// 78 | 79 | // empty offset 80 | // constexpr auto emptyArray0 = eld::detail::get_pod_offsets(array_i::value>(), 81 | // eld::detail::is_equal<0, eld::pod_size::value>()); 82 | // static_assert(std::is_same>(), ""); 83 | // 84 | // constexpr auto emptyArray1 = eld::detail::get_pod_offsets(); 85 | // static_assert(std::is_same>(), ""); 86 | // 87 | // static_assert(std::is_same, eld::detail::index_sequence<>>::value, ""); 88 | // 89 | // constexpr auto abcOffsets = eld::detail::get_pod_offsets(); 90 | 91 | 92 | using expected_res_array_t = const std::array::value>; 93 | // static_assert(std::is_same(), "Offsets arrays types mismatch!"); 94 | 95 | 96 | decltype(with_enum{eld::detail::explicitly_convertible()}) s; 97 | (void) s; 98 | static_assert(std::is_same::type, 99 | eld::pod_element_t<0, with_enum, TupleFeed, eld::ignore_enums_t>>(), ""); 100 | 101 | 102 | constexpr auto withEnumOffsets = eld::detail::get_pod_offsets(); 103 | static_assert(withEnumOffsets[0] == 0, ""); 104 | 105 | static_assert(eld::is_valid_pod(), ""); 106 | 107 | with_enum withEnum{Enoom::foo}; 108 | 109 | eld::for_each(withEnum, [](std::underlying_type::type v) 110 | { 111 | std::cout << +v << std::endl; 112 | }, eld::ignore_enums); 113 | 114 | eld::detail::filter{}; 115 | 116 | static_assert(std::is_same, eld::detail::append_if_t, int, std::is_fundamental>>(), 117 | ""); 118 | eld::detail::append_if, int, std::is_fundamental>{}; 119 | 120 | using tuple_list_t = std::tuple; 121 | using tuple_found_expected_t = std::tuple; 122 | using tuple_filtered_t = eld::detail::filter_t; 123 | 124 | static_assert(std::is_same(), ""); 125 | 126 | static_assert(std::is_empty>(), ""); 127 | 128 | using found_int_t = eld::detail::find_first_t; 129 | static_assert(std::is_same(), ""); 130 | 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /tests/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | struct abc 6 | { 7 | int a;// = 4; 8 | float b;// = 8.15f; //vs 17 express does not allow implicit conversion from double 9 | char c,// = 'c', 10 | d;// = 'd'; 11 | int e;// = 16; 12 | double f;// = 23.42; 13 | std::string g;// = "oceanic"; 14 | }; 15 | 16 | struct dce 17 | { 18 | int a;// = 4; 19 | double b;// = 3.18; 20 | abc c;//{}; 21 | }; 22 | 23 | using namespace eld; 24 | 25 | #include 26 | 27 | struct none 28 | { 29 | none(void *) 30 | {} 31 | }; 32 | 33 | #pragma pack(push, 1) 34 | struct packed 35 | { 36 | char a; 37 | int b; 38 | }; 39 | #pragma pack(pop) 40 | 41 | struct invalid_pod 42 | { 43 | int a : 2; 44 | int b : 2; 45 | char c; 46 | }; 47 | 48 | struct WithArray 49 | { 50 | int a; 51 | int b[24]; 52 | int c; 53 | }; 54 | 55 | struct S { 56 | int x; 57 | struct Foo { 58 | int i; 59 | int j; 60 | int a[3]; 61 | } b; 62 | }; 63 | 64 | int main() 65 | { 66 | std::cout << "struct abc has " << eld::pod_size() << " elements" << std::endl; 67 | std::cout << "struct dce has " << eld::pod_size() << " elements" << std::endl; 68 | std::cout << "struct none has " << eld::pod_size() << " elements" << std::endl; 69 | 70 | std::cout << eld::detail::count_args(eld::detail::is_aggregate_initialisable()) << 71 | std::endl; 72 | std::cout << eld::detail::count_args(eld::detail::is_aggregate_initialisable()) << 73 | std::endl; 74 | std::cout << eld::pod_size() << std::endl; 75 | std::cout << eld::pod_size() << std::endl; 76 | 77 | 78 | detail::make_index_sequence<5> sdgb; 79 | (void)sdgb; 80 | 81 | decltype(abc{}) abc1; 82 | decltype(abc{detail::implicitly_convertible()}) abc2; 83 | 84 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 85 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 86 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 87 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 88 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 89 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 90 | static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 91 | static_assert(detail::is_aggregate_initialisable(), 92 | "Unexpected"); 93 | // static_assert(detail::is_aggregate_initialisable(), "Unexpected"); 94 | 95 | static_assert( 96 | detail::is_aggregate_initialisable(), 97 | "Unexpected"); 98 | 99 | static_assert(eld::pod_size() == 7, "Invalid arguments number!"); 100 | static_assert(eld::pod_size() == 0, "Invalid arguments number!"); 101 | 102 | 103 | static_assert(std::is_same()})>::value, ""); 104 | static_assert(eld::detail::is_pod_member_initialisable_from_t(), 105 | "Unexpected result"); 106 | static_assert(eld::detail::is_pod_member_initialisable_from_t(), 107 | "Unexpected result"); 108 | static_assert(eld::detail::is_pod_member_initialisable_from_t(), 109 | "Unexpected result"); 110 | 111 | static_assert(!eld::detail::is_pod_member_initialisable_from_t(), 112 | "Unexpected result"); 113 | 114 | using TupleFeedAbc = eld::extend_feed;//std::tuple; 115 | 116 | // successful search 117 | 118 | eld::detail::pod_element_type<0, abc, TupleFeedAbc>::found_types f{}; 119 | 120 | using found_int_t = eld::pod_element_t<0, abc, TupleFeedAbc>; 121 | static_assert(std::is_same(), ""); 122 | 123 | static_assert(std::is_same>>::value, 124 | ""); 125 | static_assert(std::is_same>>::value, 126 | ""); 127 | 128 | static_assert(std::is_same>::value, "Failed to deduce std::string in abc"); 130 | 131 | constexpr auto offsetsAbc = eld::detail::get_pod_offsets(); 132 | constexpr auto offsetA = offsetsAbc[0]; 133 | static_assert(offsetA == offsetof(abc, a), "Invalid offset for int a"); 134 | 135 | constexpr eld::detail::const_array constArray{4, 8, 15, 16, 23, 42}; 136 | constexpr auto elemV = constArray[3]; 137 | 138 | constexpr auto offsetB = offsetsAbc[1]; 139 | static_assert(offsetsAbc[1] == offsetof(abc, b), "Invalid offset for float b"); 140 | 141 | static_assert(eld::detail::pod_elem_offset<0, abc, TupleFeedAbc>::value() == 142 | offsetof(abc, a), "Invalid offset for int a"); 143 | static_assert(eld::detail::pod_elem_offset<1, abc, TupleFeedAbc>::value() == 144 | offsetof(abc, b), "Invalid offset for float b"); 145 | static_assert(eld::detail::pod_elem_offset<2, abc, TupleFeedAbc>::value() == 146 | offsetof(abc, c), "Invalid offset for char c"); 147 | static_assert(eld::detail::pod_elem_offset<3, abc, TupleFeedAbc>::value() == 148 | offsetof(abc, d), "Invalid offset for char d"); 149 | constexpr size_t offset_char_d = eld::detail::pod_elem_offset<3, abc, TupleFeedAbc>::value(); 150 | size_t offset_int_e = eld::detail::pod_elem_offset<4, abc, TupleFeedAbc>::value(); 151 | 152 | bool offset_remainder = !((offset_char_d + sizeof(char)) % alignof(abc)); 153 | size_t align_abc = 4;//alignof(abc); 154 | size_t remainder = (offset_char_d + sizeof(char)) % align_abc; 155 | size_t remaining_bytes = align_abc - remainder; 156 | bool int_fits = remaining_bytes >= sizeof(int); 157 | size_t int_offset = offset_char_d + sizeof(char) + 158 | align_abc - (offset_char_d + sizeof(char)) % align_abc; 159 | 160 | static_assert(eld::detail::pod_elem_offset<4, abc, TupleFeedAbc>::value() == 161 | offsetof(abc, e), "Invalid offset for int e"); 162 | static_assert(eld::detail::pod_elem_offset<5, abc, TupleFeedAbc>::value() == 163 | offsetof(abc, f), "Invalid offset for double f"); 164 | static_assert(eld::detail::pod_elem_offset<6, abc, TupleFeedAbc>::value() == 165 | offsetof(abc, g), "Invalid offset for std::string g"); 166 | 167 | static_assert(eld::detail::pod_elem_offset<1, packed, TupleFeedAbc>::value() == 168 | offsetof(packed, b), "Invalid offset for int b"); 169 | 170 | struct not_packed 171 | { 172 | char a; 173 | int b; 174 | }; 175 | static_assert(eld::detail::pod_elem_offset<1, not_packed, TupleFeedAbc>::value() == 176 | offsetof(not_packed, b), "Invalid offset for int b"); 177 | 178 | eld::detail::pod_elem_offset<1, abc, TupleFeedAbc>::value(); 179 | eld::detail::pod_elem_offset<2, abc, TupleFeedAbc>::value(); 180 | size_t offset_double_f = offsetof(abc, f), 181 | offset_of_double_f_calc = eld::detail::pod_elem_offset<5, abc, TupleFeedAbc>::value(), 182 | offset_string_g = offsetof(abc, g), 183 | offset_of_string_g_calc = eld::detail::pod_elem_offset<6, abc, TupleFeedAbc>::value(); 184 | 185 | abc abcTest{}; 186 | abcTest.e = 16; 187 | eld::get<4, TupleFeedAbc>(abcTest) = 24; 188 | 189 | struct printable_pod 190 | { 191 | int a; 192 | float b; 193 | std::string d; 194 | }; 195 | 196 | struct printing 197 | { 198 | void operator()(int i) 199 | { 200 | std::cout << "int = " << i << std::endl; 201 | } 202 | 203 | void operator()(float f) 204 | { 205 | std::cout << "float = " << f << std::endl; 206 | } 207 | 208 | void operator()(const std::string &str) 209 | { 210 | std::cout << "std::string = " << str << std::endl; 211 | } 212 | 213 | }; 214 | 215 | printable_pod printablePod{4, 8.1516f, "23 42"}; 216 | 217 | size_t count = eld::for_each(printablePod, printing()); 218 | 219 | 220 | sizeof(std::string); 221 | 222 | static_assert(sizeof(abc) == eld::detail::evaluated_pod_size(), 223 | "abc evaluated size invalid"); 224 | static_assert(sizeof(packed) == eld::detail::evaluated_pod_size(), 225 | "packed evaluated size invalid"); 226 | 227 | // test tail, size must be 8 228 | struct dummy 229 | { 230 | int a; 231 | char c; 232 | std::string d; 233 | }; 234 | 235 | static_assert(sizeof(dummy) == eld::detail::evaluated_pod_size(), 236 | ""); 237 | 238 | using tuple_from_dummy = eld::pod_to_tuple_t; 239 | static_assert(std::is_same, 240 | tuple_from_dummy>(), "pod_to_tuple_t failed"); 241 | 242 | sizeof(dummy); 243 | 244 | size_t sizePrintablePod = 245 | eld::detail::evaluated_pod_size(), 246 | sizeInvalidPod = 247 | eld::detail::evaluated_pod_size(), 248 | sizeDummy = eld::detail::evaluated_pod_size(); 249 | 250 | return 0; 251 | } 252 | -------------------------------------------------------------------------------- /include/pod_reflection/pod_reflection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace eld 10 | { 11 | /*! 12 | * Tag for a type that couldn't be deduced from provided tuple feed 13 | */ 14 | struct undeduced 15 | { 16 | }; 17 | 18 | // TODO: make tag ambiguous ? 19 | 20 | // TODO: add pointers? 21 | using basic_feed = std::tuple; 45 | 46 | // TODO: filter duplicates 47 | template 48 | using extend_feed = decltype(std::tuple_cat(basic_feed(), 49 | std::tuple()...)); 50 | 51 | constexpr size_t invalid_offset = std::numeric_limits::max(); 52 | 53 | struct ignore_enums_t 54 | { 55 | }; 56 | 57 | constexpr ignore_enums_t ignore_enums{}; 58 | 59 | namespace detail 60 | { 61 | 62 | template 63 | struct tag_s 64 | { 65 | }; 66 | 67 | template struct make_void 68 | { 69 | typedef void type; 70 | }; 71 | template using void_t = typename make_void::type; 72 | 73 | /*! 74 | * \TODO: description 75 | */ 76 | struct implicitly_convertible 77 | { 78 | template 79 | constexpr operator Any() const noexcept; 80 | }; 81 | 82 | template 83 | using implicitly_convertible_s = implicitly_convertible; 84 | 85 | // stop case 86 | template 87 | constexpr size_t count_args(void_t<>) 88 | { 89 | return sizeof...(T) - 1; 90 | } 91 | 92 | template 93 | constexpr size_t count_args(void_t) 94 | { 95 | return count_args( 96 | void_t()); 97 | } 98 | 99 | template::value> 100 | struct unwrap_enum 101 | { 102 | using type = T; 103 | }; 104 | 105 | template 106 | struct unwrap_enum 107 | { 108 | using type = typename std::underlying_type::type; 109 | }; 110 | 111 | template 112 | using unwrap_enum_t = typename unwrap_enum::type; 113 | 114 | /*! 115 | * \TODO: description 116 | * @tparam Allowed 117 | */ 118 | template 119 | struct explicitly_convertible 120 | { 121 | template::value>::type> 123 | constexpr operator To() const noexcept; 124 | }; 125 | 126 | template 127 | struct explicitly_convertible 128 | { 129 | template, Allowed>::value>::type> 131 | constexpr operator To() const noexcept; 132 | }; 133 | 134 | // index sequence only 135 | template 136 | struct index_sequence 137 | { 138 | }; 139 | 140 | template 141 | struct index_sequence_helper : public index_sequence_helper 142 | { 143 | }; 144 | 145 | template 146 | struct index_sequence_helper<0U, Next ...> 147 | { 148 | using type = index_sequence; 149 | }; 150 | 151 | template 152 | using make_index_sequence = typename index_sequence_helper::type; 153 | 154 | /** 155 | * Check if T is aggregate initialisable from given types. 156 | * Yields True if T{From()...} is well-formed 157 | * @tparam T Type for aggregate initialisation 158 | * @tparam From Types 159 | */ 160 | template 161 | struct is_aggregate_initialisable_ : std::false_type 162 | { 163 | }; 164 | 165 | #ifdef __GNUC__ 166 | #pragma GCC diagnostic push 167 | #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 168 | #endif 169 | 170 | /** 171 | * Check if T is aggregate initialisable from given types. 172 | * Yields True if T{From()...} is well-formed 173 | * @tparam T Type for aggregate initialisation 174 | * @tparam From Types 175 | */ 176 | template 177 | struct is_aggregate_initialisable_< 178 | void_t()}...})>, 179 | T, 180 | From...> : std::true_type 181 | { 182 | }; 183 | 184 | /** 185 | * Check if T is aggregate initialisable from given types. 186 | * Yields True if T{From()...} is well-formed 187 | * @tparam T Type for aggregate initialisation 188 | * @tparam From Types 189 | */ 190 | template 191 | using is_aggregate_initialisable = is_aggregate_initialisable_, T, From...>; 192 | 193 | 194 | // stop case 195 | template 196 | constexpr size_t count_args(std::false_type) 197 | { 198 | return sizeof...(Args) ? sizeof...(Args) - 1 : 0; 199 | } 200 | 201 | // general recursion 202 | template 203 | constexpr size_t count_args(std::true_type) 204 | { 205 | return count_args(is_aggregate_initialisable()); 207 | } 208 | } 209 | 210 | // TODO: write tests for pod_size 211 | /*! 212 | * Provides access to the number of elements in a POD type as a compile-time constant expression 213 | * @tparam POD 214 | */ 215 | template 216 | struct pod_size : public std::integral_constant(detail::is_aggregate_initialisable())> 218 | { 219 | }; 220 | 221 | namespace detail 222 | { 223 | 224 | /** 225 | * Helper class to check if POD member is initialisable from T 226 | * @tparam POD 227 | * @tparam T 228 | * @tparam PODMemberIndex 229 | * @todo finish documentation 230 | */ 231 | template> 235 | struct is_pod_member_initialisable_from_t : std::false_type 236 | { 237 | }; 238 | 239 | /** 240 | * Helper class to check if POD member is initialisable from T 241 | * @tparam POD 242 | * @tparam T 243 | * @tparam PODMemberIndex 244 | * @todo finish documentation 245 | */ 246 | template 248 | struct is_pod_member_initialisable_from_t()..., explicitly_convertible()})>, 253 | index_sequence> : std::true_type 254 | { 255 | }; 256 | 257 | /** 258 | * Helper class to check if POD member is initialisable from T. This specialization ignores enums. 259 | * @tparam POD 260 | * @tparam T 261 | * @tparam PODMemberIndex 262 | * @todo finish documentation 263 | */ 264 | template 266 | struct is_pod_member_initialisable_from_t()..., 271 | explicitly_convertible()})>, 272 | index_sequence> : std::true_type 273 | { 274 | }; 275 | 276 | template 277 | struct const_array 278 | { 279 | constexpr T operator[](size_t index) const 280 | { 281 | return data[index]; 282 | } 283 | 284 | constexpr T back() const 285 | { 286 | return data[N - 1]; 287 | } 288 | 289 | constexpr size_t size() const 290 | { 291 | return N; 292 | } 293 | 294 | const T data[N]; 295 | }; 296 | 297 | // workaround for zero-sized c-arrays 298 | template 299 | struct const_array 300 | { 301 | constexpr T operator[](size_t index) const 302 | { 303 | return T();// data[index]; 304 | } 305 | 306 | constexpr T back() const 307 | { 308 | return T(); 309 | } 310 | 311 | constexpr size_t size() const 312 | { 313 | return 0; 314 | } 315 | 316 | const T *data = nullptr; 317 | }; 318 | 319 | template 320 | constexpr const_array 321 | combine(const const_array &array, const T &val, index_sequence) 322 | { 323 | return {array[Indx]..., val}; 324 | } 325 | 326 | template 327 | constexpr const_array combine(const const_array &array, const T &value) 328 | { 329 | return combine(array, value, make_index_sequence()); 330 | } 331 | 332 | template class /*SFINAEPredicate*/> 333 | struct append_if; 334 | 335 | template class SFINAEPredicate, typename ... Types> 336 | struct append_if, T, SFINAEPredicate> 337 | { 338 | using type = typename std::conditional::value, 339 | std::tuple, std::tuple>::type; 340 | }; 341 | 342 | template class SFINAEPredicate, typename = T> 343 | using append_if_t = typename append_if::type; 344 | 345 | // template for find_if in tuple 346 | template class /*SFINAEPredicate*/, typename = void> 347 | struct filter; 348 | 349 | template class SFINAEPredicate, typename ... Types> 350 | struct filter, SFINAEPredicate, void> 351 | { 352 | using type = decltype(std::tuple_cat(append_if_t, Types, SFINAEPredicate>()...)); 353 | }; 354 | 355 | template class SFINAEPredicate, typename = void> 356 | using filter_t = typename filter::type; 357 | 358 | template class SFINAEPredicate, typename = void> 359 | using find_first_t = typename std::conditional>::value, 360 | undeduced, 361 | typename std::tuple_element<0, filter_t>::type 362 | >::type; 363 | 364 | 365 | // workaround for index out of range in tuple 366 | template= std::tuple_size::value)> 367 | struct tuple_element 368 | { 369 | using type = undeduced; 370 | }; 371 | 372 | template 373 | struct tuple_element 374 | { 375 | using type = typename std::tuple_element::type; 376 | }; 377 | 378 | template struct disjunction : std::false_type 379 | { 380 | }; 381 | template struct disjunction : B1 382 | { 383 | }; 384 | template 385 | struct disjunction 386 | : std::conditional>::type 387 | { 388 | }; 389 | 390 | template 391 | struct contains : std::false_type 392 | { 393 | }; 394 | 395 | template 396 | struct contains> : disjunction...> 397 | { 398 | }; 399 | 400 | template 401 | using tuple_element_t = typename tuple_element::type; 402 | 403 | template 404 | class pod_element_type; 405 | 406 | template 407 | class pod_element_type, IgnoreEnums> 408 | { 409 | public: 410 | template 411 | using is_initializable_t = is_pod_member_initialisable_from_t; 412 | 413 | using found_types = decltype(std::tuple_cat(append_if_t, Types, is_initializable_t>()...)); 414 | static_assert(std::tuple_size() <= 1, "Multiple types deduced!"); 415 | 416 | public: 417 | using type = tuple_element_t<0, found_types>; 418 | }; 419 | } 420 | 421 | /*! 422 | * Provides compile-time indexed access to the types of the elements of the POD type deduced from 423 | * the provided TupleFeed class 424 | * @tparam I - index of the element 425 | * @tparam POD - POD type 426 | * @tparam TupleFeed - Tuple of types to be used as a feed to deduce an element 427 | */ 428 | template 429 | struct pod_element 430 | { 431 | static_assert(std::tuple_size(), "TupleFeed must not be empty!"); 432 | 433 | using type = typename detail::pod_element_type::type; 434 | }; 435 | 436 | template 437 | using pod_element_t = typename pod_element::type; 438 | 439 | namespace detail 440 | { 441 | template::value>> 442 | struct pod_to_tuple; 443 | 444 | template 445 | struct pod_to_tuple> 446 | { 447 | using type = std::tuple...>; 448 | }; 449 | } 450 | 451 | template 452 | using pod_to_tuple_t = typename detail::pod_to_tuple::type; 453 | 454 | namespace detail 455 | { 456 | // TODO: remove TupleFeed? 457 | template 458 | constexpr size_t pod_packing() 459 | { 460 | return sizeof(POD) % 4 ? 461 | sizeof(POD) % 4 : 462 | 4;//alignof(POD); 463 | } 464 | 465 | template 466 | struct pod_elem_size_ 467 | { 468 | using type = pod_element_t; 469 | 470 | constexpr static size_t value() 471 | { 472 | return sizeof(type); 473 | } 474 | }; 475 | 476 | template 477 | struct is_equal : std::integral_constant 478 | { 479 | }; 480 | 481 | template 482 | struct calc_offset 483 | { 484 | constexpr static std::ptrdiff_t value(const const_array &offsets) 485 | { 486 | return !((offsets.back() + pod_elem_size_::value()) % 487 | pod_packing()) || 488 | pod_packing() - 489 | (offsets.back() + pod_elem_size_::value()) % 490 | pod_packing() >= pod_elem_size_::value() ? 491 | offsets.back() + pod_elem_size_::value() : 492 | offsets.back() + pod_elem_size_::value() + 493 | pod_packing() - 494 | (offsets.back() + pod_elem_size_::value()) % 495 | pod_packing(); 496 | } 497 | }; 498 | 499 | template 500 | struct calc_offset<0, POD, TupleFeed, IgnoreEnums> 501 | { 502 | constexpr static std::ptrdiff_t value(const const_array &) 503 | { 504 | return 0; 505 | } 506 | }; 507 | 508 | // stop case: Index == pod_size 509 | template 510 | constexpr const_array::value> 511 | get_pod_offsets(const_array::value> offsets, std::true_type /*Index == pod_size*/) 512 | { 513 | return offsets; 514 | } 515 | 516 | // general recursion 517 | template 518 | constexpr const_array::value> 519 | get_pod_offsets(const_array offsets, std::false_type) 520 | { 521 | return get_pod_offsets( 522 | combine(offsets, /*TODO: calculate current*/ 523 | calc_offset::value(offsets)), 524 | is_equal::value>()); 525 | } 526 | 527 | template 528 | constexpr const_array::value> 529 | get_pod_offsets() 530 | { 531 | return get_pod_offsets(const_array{}, 532 | is_equal<0, pod_size::value>()); 533 | } 534 | 535 | // TODO: check this function! 536 | // TODO: optimize code generation, now it is n! 537 | template 538 | class pod_elem_offset 539 | { 540 | static_assert(!std::is_same>::value, 541 | "Can't get an offset for an undeduced POD element!"); 542 | 543 | public: 544 | constexpr static std::ptrdiff_t value() 545 | { 546 | return get_pod_offsets()[I]; 547 | } 548 | }; 549 | 550 | /*! 551 | * \warning Invalid implementation: alignof does not yield packing size of a struct 552 | * @tparam TupleFeed 553 | * @tparam POD 554 | * @return 555 | */ 556 | template 557 | constexpr size_t evaluated_pod_size() 558 | { 559 | return (pod_elem_offset() - 1, POD, TupleFeed, IgnoreEnums>::value() + 560 | sizeof(pod_element_t() - 1, POD, TupleFeed, IgnoreEnums>)) % 561 | pod_packing() ? 562 | pod_elem_offset() - 1, POD, TupleFeed, IgnoreEnums>::value() + 563 | sizeof(pod_element_t() - 1, POD, TupleFeed, IgnoreEnums>) + 564 | pod_packing() - 565 | (pod_elem_offset() - 1, POD, TupleFeed, IgnoreEnums>::value() + 566 | sizeof(pod_element_t() - 1, POD, TupleFeed, IgnoreEnums>)) % 567 | pod_packing() : 568 | pod_elem_offset() - 1, POD, TupleFeed, IgnoreEnums>::value() + 569 | sizeof(pod_element_t() - 1, POD, TupleFeed, IgnoreEnums>); 570 | } 571 | 572 | } 573 | 574 | // TODO: check warnings 575 | #ifdef __GNUC__ 576 | #pragma GCC diagnostic pop 577 | #endif 578 | 579 | /*! 580 | * Checks if POD type is valid for accessing and manipulating elements. 581 | * \warning POD is not valid if it contains bitfields. 582 | * @tparam TupleFeed 583 | * @tparam POD 584 | * @return 585 | * \todo check this function 586 | * \todo return false if some of the elements have not been deduced 587 | */ 588 | template 589 | constexpr bool is_valid_pod() 590 | { 591 | // calculate expected POD size using offset 592 | return !detail::contains>() && 593 | sizeof(POD) == detail::evaluated_pod_size(); 594 | } 595 | 596 | /*! 597 | * Extracts the Ith element from the POD structure 598 | * @tparam I - index of an element to access 599 | * @tparam TupleFeed - Tuple of types to be used as a feed to deduce POD elements 600 | * @tparam POD 601 | * @param pod 602 | * @return 603 | * \warning This function is UB. Need to write a workaround, i.e. - disable optimization 604 | */ 605 | template 606 | pod_element_t &get(POD &pod) 607 | { 608 | static_assert(is_valid_pod(), "Invalid POD struct: possibly contains bitfields"); 609 | static_assert(!std::is_same>::value, 610 | "Can't get an undeduced POD element!"); 611 | return *reinterpret_cast *>(((std::ptrdiff_t) &pod + 612 | detail::pod_elem_offset::value())); 613 | } 614 | 615 | template 616 | pod_element_t &get(POD &pod, ignore_enums_t) 617 | { 618 | static_assert(is_valid_pod(), 619 | "Invalid POD struct: possibly contains bitfields"); 620 | static_assert(!std::is_same>::value, 621 | "Can't get an undeduced POD element!"); 622 | return *reinterpret_cast *>(((std::ptrdiff_t) &pod + 623 | detail::pod_elem_offset::value())); 624 | } 625 | 626 | /*! 627 | * Extracts the Ith element from the POD structure 628 | * @tparam I - index of an element to access 629 | * @tparam TupleFeed - Tuple of types to be used as a feed to deduce POD elements 630 | * @tparam POD 631 | * @param pod 632 | * @return 633 | * \warning This function is UB. Need to write a workaround, i.e. - disable optimization 634 | */ 635 | template 636 | const pod_element_t &get(const POD &pod) 637 | { 638 | static_assert(is_valid_pod(), "Invalid POD struct: possibly contains bitfields"); 639 | static_assert(!std::is_same>::value, 640 | "Can't get an undeduced POD element!"); 641 | return *reinterpret_cast *>(((std::ptrdiff_t) &pod + 642 | detail::pod_elem_offset::value())); 643 | } 644 | 645 | template 646 | const pod_element_t &get(const POD &pod, ignore_enums_t) 647 | { 648 | static_assert(is_valid_pod(), 649 | "Invalid POD struct: possibly contains bitfields"); 650 | static_assert(!std::is_same>::value, 651 | "Can't get an undeduced POD element!"); 652 | return *reinterpret_cast *>(((std::ptrdiff_t) &pod + 653 | detail::pod_elem_offset::value())); 654 | } 655 | 656 | namespace detail 657 | { 658 | template 659 | size_t for_each(POD &pod, F &&func, index_sequence) 660 | { 661 | auto f = std::forward(func); 662 | return (int) std::initializer_list{(f(get(pod)), 0)...}.size(); 663 | } 664 | 665 | template 666 | size_t for_each(POD &pod, F &&func, index_sequence, ignore_enums_t) 667 | { 668 | auto f = std::forward(func); 669 | return (int) std::initializer_list{(f(get(pod, ignore_enums)), 0)...}.size(); 670 | } 671 | } 672 | 673 | /*! 674 | * Invokes functor of type F for each element in a given POD. TupleFeed must be 675 | * provided for elements deduction 676 | * @tparam TupleFeed 677 | * @tparam POD 678 | * @tparam F 679 | * @param pod 680 | * @param func 681 | * @return 682 | * \warning Invokes elements in reverse order 683 | * \todo assert that a POD does not have bitfields 684 | * \todo assert that a POD does not contain fixed size arrays 685 | */ 686 | template 687 | size_t for_each(POD &pod, F &&func) 688 | { 689 | return detail::for_each(pod, std::forward(func), 690 | detail::make_index_sequence::value>()); 691 | } 692 | 693 | template 694 | size_t for_each(POD &pod, F &&func, ignore_enums_t) 695 | { 696 | return detail::for_each(pod, std::forward(func), 697 | detail::make_index_sequence::value>(), ignore_enums_t()); 698 | } 699 | 700 | } 701 | --------------------------------------------------------------------------------