add traits to value

This commit is contained in:
BlackMATov
2021-07-05 21:17:58 +07:00
parent e9cdbd0c0a
commit 88c3bf4eb2
2 changed files with 213 additions and 3 deletions

View File

@@ -8,6 +8,34 @@
#include "meta_fwd.hpp" #include "meta_fwd.hpp"
namespace meta_hpp
{
class value;
}
namespace meta_hpp::value_detail
{
struct traits {
bool(*equal)(const value&, const value&){};
bool(*not_equal)(const value&, const value&){};
};
template < typename T >
bool value_equal(const value& l, const value& r);
template < typename T >
bool value_not_equal(const value& l, const value& r);
template < typename T >
const traits* get_traits() noexcept {
static traits traits{
&value_equal<T>,
&value_not_equal<T>,
};
return &traits;
}
}
namespace meta_hpp namespace meta_hpp
{ {
class value { class value {
@@ -25,7 +53,8 @@ namespace meta_hpp
, typename = std::enable_if_t<!std::is_same_v<U, value>> > , typename = std::enable_if_t<!std::is_same_v<U, value>> >
value(T&& val) value(T&& val)
: raw_{std::forward<T>(val)} : raw_{std::forward<T>(val)}
, fid_{get_type_family_id<U>()} {} , fid_{get_type_family_id<U>()}
, traits_{value_detail::get_traits<U>()} {}
template < typename T template < typename T
, typename U = std::decay_t<T> , typename U = std::decay_t<T>
@@ -38,12 +67,14 @@ namespace meta_hpp
template < typename T, typename... Args > template < typename T, typename... Args >
explicit value(std::in_place_type_t<T>, Args&&... args) explicit value(std::in_place_type_t<T>, Args&&... args)
: raw_{std::in_place_type<T>, std::forward<Args>(args)...} : raw_{std::in_place_type<T>, std::forward<Args>(args)...}
, fid_{get_type_family_id<T>()} {} , fid_{get_type_family_id<T>()}
, traits_{value_detail::get_traits<T>()} {}
template < typename T, typename U, typename... Args > template < typename T, typename U, typename... Args >
explicit value(std::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args) explicit value(std::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args)
: raw_{std::in_place_type<T>, ilist, std::forward<Args>(args)...} : raw_{std::in_place_type<T>, ilist, std::forward<Args>(args)...}
, fid_{get_type_family_id<T>()} {} , fid_{get_type_family_id<T>()}
, traits_{value_detail::get_traits<T>()} {}
template < typename T > template < typename T >
T cast() && { T cast() && {
@@ -80,6 +111,51 @@ namespace meta_hpp
using std::swap; using std::swap;
swap(raw_, other.raw_); swap(raw_, other.raw_);
swap(fid_, other.fid_); swap(fid_, other.fid_);
swap(traits_, other.traits_);
}
template < typename T
, typename U = std::decay_t<T>
, typename = std::enable_if_t<!std::is_same_v<U, value>>
, typename = std::enable_if_t<std::is_invocable_v<std::equal_to<>, U, U>> >
friend bool operator==(const value& l, T&& r) {
return l.fid() == get_type_family_id<U>()
&& std::equal_to<>{}(*l.try_cast<U>(), std::forward<T>(r));
}
template < typename T
, typename U = std::decay_t<T>
, typename = std::enable_if_t<!std::is_same_v<U, value>>
, typename = std::enable_if_t<std::is_invocable_v<std::equal_to<>, U, U>> >
friend bool operator==(T&& l, const value& r) {
return get_type_family_id<U>() == r.fid()
&& std::equal_to<>{}(std::forward<T>(l), *r.try_cast<U>());
}
template < typename T
, typename U = std::decay_t<T>
, typename = std::enable_if_t<!std::is_same_v<U, value>>
, typename = std::enable_if_t<std::is_invocable_v<std::equal_to<>, U, U>> >
friend bool operator!=(const value& l, T&& r) {
return l.fid() != get_type_family_id<U>()
|| std::not_equal_to<>{}(*l.try_cast<U>(), std::forward<T>(r));
}
template < typename T
, typename U = std::decay_t<T>
, typename = std::enable_if_t<!std::is_same_v<U, value>>
, typename = std::enable_if_t<std::is_invocable_v<std::equal_to<>, U, U>> >
friend bool operator!=(T&& l, const value& r) {
return get_type_family_id<U>() != r.fid()
|| std::not_equal_to<>{}(std::forward<T>(l), *r.try_cast<U>());
}
friend bool operator==(const value& l, const value& r) {
return l.traits_->equal(l, r);
}
friend bool operator!=(const value& l, const value& r) {
return l.traits_->not_equal(l, r);
} }
public: public:
bool to_bool() const { return cast<bool>(); } bool to_bool() const { return cast<bool>(); }
@@ -105,9 +181,37 @@ namespace meta_hpp
private: private:
std::any raw_; std::any raw_;
family_id fid_; family_id fid_;
const value_detail::traits* traits_{};
}; };
inline void swap(value& l, value& r) noexcept { inline void swap(value& l, value& r) noexcept {
l.swap(r); l.swap(r);
} }
} }
namespace meta_hpp::value_detail
{
template < typename T >
bool value_equal(const value& l, const value& r) {
if ( l.fid() != r.fid() ) {
return false;
}
if constexpr ( std::is_invocable_v<std::equal_to<>, T, T> ) {
return std::equal_to<>{}(*l.try_cast<T>(), *(r.try_cast<T>()));
} else {
return std::addressof(l) == std::addressof(r);
}
}
template < typename T >
bool value_not_equal(const value& l, const value& r) {
if ( l.fid() != r.fid() ) {
return true;
}
if constexpr ( std::is_invocable_v<std::not_equal_to<>, T, T> ) {
return std::not_equal_to<>{}(*l.try_cast<T>(), *(r.try_cast<T>()));
} else {
return std::addressof(l) != std::addressof(r);
}
}
}

