meta: review overloaded meta functions

This commit is contained in:
Michele Caini
2020-10-25 14:52:02 +01:00
parent 24f937fe4f
commit 92f79c99f7
4 changed files with 98 additions and 129 deletions

View File

@@ -675,8 +675,8 @@ public:
static internal::meta_func_node node{
{},
type,
nullptr, // parent
nullptr, // next
nullptr,
nullptr,
std::tuple_size_v<typename helper_type::args_type>,
helper_type::is_const,
helper_type::is_static,
@@ -689,24 +689,13 @@ public:
ENTT_ASSERT(!exists(&node, type->func));
if (exists(id, type->func)) {
auto const existing_overload =
internal::find_if<&std::decay_t<decltype(*type)>::func>(
[id](const auto* curr) {
return id == curr->id;
}, type);
ENTT_ASSERT(existing_overload);
node.next = existing_overload->next;
existing_overload->next = &node;
}
else {
node.next = type->func;
type->func = &node;
}
internal::meta_func_node **it = &type->func;
for(; *it && (*it)->id != id; it = &(*it)->next);
for(; *it && (*it)->id == id && (*it)->size < node.size; it = &(*it)->next);
node.id = id;
node.next = *it;
*it = &node;
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
}

View File

@@ -362,24 +362,19 @@ private:
template<auto Member, typename Op>
auto find_if(const Op &op, const meta_type_node *node)
-> std::decay_t<decltype(node->*Member)> {
std::decay_t<decltype(node->*Member)> ret = nullptr;
for(auto &&curr: meta_range{node->*Member}) {
if(op(&curr)) {
ret = &curr;
break;
return &curr;
}
}
if(!ret) {
for(auto &&curr: meta_range{node->base}) {
if(ret = find_if<Member>(op, curr.type()); ret) {
break;
}
for(auto &&curr: meta_range{node->base}) {
if(auto *ret = find_if<Member>(op, curr.type()); ret) {
return ret;
}
}
return ret;
return nullptr;
}

View File

@@ -735,7 +735,7 @@ struct meta_ctor {
* @param sz Number of parameters to use to construct the instance.
* @return A meta any containing the new instance, if any.
*/
[[nodiscard]] meta_any invoke(meta_any * const args, const std::size_t sz) const {
[[nodiscard]] meta_any invoke(meta_any * const args, const size_type sz) const {
return sz == size() ? node->invoke(args) : meta_any{};
}
@@ -954,7 +954,7 @@ struct meta_func {
* @param sz Number of parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
meta_any invoke(meta_handle instance, meta_any * const args, const std::size_t sz) const {
meta_any invoke(meta_handle instance, meta_any * const args, const size_type sz) const {
return sz == size() ? node->invoke(std::move(instance), args) : meta_any{};
}
@@ -1004,17 +1004,20 @@ private:
/*! @brief Opaque wrapper for meta types. */
class meta_type {
template<typename... Args, std::size_t... Indexes>
[[nodiscard]] auto ctor(std::index_sequence<Indexes...>) const {
internal::meta_range range{node->ctor};
bool can_cast_or_convert(const meta_type type, const type_info info) const ENTT_NOEXCEPT {
for(auto conv: type.conv()) {
if(conv.type().info() == info) {
return true;
}
}
return std::find_if(range.begin(), range.end(), [](const auto &candidate) {
return candidate.size == sizeof...(Args) && ([](auto *from, auto *to) {
return (from->info == to->info)
|| internal::find_if<&node_type::base>([to](const auto *curr) { return curr->type()->info == to->info; }, from)
|| internal::find_if<&node_type::conv>([to](const auto *curr) { return curr->type()->info == to->info; }, from);
}(internal::meta_info<Args>::resolve(), candidate.arg(Indexes)) && ...);
}).operator->();
for(auto base: type.base()) {
if(base.type().info() == info || can_cast_or_convert(base.type(), info)) {
return true;
}
}
return false;
}
public:
@@ -1268,7 +1271,13 @@ public:
*/
template<typename... Args>
[[nodiscard]] meta_ctor ctor() const {
return ctor<Args...>(std::index_sequence_for<Args...>{});
for(const auto &candidate: internal::meta_range{node->ctor}) {
if(size_type index{}; candidate.size == sizeof...(Args) && ([this](auto *from, auto *to) { return from->info == to->info || can_cast_or_convert(from, to->info); }(internal::meta_info<Args>::resolve(), candidate.arg(index++)) && ...)) {
return &candidate;
}
}
return nullptr;
}
/**
@@ -1304,7 +1313,9 @@ public:
/**
* @brief Returns the meta function associated with a given identifier.
*
* The meta functions of the base classes will also be visited, if any.
* The meta functions of the base classes will also be visited, if any.<br/>
* In the case of overloaded meta functions, the first one with the required
* id will be returned.
*
* @param id Unique identifier.
* @return The meta function associated with the given identifier, if any.
@@ -1326,7 +1337,7 @@ public:
* @param sz Number of parameters to use to construct the instance.
* @return A meta any containing the new instance, if any.
*/
[[nodiscard]] meta_any construct(meta_any * const args, const std::size_t sz) const {
[[nodiscard]] meta_any construct(meta_any * const args, const size_type sz) const {
meta_any any{};
internal::find_if<&node_type::ctor>([args, sz, &any](const auto *curr) {
@@ -1367,59 +1378,33 @@ public:
* @param sz Number of parameters to use to invoke the function.
* @return A meta any containing the returned value, if any.
*/
meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const std::size_t sz) const {
const internal::meta_func_node* best_match{ nullptr };
internal::meta_func_node::size_type best_match_casts_required{0};
meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const size_type sz) const {
const internal::meta_func_node* candidate{};
size_type extent{sz + 1u};
bool ambiguous{};
for (auto candidate = internal::find_if<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node); candidate && (id == candidate->id); candidate = candidate->next) {
if (sz != candidate->size) {
continue;
for(auto *it = internal::find_if<&node_type::func>([id, sz](const auto *curr) { return curr->id == id && curr->size == sz; }, node); it && it->id == id && it->size == sz; it = it->next) {
size_type direct{};
size_type ext{};
for(size_type next{}; next < sz && next == (direct + ext); ++next) {
const auto type = args[next].type();
const auto req = it->arg(next)->info;
type.info() == req ? ++direct : (ext += can_cast_or_convert(type, req));
}
node_type::size_type casts_required{ 0 };
bool match{ true };
for (node_type::size_type arg_idx = 0; arg_idx < sz; ++arg_idx) {
auto const& candidate_arg_type_info = candidate->arg (arg_idx)->info;
auto const arg_type = (args + arg_idx)->type();
ENTT_ASSERT(bool(candidate_arg_type_info));
if (candidate_arg_type_info == arg_type.info()) {
continue;
if((direct + ext) == sz) {
if(ext < extent) {
candidate = it;
extent = ext;
ambiguous = false;
} else if(ext == extent) {
ambiguous = true;
}
if (auto const arg_convs = arg_type.conv();
arg_convs.end () != std::find_if(
arg_convs.begin(),
arg_convs.end(),
[candidate_arg_type_info](auto&& curr) {
return curr.type().info() == candidate_arg_type_info;
})) {
casts_required++;
}
else {
match = false;
break;
}
}
if (!match) {
continue;
}
if ((0 < best_match_casts_required) && (casts_required == best_match_casts_required)) {
return meta_any{};
}
if (!best_match || (casts_required < best_match_casts_required)) {
best_match = candidate;
best_match_casts_required = casts_required;
}
}
return best_match ? best_match->invoke (instance, args) : meta_any{};
return (candidate && !ambiguous) ? candidate->invoke(instance, args) : meta_any{};
}
/**

View File

@@ -49,7 +49,7 @@ struct clazz_t {
};
struct overloaded_func_t {
void e (int v) {
void e(int v) {
value = v + v;
}
@@ -59,19 +59,19 @@ struct overloaded_func_t {
int f(int a, int b) {
value = a;
return b*b;
return b * b;
}
int f(int v) const {
return v*v;
return v * v;
}
float f (int a, float b) {
float f(int a, float b) {
value = a;
return b + b;
}
void g (int v) {
void g(int v) {
value = v * v;
}
@@ -100,12 +100,12 @@ struct MetaType: ::testing::Test {
entt::meta<concrete_t>().base<base_t>().base<abstract_t>();
entt::meta<overloaded_func_t>().type("overloaded_func"_hs)
.func<&overloaded_func_t::e> ("e"_hs)
.func<entt::overload<int(const base_t &, int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int) const>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<float (int, float)> (&overloaded_func_t::f)> ("f"_hs)
.func<&overloaded_func_t::g> ("g"_hs);
.func<&overloaded_func_t::e> ("e"_hs)
.func<entt::overload<int(const base_t &, int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int, int)>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<int(int) const>(&overloaded_func_t::f)>("f"_hs)
.func<entt::overload<float(int, float)> (&overloaded_func_t::f)> ("f"_hs)
.func<&overloaded_func_t::g> ("g"_hs);
entt::meta<property_t>()
.data<property_t::random>("random"_hs)
@@ -326,54 +326,54 @@ TEST_F(MetaType, Invoke) {
}
TEST_F(MetaType, OverloadedFunc) {
entt::meta<float> ().conv<int>();
entt::meta<float>().conv<int>();
entt::meta<double>().conv<float>();
auto type = entt::resolve<overloaded_func_t>();
const auto type = entt::resolve<overloaded_func_t>();
overloaded_func_t instance{};
ASSERT_TRUE(type.func("f"_hs));
ASSERT_TRUE(type.func("e"_hs));
ASSERT_TRUE(type.func("g"_hs));
auto const first_overload_result = type.invoke("f"_hs, instance, base_t{}, 1, 2);
const auto first = type.invoke("f"_hs, instance, base_t{}, 1, 2);
ASSERT_TRUE(first_overload_result);
ASSERT_EQ (overloaded_func_t::value, 1);
ASSERT_TRUE (first_overload_result.try_cast<int>());
ASSERT_EQ (first_overload_result.cast<int>(), 4);
ASSERT_TRUE(first);
ASSERT_EQ(overloaded_func_t::value, 1);
ASSERT_NE(first.try_cast<int>(), nullptr);
ASSERT_EQ(first.cast<int>(), 4);
auto const second_overload_result = type.invoke("f"_hs, instance, 3, 4);
const auto second = type.invoke("f"_hs, instance, 3, 4);
ASSERT_TRUE (second_overload_result);
ASSERT_EQ (overloaded_func_t::value, 3);
ASSERT_TRUE (second_overload_result.try_cast<int>());
ASSERT_EQ (second_overload_result.cast<int> (), 16);
ASSERT_TRUE(second);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(second.try_cast<int>(), nullptr);
ASSERT_EQ(second.cast<int>(), 16);
auto const third_overload_result = type.invoke("f"_hs, instance, 5);
const auto third = type.invoke("f"_hs, instance, 5);
ASSERT_TRUE (third_overload_result);
ASSERT_EQ (overloaded_func_t::value, 3);
ASSERT_TRUE (third_overload_result.try_cast<int>());
ASSERT_EQ (third_overload_result.cast<int> (), 25);
ASSERT_TRUE(third);
ASSERT_EQ(overloaded_func_t::value, 3);
ASSERT_NE(third.try_cast<int>(), nullptr);
ASSERT_EQ(third.cast<int>(), 25);
auto const fourth_overload_result = type.invoke("f"_hs, instance, 6, 7.0f);
const auto fourth = type.invoke("f"_hs, instance, 6, 7.f);
ASSERT_TRUE (fourth_overload_result);
ASSERT_EQ (overloaded_func_t::value, 6);
ASSERT_TRUE (fourth_overload_result.try_cast<float>());
ASSERT_EQ (fourth_overload_result.cast<float> (), 14.0f);
ASSERT_TRUE(fourth);
ASSERT_EQ(overloaded_func_t::value, 6);
ASSERT_NE(fourth.try_cast<float>(), nullptr);
ASSERT_EQ(fourth.cast<float>(), 14.f);
auto const overload_with_cast_result = type.invoke("f"_hs, instance, 8, 9.0f);
const auto cast = type.invoke("f"_hs, instance, 8, 9.f);
ASSERT_TRUE (overload_with_cast_result);
ASSERT_EQ (overloaded_func_t::value, 8);
ASSERT_TRUE (overload_with_cast_result.try_cast<float>());
ASSERT_EQ (overload_with_cast_result.cast<float> (), 18);
ASSERT_TRUE(cast);
ASSERT_EQ(overloaded_func_t::value, 8);
ASSERT_NE(cast.try_cast<float>(), nullptr);
ASSERT_EQ(cast.cast<float>(), 18.f);
auto const ambiguous_overload_result = type.invoke("f"_hs, instance, 8, 9.0);
const auto ambiguous = type.invoke("f"_hs, instance, 8, 9.);
ASSERT_FALSE (ambiguous_overload_result);
ASSERT_FALSE(ambiguous);
}
TEST_F(MetaType, SetGet) {