2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-19 04:22:13 +00:00

Added pod_ops namespace and a lot of usefull generic operations. Improved diagnostics and docs

This commit is contained in:
Antony Polukhin
2016-04-08 21:55:06 +03:00
parent 73d85bee28
commit d6bf6d1e74
3 changed files with 296 additions and 88 deletions

View File

@@ -118,6 +118,42 @@ auto flat_make_tuple(const T& val) noexcept;
/// assert(s.s == 11);
template <class T>
auto flat_tie(T& val, typename std::enable_if< std::is_trivially_assignable<T, T>::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 <class Char, class Traits, class T>
void flat_write(std::basic_ostream<Char, Traits>& 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 <class Char, class Traits, class T>
void flat_read(std::basic_istream<Char, Traits>& 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

View File

@@ -5,9 +5,14 @@
#pragma once
#if __cplusplus < 201402L
# error C++14 is required for this header.
#endif
#include <type_traits>
#include <utility>
#include <memory>
#include <iosfwd>
#ifdef __clang__
# pragma clang diagnostic push
@@ -161,7 +166,7 @@ struct size_array { // libc++ misses constexpr on operat
template <std::size_t I, std::size_t N>
constexpr std::size_t get(const size_array<N>& 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 <class Type> constexpr std::size_t type_to_id(identity<const volatile T
template <class Type> constexpr std::size_t type_to_id(identity<volatile Type*>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<Type&>) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<Type>, typename std::enable_if<std::is_enum<Type>::value>::type* = 0) noexcept;
template <class Type> constexpr auto type_to_id(identity<Type>, typename std::enable_if<!std::is_enum<Type>::value>::type* = 0) noexcept;
template <class Type> constexpr std::size_t type_to_id(identity<Type>, typename std::enable_if<std::is_empty<Type>::value>::type* = 0) noexcept;
template <class Type> constexpr auto type_to_id(identity<Type>, typename std::enable_if<!std::is_enum<Type>::value && !std::is_empty<Type>::value>::type* = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_const_ptr_type> = 0) noexcept;
template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, if_extension<Index, native_ptr_type> = 0) noexcept;
@@ -244,7 +250,7 @@ template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, 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<Type>) noexcept { \
return Index; \
} \
@@ -256,29 +262,30 @@ template <std::size_t Index> constexpr auto id_to_type(size_t_<Index >, 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 <class Type>
@@ -336,9 +343,14 @@ constexpr std::size_t type_to_id(identity<Type>, typename std::enable_if<std::is
return type_to_id(identity<typename std::underlying_type<Type>::type >{});
}
template <class Type>
constexpr std::size_t type_to_id(identity<Type>, typename std::enable_if<std::is_empty<Type>::value>::type*) noexcept {
static_assert(!std::is_empty<Type>::value, "Empty classes/structures as POD members are not supported.");
return 0;
}
template <class Type>
constexpr auto type_to_id(identity<Type>, typename std::enable_if<!std::is_enum<Type>::value>::type*) noexcept {
constexpr auto type_to_id(identity<Type>, typename std::enable_if<!std::is_enum<Type>::value && !std::is_empty<Type>::value>::type*) noexcept {
return fields_count_and_type_ids_with_zeros<Type>();
}
@@ -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<T, I...>(misc))::value,
"reference members are not supported"
"Reference members are not supported."
);
constexpr auto offsets = get_type_offsets<T, N, I...>();
@@ -505,7 +517,7 @@ constexpr void detect_fields_count_and_type_ids(std::size_t* types, std::index_s
template <class T, std::size_t N>
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<volatile From, To> {
# 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 <std::size_t I, class T>
decltype(auto) flat_get(const T& val) noexcept {
const auto* const t = reinterpret_cast<const detail::as_tuple_t<T>*>( std::addressof(val) );
MAY_ALIAS const auto* const t = reinterpret_cast<const detail::as_tuple_t<T>*>( std::addressof(val) );
return detail::sequence_tuple::get<I>(*t);
}
@@ -620,7 +638,7 @@ decltype(auto) flat_get(const T& val) noexcept {
/// Example usage: flat_get<0>(my_structure());
template <std::size_t I, class T>
decltype(auto) flat_get(T& val, typename std::enable_if< std::is_trivially_assignable<T, T>::value>::type* = 0) noexcept {
auto* const t = reinterpret_cast<detail::as_tuple_t<T>*>( std::addressof(val) );
MAY_ALIAS auto* const t = reinterpret_cast<detail::as_tuple_t<T>*>( std::addressof(val) );
return detail::sequence_tuple::get<I>(*t);
}
@@ -632,7 +650,7 @@ using flat_tuple_element = detail::teleport_extents<
T,
typename detail::sequence_tuple::tuple_element<I, detail::as_tuple_t<T> >::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<T>::value;
template <class T>
auto flat_make_tuple(const T& val) noexcept {
typedef detail::as_tuple_t<T> internal_tuple_t;
const internal_tuple_t& t = *reinterpret_cast<const internal_tuple_t*>( std::addressof(val) );
MAY_ALIAS const internal_tuple_t& t = *reinterpret_cast<const internal_tuple_t*>( 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 <class T>
auto flat_tie(T& val, typename std::enable_if< std::is_trivially_assignable<T, T>::value>::type* = 0 ) noexcept {
typedef detail::as_tuple_t<T> internal_tuple_t;
internal_tuple_t& t = *reinterpret_cast<internal_tuple_t*>( std::addressof(val) );
MAY_ALIAS internal_tuple_t& t = *reinterpret_cast<internal_tuple_t*>( std::addressof(val) );
return detail::flat_tie_impl(
t,
std::make_index_sequence< internal_tuple_t::size_v >()
);
}
#undef MAY_ALIAS
namespace detail {
template <std::size_t I, std::size_t N>
struct flat_print_impl {
template <class Stream, class T>
static void print (Stream& out, const T& value) {
if (!!I) out << ", ";
out << flat_get<I>(value);
flat_print_impl<I + 1, N>::print(out, value);
}
};
template <std::size_t I>
struct flat_print_impl<I, I> {
template <class Stream, class T> 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 <class Char, class Traits, class T>
void flat_write(std::basic_ostream<Char, Traits>& out, const T& value) {
out << "{ ";
detail::flat_print_impl<0, flat_tuple_size_v<T> >::print(out, value);
out << " }";
}
namespace detail {
template <std::size_t I, std::size_t N>
struct flat_read_impl {
template <class Stream, class T>
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<I>(value);
flat_read_impl<I + 1, N>::read(in, value);
}
};
template <std::size_t I>
struct flat_read_impl<I, I> {
template <class Stream, class T> 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 <class Char, class Traits, class T>
void flat_read(std::basic_istream<Char, Traits>& in, T& value) {
const auto prev_exceptions = in.exceptions();
in.exceptions( typename std::basic_istream<Char, Traits>::iostate(0) );
const auto prev_flags = in.flags( typename std::basic_istream<Char, Traits>::fmtflags(0) );
char parenthis = {};
in >> parenthis;
if (parenthis != '{') in.setstate(std::basic_istream<Char, Traits>::failbit);
detail::flat_read_impl<0, flat_tuple_size_v<T> >::read(in, value);
in >> parenthis;
if (parenthis != ' ') in.setstate(std::basic_istream<Char, Traits>::failbit);
in >> parenthis;
if (parenthis != '}') in.setstate(std::basic_istream<Char, Traits>::failbit);
in.flags(prev_flags);
in.exceptions(prev_exceptions);
}
namespace detail {
template <class T1, class T2>
using same_pods_enable = typename std::enable_if<
std::is_same<T1, T2>::value && std::is_pod<T1>::value && std::is_pod<T2>::value,
bool
>::type;
template <std::size_t I, std::size_t N>
struct equal_impl {
template <class T>
static bool cmp(const T& v1, const T& v2) noexcept {
return flat_get<I>(v1) == flat_get<I>(v2)
&& equal_impl<I + 1, N>::cmp(v1, v2);
}
};
template <std::size_t N>
struct equal_impl<N, N> {
template <class T>
static bool cmp(const T&, const T&) noexcept {
return true;
}
};
template <std::size_t I, std::size_t N>
struct less_impl {
template <class T>
static bool cmp(const T& v1, const T& v2) noexcept {
return flat_get<I>(v1) < flat_get<I>(v2)
|| (flat_get<I>(v1) == flat_get<I>(v2) && less_impl<I + 1, N>::cmp(v1, v2));
}
};
template <std::size_t N>
struct less_impl<N, N> {
template <class T>
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 <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator==(const T1& lhs, const T2& rhs) noexcept {
return detail::equal_impl<0, flat_tuple_size_v<T1> >::cmp(lhs, rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator!=(const T1& lhs, const T2& rhs) noexcept {
return !(lhs == rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator<(const T1& lhs, const T2& rhs) noexcept {
return detail::less_impl<0, flat_tuple_size_v<T1> >::cmp(lhs, rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator>(const T1& lhs, const T2& rhs) noexcept {
return rhs < lhs;
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator<=(const T1& lhs, const T2& rhs) noexcept {
return !(lhs > rhs);
}
template <class T1, class T2>
inline detail::same_pods_enable<T1, T2> operator>=(const T1& lhs, const T2& rhs) noexcept {
return !(lhs < rhs);
}
///////////////////// basic_ostream operator <<
template <class Char, class Traits, class T>
typename std::enable_if<std::is_pod<T>::value && std::is_class<T>::value, std::basic_ostream<Char, Traits>& >::type
operator<<(std::basic_ostream<Char, Traits>& out, const T& value)
{
flat_write(out, value);
return out;
}
///////////////////// basic_istream operator >>
template <class Char, class Traits, class T>
typename std::enable_if<std::is_pod<T>::value && std::is_class<T>::value, std::basic_istream<Char, Traits>& >::type
operator>>(std::basic_istream<Char, Traits>& in, T& value)
{
flat_read(in, value);
return in;
}
} // pod_ops

View File

@@ -6,6 +6,7 @@
#include <iostream>
#include <boost/type_index.hpp>
#include <tuple>
#include <sstream>
#include "magic_get.hpp"
template <std::size_t I, class T>
@@ -191,75 +192,37 @@ void test_with_enums() {
);
}
namespace pod_ops {
template <class T1, class T2>
inline bool operator==(const T1& lhs, const T2& rhs) {
return flat_make_tuple(lhs) == flat_make_tuple(rhs);
}
template <class T1, class T2>
inline bool operator!=(const T1& lhs, const T2& rhs) {
return flat_make_tuple(lhs) != flat_make_tuple(rhs);
}
template <class T1, class T2>
inline bool operator<(const T1& lhs, const T2& rhs) {
return flat_make_tuple(lhs) < flat_make_tuple(rhs);
}
template <class T1, class T2>
inline bool operator>(const T1& lhs, const T2& rhs) {
return flat_make_tuple(lhs) > flat_make_tuple(rhs);
}
} // pod_ops
// ...
template <std::size_t I, std::size_t N>
struct print_impl {
template <class T>
static void print (std::ostream& out, const T& value) {
if (!!I) out << ", ";
out << flat_get<I>(value);
print_impl<I + 1, N>::print(out, value);
}
};
template <std::size_t I>
struct print_impl<I, I> {
template <class T> static void print (std::ostream&, const T&) {}
};
template <class Ostreamable, class T>
typename std::enable_if<std::is_pod<T>::value && std::is_class<T>::value , Ostreamable& >::type
operator<<(Ostreamable& out, const T& value)
{
out << "{ ";
print_impl<0, flat_tuple_size_v<T> >::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() {