View File

@@ -14,6 +14,8 @@ namespace
{ {
class clazz {}; class clazz {};
class clazz2 {}; class clazz2 {};
void func() {}
} }
TEST_CASE("meta/value") { TEST_CASE("meta/value") {
@@ -61,6 +63,110 @@ TEST_CASE("meta/value/fid") {
CHECK(meta::value{std::in_place_type<std::vector<int>>, {1,2,3,4}}.fid() == meta::get_type_family_id<std::vector<int>>()); CHECK(meta::value{std::in_place_type<std::vector<int>>, {1,2,3,4}}.fid() == meta::get_type_family_id<std::vector<int>>());
} }
TEST_CASE("meta/value/equal") {
namespace meta = meta_hpp;
using namespace std::string_literals;
{
CHECK(meta::value{1} == 1);
CHECK(meta::value{1} != 0);
CHECK(meta::value{1} != "hello"s);
CHECK_FALSE(meta::value{1} != 1);
CHECK_FALSE(meta::value{1} == 0);
CHECK_FALSE(meta::value{1} == "hello"s);
}
{
CHECK(1 == meta::value{1});
CHECK(0 != meta::value{1});
CHECK("hello"s != meta::value{1});
CHECK_FALSE(1 != meta::value{1});
CHECK_FALSE(0 == meta::value{1});
CHECK_FALSE("hello"s == meta::value{1});
}
{
int i = 1;
CHECK(meta::value{1} == i);
CHECK(meta::value{1} == std::as_const(i));
CHECK_FALSE(meta::value{1} != i);
CHECK_FALSE(meta::value{1} != std::as_const(i));
CHECK(i == meta::value{1});
CHECK(std::as_const(i) == meta::value{1});
CHECK_FALSE(i != meta::value{1});
CHECK_FALSE(std::as_const(i) != meta::value{1});
}
{
CHECK(meta::value{1} == meta::value{1});
CHECK(meta::value{1} != meta::value{0});
CHECK(meta::value{1} != meta::value{"hello"s});
CHECK_FALSE(meta::value{1} != meta::value{1});
CHECK_FALSE(meta::value{1} == meta::value{0});
CHECK_FALSE(meta::value{1} == meta::value{"hello"s});
CHECK(meta::value{1} == meta::value{1});
CHECK(meta::value{0} != meta::value{1});
CHECK(meta::value{"hello"s} != meta::value{1});
CHECK_FALSE(meta::value{1} != meta::value{1});
CHECK_FALSE(meta::value{0} == meta::value{1});
CHECK_FALSE(meta::value{"hello"s} == meta::value{1});
}
{
CHECK(meta::value{std::in_place_type<int>, 1} == meta::value{1});
CHECK(meta::value{1} == meta::value{std::in_place_type<int>, 1});
using vi = std::vector<int>;
CHECK(meta::value{std::in_place_type<vi>, {1,2,3}} == meta::value{vi{1,2,3}});
CHECK(meta::value{vi{1,2,3}} == meta::value{std::in_place_type<vi>, {1,2,3}});
CHECK_FALSE(meta::value{std::in_place_type<vi>, {1,2,3}} == meta::value{vi{1,2,4}});
CHECK_FALSE(meta::value{vi{1,2,4}} == meta::value{std::in_place_type<vi>, {1,2,3}});
}
{
meta::value v1{&func};
meta::value v2{&func};
CHECK(v1 == v1);
CHECK_FALSE(v1 != v1);
CHECK(v1 == v2);
CHECK_FALSE(v1 != v2);
}
{
meta::value v1{clazz{}};
meta::value v2{clazz{}};
CHECK(v1 == v1);
CHECK_FALSE(v1 != v1);
CHECK(v1 != v2);
CHECK_FALSE(v1 == v2);
}
{
const int rv = 1;
meta::value v1{&rv};
meta::value v2{&rv};
CHECK(v1 == v1);
CHECK_FALSE(v1 != v1);
}
{
meta::value v1{"hello"s};
meta::value{1}.swap(v1);
CHECK(v1 == meta::value{1});
}
}
TEST_CASE("meta/value/swap") { TEST_CASE("meta/value/swap") {
namespace meta = meta_hpp; namespace meta = meta_hpp;
using namespace std::string_literals; using namespace std::string_literals;