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:
36
README.md
36
README.md
@@ -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
|
||||
|
||||
279
magic_get.hpp
279
magic_get.hpp
@@ -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
|
||||
|
||||
69
main.cpp
69
main.cpp
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user