set and get member with static arguments

This commit is contained in:
BlackMATov
2021-08-16 07:34:41 +07:00
parent f229d992df
commit beb9e6dea3
2 changed files with 229 additions and 5 deletions

View File

@@ -21,6 +21,12 @@ namespace meta_hpp
const std::string& name() const noexcept;
const member_type& type() const noexcept;
template < typename Instance >
value get(Instance&& instance) const;
template < typename Instance, typename Value >
void set(Instance&& instance, Value&& value) const;
public:
template < typename F >
void visit(F&& f) const;
@@ -40,12 +46,71 @@ namespace meta_hpp
};
}
namespace meta_hpp::detail
{
using member_getter = std::function<value(const inst&)>;
using member_setter = std::function<void(const inst&, const arg&)>;
template < typename Member >
value raw_member_getter(Member member, const inst& inst) {
using mt = member_pointer_traits<Member>;
using class_type = typename mt::class_type;
using value_type = typename mt::value_type;
using qualified_type = const class_type;
if ( !inst.can_cast_to<qualified_type>() ) {
throw std::logic_error("an attempt to get a member with an incorrect instance type");
}
value_type return_value{std::invoke(member, inst.cast<qualified_type>())};
return value{std::forward<value_type>(return_value)};
}
template < typename Member >
void raw_member_setter(Member member, const inst& inst, const arg& arg) {
using mt = member_pointer_traits<Member>;
using class_type = typename mt::class_type;
using value_type = typename mt::value_type;
using qualified_type = class_type;
if constexpr ( !std::is_const_v<value_type> ) {
if ( !inst.can_cast_to<qualified_type>() ) {
throw std::logic_error("an attempt to set a member with an incorrect instance type");
}
if ( !arg.can_cast_to<value_type>() ) {
throw std::logic_error("an attempt to set a member with an incorrect argument type");
}
std::invoke(member, inst.cast<qualified_type>()) = arg.cast<value_type>();
} else {
throw std::logic_error("an attempt to set a constant member");
}
}
template < typename Member >
member_getter make_member_getter(Member member) {
using namespace std::placeholders;
return std::bind(&raw_member_getter<Member>, member, _1);
}
template < typename Member >
member_setter make_member_setter(Member member) {
using namespace std::placeholders;
return std::bind(&raw_member_setter<Member>, member, _1, _2);
}
}
namespace meta_hpp
{
struct member_info::state final {
std::string name;
member_type type;
data_info_map datas;
detail::member_getter getter;
detail::member_setter setter;
};
}
@@ -67,6 +132,16 @@ namespace meta_hpp
inline const member_type& member_info::type() const noexcept {
return state_->type;
}
template < typename Instance >
value member_info::get(Instance&& instance) const {
return state_->getter(inst{std::forward<Instance>(instance)});
}
template < typename Instance, typename Value >
void member_info::set(Instance&& instance, Value&& value) const {
state_->setter(inst{std::forward<Instance>(instance)}, arg{std::forward<Value>(value)});
}
}
namespace meta_hpp
@@ -91,12 +166,12 @@ namespace meta_hpp
namespace meta_hpp
{
template < typename Member >
member_info::member_info(std::string name, Member instance)
member_info::member_info(std::string name, Member member)
: state_{std::make_shared<state>(state{
std::move(name),
type_db::get<Member>().template as<member_type>(),
{}
})} {
(void)instance;
}
{},
detail::make_member_getter<Member>(member),
detail::make_member_setter<Member>(member),
})} {}
}

View File

