From d6bf6d1e746ac3e9769b90fdf7bbe740d0bcb903 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 8 Apr 2016 21:55:06 +0300 Subject: [PATCH] Added pod_ops namespace and a lot of usefull generic operations. Improved diagnostics and docs --- README.md | 36 +++++++ magic_get.hpp | 279 +++++++++++++++++++++++++++++++++++++++++++------- main.cpp | 69 +++---------- 3 files changed, 296 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index aa4fff2..9490cee 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,42 @@ auto flat_make_tuple(const T& val) noexcept; /// assert(s.s == 11); template auto flat_tie(T& val, typename std::enable_if< std::is_trivially_assignable::value>::type* = 0 ) noexcept; + + +/// Writes to `out` POD `value` +/// Example usage: +/// struct my_struct { int i, short s; }; +/// my_struct s{12, 13}; +/// flat_write(std::cout, s); // outputs '{ 12, 13 }' +template +void flat_write(std::basic_ostream& out, const T& value); + + +/// Reads POD `value` from stream `in` +/// Example usage: +/// struct my_struct { int i, short s; }; +/// my_struct s; +/// std::stringstream ss; +/// ss << "{ 12, 13 }"; +/// ss >> s; +/// assert(s.i == 12); +/// assert(s.i == 13); +template +void flat_read(std::basic_istream& in, T& value); + + +/// Contains comparison operators and stream operators for any POD types +/// Example usage: +/// struct comparable_struct { +/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f; +/// }; // No operators defined for that structure +/// using namespace pod_ops; +/// +/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11}; +/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111}; +/// assert(s1 < s2); +/// std::cout << s1 << std::endl; // Outputs: { 0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11 } +namespace pod_ops; ``` ### Requirements and Limitations diff --git a/magic_get.hpp b/magic_get.hpp index b90a255..398ba95 100644 --- a/magic_get.hpp +++ b/magic_get.hpp @@ -5,9 +5,14 @@ #pragma once +#if __cplusplus < 201402L +# error C++14 is required for this header. +#endif + #include #include #include +#include #ifdef __clang__ # pragma clang diagnostic push @@ -161,7 +166,7 @@ struct size_array { // libc++ misses constexpr on operat template constexpr std::size_t get(const size_array& a) noexcept { - static_assert(I < N, "Out of bounds"); + static_assert(I < N, "Array index out of bounds"); return a.data[I]; } @@ -212,7 +217,7 @@ constexpr std::size_t type_to_id_extension_apply(std::size_t ext) noexcept { constexpr std::size_t extensions = (Unptr & ~native_types_mask); static_assert( !((extensions >> bits_per_extension) & native_types_mask), - "max extensions reached" + "Too many extensions for a single field (something close to `int************************** p;` is in the POD type)." ); return (extensions >> bits_per_extension) | native_id | ext; @@ -234,7 +239,8 @@ template constexpr std::size_t type_to_id(identity constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity, typename std::enable_if::value>::type* = 0) noexcept; -template constexpr auto type_to_id(identity, typename std::enable_if::value>::type* = 0) noexcept; +template constexpr std::size_t type_to_id(identity, typename std::enable_if::value>::type* = 0) noexcept; +template constexpr auto type_to_id(identity, typename std::enable_if::value && !std::is_empty::value>::type* = 0) noexcept; template constexpr auto id_to_type(size_t_, if_extension = 0) noexcept; template constexpr auto id_to_type(size_t_, if_extension = 0) noexcept; @@ -244,7 +250,7 @@ template constexpr auto id_to_type(size_t_, if_exten ///////////////////// Definitions of type_to_id and id_to_type for fundamental types -#define REGISTER_TYPE(Type, Index) \ +#define BOOST_MAGIC_GET_REGISTER_TYPE(Type, Index) \ constexpr std::size_t type_to_id(identity) noexcept { \ return Index; \ } \ @@ -256,29 +262,30 @@ template constexpr auto id_to_type(size_t_, if_exten // Register all base types here -REGISTER_TYPE(unsigned char , 1) -REGISTER_TYPE(unsigned short , 2) -REGISTER_TYPE(unsigned int , 3) -REGISTER_TYPE(unsigned long , 4) -REGISTER_TYPE(unsigned long long , 5) -REGISTER_TYPE(signed char , 6) -REGISTER_TYPE(short , 7) -REGISTER_TYPE(int , 8) -REGISTER_TYPE(long , 9) -REGISTER_TYPE(long long , 10) -REGISTER_TYPE(char , 11) -REGISTER_TYPE(wchar_t , 12) -REGISTER_TYPE(char16_t , 13) -REGISTER_TYPE(char32_t , 14) -REGISTER_TYPE(float , 15) -REGISTER_TYPE(double , 16) -REGISTER_TYPE(long double , 17) -REGISTER_TYPE(bool , 18) -REGISTER_TYPE(void* , 19) -REGISTER_TYPE(const void* , 20) -REGISTER_TYPE(volatile void* , 21) -REGISTER_TYPE(const volatile void* , 22) -REGISTER_TYPE(std::nullptr_t , 23) +BOOST_MAGIC_GET_REGISTER_TYPE(unsigned char , 1) +BOOST_MAGIC_GET_REGISTER_TYPE(unsigned short , 2) +BOOST_MAGIC_GET_REGISTER_TYPE(unsigned int , 3) +BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long , 4) +BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long long , 5) +BOOST_MAGIC_GET_REGISTER_TYPE(signed char , 6) +BOOST_MAGIC_GET_REGISTER_TYPE(short , 7) +BOOST_MAGIC_GET_REGISTER_TYPE(int , 8) +BOOST_MAGIC_GET_REGISTER_TYPE(long , 9) +BOOST_MAGIC_GET_REGISTER_TYPE(long long , 10) +BOOST_MAGIC_GET_REGISTER_TYPE(char , 11) +BOOST_MAGIC_GET_REGISTER_TYPE(wchar_t , 12) +BOOST_MAGIC_GET_REGISTER_TYPE(char16_t , 13) +BOOST_MAGIC_GET_REGISTER_TYPE(char32_t , 14) +BOOST_MAGIC_GET_REGISTER_TYPE(float , 15) +BOOST_MAGIC_GET_REGISTER_TYPE(double , 16) +BOOST_MAGIC_GET_REGISTER_TYPE(long double , 17) +BOOST_MAGIC_GET_REGISTER_TYPE(bool , 18) +BOOST_MAGIC_GET_REGISTER_TYPE(void* , 19) +BOOST_MAGIC_GET_REGISTER_TYPE(const void* , 20) +BOOST_MAGIC_GET_REGISTER_TYPE(volatile void* , 21) +BOOST_MAGIC_GET_REGISTER_TYPE(const volatile void* , 22) +BOOST_MAGIC_GET_REGISTER_TYPE(std::nullptr_t , 23) + ///////////////////// Definitions of type_to_id and id_to_type for types with extensions and nested types template @@ -336,9 +343,14 @@ constexpr std::size_t type_to_id(identity, typename std::enable_if::type >{}); } +template +constexpr std::size_t type_to_id(identity, typename std::enable_if::value>::type*) noexcept { + static_assert(!std::is_empty::value, "Empty classes/structures as POD members are not supported."); + return 0; +} template -constexpr auto type_to_id(identity, typename std::enable_if::value>::type*) noexcept { +constexpr auto type_to_id(identity, typename std::enable_if::value && !std::is_empty::value>::type*) noexcept { return fields_count_and_type_ids_with_zeros(); } @@ -481,7 +493,7 @@ constexpr auto type_to_array_of_type_ids(std::size_t* types) noexcept constexpr std::size_t* misc = nullptr; static_assert( !decltype(has_reference_members(misc))::value, - "reference members are not supported" + "Reference members are not supported." ); constexpr auto offsets = get_type_offsets(); @@ -505,7 +517,7 @@ constexpr void detect_fields_count_and_type_ids(std::size_t* types, std::index_s template constexpr void detect_fields_count_and_type_ids(std::size_t*, std::index_sequence<>) noexcept { - static_assert(!!sizeof(T), "Failed for unknown reason"); + static_assert(!!sizeof(T), "Failed for unknown reason."); } ///////////////////// Returns array of typeids and zeros @@ -606,11 +618,17 @@ struct teleport_extents { # pragma clang diagnostic pop #endif +#ifdef __GNUC__ +#define MAY_ALIAS __attribute__((__may_alias__)) +#else +#define MAY_ALIAS +#endif + /// Returns const reference to a field with index `I` in flattened `T`. /// Example usage: flat_get<0>(my_structure()); template decltype(auto) flat_get(const T& val) noexcept { - const auto* const t = reinterpret_cast*>( std::addressof(val) ); + MAY_ALIAS const auto* const t = reinterpret_cast*>( std::addressof(val) ); return detail::sequence_tuple::get(*t); } @@ -620,7 +638,7 @@ decltype(auto) flat_get(const T& val) noexcept { /// Example usage: flat_get<0>(my_structure()); template decltype(auto) flat_get(T& val, typename std::enable_if< std::is_trivially_assignable::value>::type* = 0) noexcept { - auto* const t = reinterpret_cast*>( std::addressof(val) ); + MAY_ALIAS auto* const t = reinterpret_cast*>( std::addressof(val) ); return detail::sequence_tuple::get(*t); } @@ -632,7 +650,7 @@ using flat_tuple_element = detail::teleport_extents< T, typename detail::sequence_tuple::tuple_element >::type >; - + /// Type of a field with index `I` in flattened `T` /// Example usage: std::vector< flat_tuple_element_t<0, my_structure> > v; @@ -661,7 +679,7 @@ constexpr std::size_t flat_tuple_size_v = flat_tuple_size::value; template auto flat_make_tuple(const T& val) noexcept { typedef detail::as_tuple_t internal_tuple_t; - const internal_tuple_t& t = *reinterpret_cast( std::addressof(val) ); + MAY_ALIAS const internal_tuple_t& t = *reinterpret_cast( std::addressof(val) ); return detail::flat_make_tuple_impl( t, std::make_index_sequence< internal_tuple_t::size_v >() @@ -678,9 +696,200 @@ auto flat_make_tuple(const T& val) noexcept { template auto flat_tie(T& val, typename std::enable_if< std::is_trivially_assignable::value>::type* = 0 ) noexcept { typedef detail::as_tuple_t internal_tuple_t; - internal_tuple_t& t = *reinterpret_cast( std::addressof(val) ); + MAY_ALIAS internal_tuple_t& t = *reinterpret_cast( std::addressof(val) ); return detail::flat_tie_impl( t, std::make_index_sequence< internal_tuple_t::size_v >() ); } + +#undef MAY_ALIAS + + +namespace detail { + template + struct flat_print_impl { + template + static void print (Stream& out, const T& value) { + if (!!I) out << ", "; + out << flat_get(value); + flat_print_impl::print(out, value); + } + }; + + template + struct flat_print_impl { + template static void print (Stream&, const T&) noexcept {} + }; +} + +/// Writes to `out` POD `value` +/// Example usage: +/// struct my_struct { int i, short s; }; +/// my_struct s{12, 13}; +/// flat_write(std::cout, s); // outputs '{ 12, 13 }' +template +void flat_write(std::basic_ostream& out, const T& value) { + out << "{ "; + detail::flat_print_impl<0, flat_tuple_size_v >::print(out, value); + out << " }"; +} + + +namespace detail { + template + struct flat_read_impl { + template + static void read (Stream& in, T& value) { + char ignore = {}; + if (!!I) { + in >> ignore; + if (ignore != ',') in.setstate(Stream::failbit); + } + in >> ignore; + if (ignore != ' ') in.setstate(Stream::failbit); + in >> flat_get(value); + flat_read_impl::read(in, value); + } + }; + + template + struct flat_read_impl { + template static void read (Stream&, const T&) {} + }; +} + +/// Reads POD `value` from stream `in` +/// Example usage: +/// struct my_struct { int i, short s; }; +/// my_struct s; +/// std::stringstream ss; +/// ss << "{ 12, 13 }"; +/// ss >> s; +/// assert(s.i == 12); +/// assert(s.i == 13); +template +void flat_read(std::basic_istream& in, T& value) { + const auto prev_exceptions = in.exceptions(); + in.exceptions( typename std::basic_istream::iostate(0) ); + const auto prev_flags = in.flags( typename std::basic_istream::fmtflags(0) ); + + char parenthis = {}; + in >> parenthis; + if (parenthis != '{') in.setstate(std::basic_istream::failbit); + detail::flat_read_impl<0, flat_tuple_size_v >::read(in, value); + + in >> parenthis; + if (parenthis != ' ') in.setstate(std::basic_istream::failbit); + in >> parenthis; + if (parenthis != '}') in.setstate(std::basic_istream::failbit); + + in.flags(prev_flags); + in.exceptions(prev_exceptions); +} + + +namespace detail { + template + using same_pods_enable = typename std::enable_if< + std::is_same::value && std::is_pod::value && std::is_pod::value, + bool + >::type; + + template + struct equal_impl { + template + static bool cmp(const T& v1, const T& v2) noexcept { + return flat_get(v1) == flat_get(v2) + && equal_impl::cmp(v1, v2); + } + }; + + template + struct equal_impl { + template + static bool cmp(const T&, const T&) noexcept { + return true; + } + }; + + template + struct less_impl { + template + static bool cmp(const T& v1, const T& v2) noexcept { + return flat_get(v1) < flat_get(v2) + || (flat_get(v1) == flat_get(v2) && less_impl::cmp(v1, v2)); + } + }; + + template + struct less_impl { + template + static bool cmp(const T&, const T&) noexcept { + return false; + } + }; +} + +/// Contains comparison operators and stream operators for any POD types +/// Example usage: +/// struct comparable_struct { +/// int i; short s; char data[7]; bool bl; int a,b,c,d,e,f; +/// }; +/// using namespace pod_ops; +/// +/// comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11}; +/// comparable_struct s2 {0, 1, "Hello", false, 6,7,8,9,10,11111}; +/// assert(s1 < s2); +/// std::cout << s1 << std::endl; // Outputs: { 0, 1, H, e, l, l, o, , , 0, 6, 7, 8, 9, 10, 11 } + +namespace pod_ops { + ///////////////////// Comparisons + template + inline detail::same_pods_enable operator==(const T1& lhs, const T2& rhs) noexcept { + return detail::equal_impl<0, flat_tuple_size_v >::cmp(lhs, rhs); + } + + template + inline detail::same_pods_enable operator!=(const T1& lhs, const T2& rhs) noexcept { + return !(lhs == rhs); + } + + template + inline detail::same_pods_enable operator<(const T1& lhs, const T2& rhs) noexcept { + return detail::less_impl<0, flat_tuple_size_v >::cmp(lhs, rhs); + } + + template + inline detail::same_pods_enable operator>(const T1& lhs, const T2& rhs) noexcept { + return rhs < lhs; + } + + template + inline detail::same_pods_enable operator<=(const T1& lhs, const T2& rhs) noexcept { + return !(lhs > rhs); + } + + template + inline detail::same_pods_enable operator>=(const T1& lhs, const T2& rhs) noexcept { + return !(lhs < rhs); + } + + ///////////////////// basic_ostream operator << + template + typename std::enable_if::value && std::is_class::value, std::basic_ostream& >::type + operator<<(std::basic_ostream& out, const T& value) + { + flat_write(out, value); + return out; + } + + ///////////////////// basic_istream operator >> + template + typename std::enable_if::value && std::is_class::value, std::basic_istream& >::type + operator>>(std::basic_istream& in, T& value) + { + flat_read(in, value); + return in; + } +} // pod_ops diff --git a/main.cpp b/main.cpp index 3b2bec9..f09e2b4 100644 --- a/main.cpp +++ b/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "magic_get.hpp" template @@ -191,75 +192,37 @@ void test_with_enums() { ); } -namespace pod_ops { - -template -inline bool operator==(const T1& lhs, const T2& rhs) { - return flat_make_tuple(lhs) == flat_make_tuple(rhs); -} - -template -inline bool operator!=(const T1& lhs, const T2& rhs) { - return flat_make_tuple(lhs) != flat_make_tuple(rhs); -} - -template -inline bool operator<(const T1& lhs, const T2& rhs) { - return flat_make_tuple(lhs) < flat_make_tuple(rhs); -} - -template -inline bool operator>(const T1& lhs, const T2& rhs) { - return flat_make_tuple(lhs) > flat_make_tuple(rhs); -} - -} // pod_ops - -// ... - -template -struct print_impl { - template - static void print (std::ostream& out, const T& value) { - if (!!I) out << ", "; - out << flat_get(value); - print_impl::print(out, value); - } -}; - -template -struct print_impl { - template static void print (std::ostream&, const T&) {} -}; - -template -typename std::enable_if::value && std::is_class::value , Ostreamable& >::type - operator<<(Ostreamable& out, const T& value) -{ - out << "{ "; - print_impl<0, flat_tuple_size_v >::print(out, value); - out << " }"; - return out; -} - - void test_comparable_struct() { - using namespace pod_ops; struct comparable_struct { int i; short s; char data[50]; bool bl; int a,b,c,d,e,f; }; + using namespace pod_ops; comparable_struct s1 {0, 1, "Hello", false, 6,7,8,9,10,11}; comparable_struct s2 = s1; comparable_struct s3 {0, 1, "Hello", false, 6,7,8,9,10,11111}; assert(s1 == s2); + assert(s1 <= s2); + assert(s1 >= s2); assert(!(s1 != s2)); assert(!(s1 == s3)); assert(s1 != s3); assert(s1 < s3); assert(s3 > s2); + assert(s1 <= s3); + assert(s3 >= s2); std::cout << s1 << std::endl; + + comparable_struct s4; + std::stringstream ss; + ss.exceptions ( std::ios::failbit); + ss << s1; + ss >> s4; + std::cout << s4 << std::endl; + assert(s1 == s4); + int i = 1, j = 2; + assert(i != j); } int main() {