meta: review overloaded meta functions
This commit is contained in:
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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{};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user