@@ -8,7 +8,156 @@
namespace
{
using namespace meta_hpp;
using namespace std::string_literals;
}
namespace
{
struct ivec2 {
int x{};
int y{};
[[maybe_unused]] ivec2() = default;
[[maybe_unused]] explicit ivec2(int v): x{v}, y{v} {}
[[maybe_unused]] ivec2(int x, int y): x{x}, y{y} {}
};
struct ipnt2 {
const int x{};
const int y{};
[[maybe_unused]] ipnt2() = default;
[[maybe_unused]] explicit ipnt2(int v): x{v}, y{v} {}
[[maybe_unused]] ipnt2(int x, int y): x{x}, y{y} {}
};
bool operator==(const ivec2& l, const ivec2& r) noexcept {
return l.x == r.x && l.y == r.y;
}
bool operator==(const ipnt2& l, const ipnt2& r) noexcept {
return l.x == r.x && l.y == r.y;
}
}
TEST_CASE("features/infos/member") {
registry db;
db(
namespace_{"vmath"}(
class_<ivec2>{"ivec2"}(
member_{"x", &ivec2::x},
member_{"y", &ivec2::y}
),
class_<ipnt2>{"ipnt2"}(
member_{"x", &ipnt2::x},
member_{"y", &ipnt2::y}
)
)
);
SUBCASE("non_const") {
const member_info x_mi = db.get_member_by_path("vmath::ivec2::x");
const member_info y_mi = db.get_member_by_path("vmath::ivec2::y");
REQUIRE(x_mi);
REQUIRE(y_mi);
{
CHECK(x_mi.name() == "x");
CHECK(x_mi.type().class_type() == type_db::get<ivec2>());
CHECK(x_mi.type().value_type() == type_db::get<int>());
CHECK(y_mi.name() == "y");
CHECK(y_mi.type().class_type() == type_db::get<ivec2>());
CHECK(y_mi.type().value_type() == type_db::get<int>());
}
{
ivec2 v{1,2};
CHECK(x_mi.get(v).equals(1));
CHECK(x_mi.get(std::as_const(v)).equals(1));
CHECK(x_mi.get(std::move(v)).equals(1));
CHECK(x_mi.get(std::move(std::as_const(v))).equals(1));
CHECK(y_mi.get(v).equals(2));
CHECK(y_mi.get(std::as_const(v)).equals(2));
CHECK(y_mi.get(std::move(v)).equals(2));
CHECK(y_mi.get(std::move(std::as_const(v))).equals(2));
static_assert(std::is_invocable_v<decltype(&ivec2::x), ivec2&>);
static_assert(std::is_invocable_v<decltype(&ivec2::x), const ivec2&>);
static_assert(std::is_invocable_v<decltype(&ivec2::x), ivec2&&>);
static_assert(std::is_invocable_v<decltype(&ivec2::x), const ivec2&&>);
}
{
ivec2 v{1,2};
CHECK_NOTHROW(x_mi.set(v, 10)); CHECK(v.x == 10);
CHECK_THROWS(x_mi.set(std::as_const(v), 11)); CHECK(v.x == 10);
CHECK_NOTHROW(x_mi.set(std::move(v), 12)); CHECK(v.x == 12);
CHECK_THROWS(x_mi.set(std::move(std::as_const(v)), 13)); CHECK(v.x == 12);
CHECK_NOTHROW(y_mi.set(v, 20)); CHECK(v.y == 20);
CHECK_THROWS(y_mi.set(std::as_const(v), 21)); CHECK(v.y == 20);
CHECK_NOTHROW(y_mi.set(std::move(v), 22)); CHECK(v.y == 22);
CHECK_THROWS(y_mi.set(std::move(std::as_const(v)), 23)); CHECK(v.y == 22);
CHECK(v == ivec2{12,22});
}
}
SUBCASE("const") {
const member_info x_mi = db.get_member_by_path("vmath::ipnt2::x");
const member_info y_mi = db.get_member_by_path("vmath::ipnt2::y");
REQUIRE(x_mi);
REQUIRE(y_mi);
{
CHECK(x_mi.name() == "x");
CHECK(x_mi.type().class_type() == type_db::get<ipnt2>());
CHECK(x_mi.type().value_type() == type_db::get<const int>());
CHECK(y_mi.name() == "y");
CHECK(y_mi.type().class_type() == type_db::get<ipnt2>());
CHECK(y_mi.type().value_type() == type_db::get<const int>());
}
{
ipnt2 v{1,2};
CHECK(x_mi.get(v).equals(1));
CHECK(x_mi.get(std::as_const(v)).equals(1));
CHECK(x_mi.get(std::move(v)).equals(1));
CHECK(x_mi.get(std::move(std::as_const(v))).equals(1));
CHECK(y_mi.get(v).equals(2));
CHECK(y_mi.get(std::as_const(v)).equals(2));
CHECK(y_mi.get(std::move(v)).equals(2));
CHECK(y_mi.get(std::move(std::as_const(v))).equals(2));
static_assert(std::is_invocable_v<decltype(&ipnt2::x), ipnt2&>);
static_assert(std::is_invocable_v<decltype(&ipnt2::x), const ipnt2&>);
static_assert(std::is_invocable_v<decltype(&ipnt2::x), ipnt2&&>);
static_assert(std::is_invocable_v<decltype(&ipnt2::x), const ipnt2&&>);
}
{
ipnt2 v{1,2};
CHECK_THROWS(x_mi.set(v, 10)); CHECK(v.x == 1);
CHECK_THROWS(x_mi.set(std::as_const(v), 11)); CHECK(v.x == 1);
CHECK_THROWS(x_mi.set(std::move(v), 12)); CHECK(v.x == 1);
CHECK_THROWS(x_mi.set(std::move(std::as_const(v)), 13)); CHECK(v.x == 1);
CHECK_THROWS(y_mi.set(v, 20)); CHECK(v.y == 2);
CHECK_THROWS(y_mi.set(std::as_const(v), 21)); CHECK(v.y == 2);
CHECK_THROWS(y_mi.set(std::move(v), 22)); CHECK(v.y == 2);
CHECK_THROWS(y_mi.set(std::move(std::as_const(v)), 23)); CHECK(v.y == 2);
CHECK(v == ipnt2{1,2});
}
}
}