Skip to content

Commit

Permalink
Update generic wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
HenryAWE committed Sep 16, 2024
1 parent 16f1f63 commit 5f07ad1
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 62 deletions.
228 changes: 178 additions & 50 deletions include/asbind20/bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,54 @@ class register_helper_base
asIScriptEngine* const m_engine;
};

template <typename T>
concept native_function =
std::is_function_v<T> ||
std::is_function_v<std::remove_pointer_t<T>> ||
std::is_member_function_pointer_v<T>;
namespace detail
{
template <typename T, typename Class>
static constexpr bool is_this_arg_v =
std::is_same_v<T, Class*> ||
std::is_same_v<T, const Class*> ||
std::is_same_v<T, Class&> ||
std::is_same_v<T, const Class&>;

template <typename Class, typename... Args>
static consteval asECallConvTypes deduce_method_callconv() noexcept
{
using args_t = std::tuple<Args...>;
constexpr std::size_t arg_count = sizeof...(Args);
using first_arg_t = std::tuple_element_t<0, args_t>;
using last_arg_t = std::tuple_element_t<sizeof...(Args) - 1, args_t>;

if constexpr(arg_count == 1 && std::is_same_v<first_arg_t, asIScriptGeneric*>)
return asCALL_GENERIC;
else
{
constexpr bool obj_first = is_this_arg_v<std::remove_cv_t<first_arg_t>, Class>;
constexpr bool obj_last = is_this_arg_v<std::remove_cv_t<last_arg_t>, Class> && arg_count != 1;

static_assert(obj_last || obj_first, "Missing object parameter");

if(obj_first)
return arg_count == 1 ? asCALL_CDECL_OBJLAST : asCALL_CDECL_OBJFIRST;
else
return asCALL_CDECL_OBJLAST;
}
}
} // namespace detail

using generic_function_t = void(asIScriptGeneric* gen);

template <native_function auto Function, asECallConvTypes OriginalConv>
template <typename T>
concept native_function =
!std::is_convertible_v<T, generic_function_t*> &&
(std::is_function_v<T> ||
std::is_function_v<std::remove_pointer_t<T>> ||
std::is_member_function_pointer_v<T>);

template <
native_function auto Function,
asECallConvTypes OriginalConv>
requires(OriginalConv != asCALL_GENERIC)
class generic_wrapper
class generic_wrapper_t
{
public:
using function_type = decltype(Function);
Expand All @@ -205,6 +242,20 @@ class generic_wrapper
"Invalid calling convention"
);

constexpr generic_wrapper_t() noexcept = default;

constexpr generic_wrapper_t(const generic_wrapper_t&) noexcept = default;

static constexpr function_type underlying_function() noexcept
{
return Function;
}

static constexpr asECallConvTypes underlying_convention() noexcept
{
return OriginalConv;
}

static constexpr generic_function_t* generate()
{
return &wrapper_impl;
Expand All @@ -216,20 +267,50 @@ class generic_wrapper
}

private:
static decltype(auto) get_this_arg(asIScriptGeneric* gen)
{
using traits = function_traits<function_type>;

void* ptr = gen->GetObject();

if constexpr(OriginalConv == asCALL_THISCALL)
{
using pointer_t = typename traits::class_type*;
return static_cast<pointer_t>(ptr);
}
else if constexpr(OriginalConv == asCALL_CDECL_OBJFIRST)
{
using this_arg_t = typename traits::first_arg_type;
if constexpr(std::is_pointer_v<this_arg_t>)
return static_cast<this_arg_t>(ptr);
else
return *static_cast<std::remove_reference_t<this_arg_t>*>(ptr);
}
else if constexpr(OriginalConv == asCALL_CDECL_OBJLAST)
{
using this_arg_t = typename traits::last_arg_type;
if constexpr(std::is_pointer_v<this_arg_t>)
return static_cast<this_arg_t>(ptr);
else
return *static_cast<std::remove_reference_t<this_arg_t>*>(ptr);
}
else
static_assert(!OriginalConv, "Invalid type");
}

static void wrapper_impl(asIScriptGeneric* gen)
{
using traits = function_traits<decltype(Function)>;
using traits = function_traits<function_type>;

if constexpr(traits::is_method::value)
if constexpr(OriginalConv == asCALL_THISCALL)
{
[gen]<std::size_t... Is>(std::index_sequence<Is...>)
{
auto* this_ = (typename traits::class_type*)gen->GetObject();
if constexpr(std::is_void_v<typename traits::return_type>)
{
std::invoke(
Function,
this_,
get_this_arg(gen),
get_generic_arg<typename traits::template arg_type<Is>>(
gen, static_cast<asUINT>(Is)
)...
Expand All @@ -241,7 +322,7 @@ class generic_wrapper
gen,
std::invoke(
Function,
this_,
get_this_arg(gen),
get_generic_arg<typename traits::template arg_type<Is>>(
gen, static_cast<asUINT>(Is)
)...
Expand All @@ -250,16 +331,51 @@ class generic_wrapper
}
}(std::make_index_sequence<traits::arg_count::value>());
}
else
else if constexpr(OriginalConv == asCALL_CDECL_OBJFIRST)
{
static_assert(traits::arg_count::value >= 1);

[gen]<std::size_t... Is>(std::index_sequence<Is...>)
{
if constexpr(std::is_void_v<typename traits::return_type>)
{
std::invoke(
Function,
get_this_arg(gen),
get_generic_arg<typename traits::template arg_type<Is + 1>>(
gen, static_cast<asUINT>(Is)
)...
);
}
else
{
set_generic_return<typename traits::return_type>(
gen,
std::invoke(
Function,
get_this_arg(gen),
get_generic_arg<typename traits::template arg_type<Is + 1>>(
gen, static_cast<asUINT>(Is)
)...
)
);
}
}(std::make_index_sequence<traits::arg_count::value - 1>());
}
else if constexpr(OriginalConv == asCALL_CDECL_OBJLAST)
{
static_assert(traits::arg_count::value >= 1);

get_generic_arg<typename traits::template arg_type<Is>>(gen, static_cast<asUINT>(Is))...
[gen]<std::size_t... Is>(std::index_sequence<Is...>)
{
if constexpr(std::is_void_v<typename traits::return_type>)
{
std::invoke(
Function,
get_generic_arg<typename traits::template arg_type<Is>>(
gen, static_cast<asUINT>(Is)
)...,
get_this_arg(gen)
);
}
else
Expand All @@ -268,7 +384,37 @@ class generic_wrapper
gen,
std::invoke(
Function,
get_generic_arg<typename traits::template arg_type<Is>>(gen, static_cast<asUINT>(Is))...
get_generic_arg<typename traits::template arg_type<Is>>(
gen, static_cast<asUINT>(Is)
)...,
get_this_arg(gen)
)
);
}
}(std::make_index_sequence<traits::arg_count::value - 1>());
}
else
{
[gen]<std::size_t... Is>(std::index_sequence<Is...>)
{
if constexpr(std::is_void_v<typename traits::return_type>)
{
std::invoke(
Function,
get_generic_arg<typename traits::template arg_type<Is>>(
gen, static_cast<asUINT>(Is)
)...
);
}
else
{
set_generic_return<typename traits::return_type>(
gen,
std::invoke(
Function,
get_generic_arg<typename traits::template arg_type<Is>>(
gen, static_cast<asUINT>(Is)
)...
)
);
}
Expand All @@ -277,6 +423,11 @@ class generic_wrapper
}
};

template <
native_function auto Function,
asECallConvTypes OriginalConv>
constexpr inline generic_wrapper_t<Function, OriginalConv> generic_wrapper{};

namespace detail
{
class class_register_helper_base : public register_helper_base
Expand Down Expand Up @@ -375,50 +526,19 @@ namespace detail
assert(r >= 0);
}

template <typename T, typename Class>
static constexpr bool is_this_arg_v =
std::is_same_v<T, Class*> ||
std::is_same_v<T, const Class*> ||
std::is_same_v<T, Class&> ||
std::is_same_v<T, const Class&>;

template <typename Class, typename... Args>
static consteval asECallConvTypes call_conv_from_args() noexcept
{
using args_t = std::tuple<Args...>;
constexpr std::size_t arg_count = sizeof...(Args);
using first_arg_t = std::tuple_element_t<0, args_t>;
using last_arg_t = std::tuple_element_t<sizeof...(Args) - 1, args_t>;

if constexpr(arg_count == 1 && std::is_same_v<first_arg_t, asIScriptGeneric*>)
return asCALL_GENERIC;
else
{
constexpr bool obj_first = is_this_arg_v<std::remove_cv_t<first_arg_t>, Class>;
constexpr bool obj_last = is_this_arg_v<std::remove_cv_t<last_arg_t>, Class> && arg_count != 1;

static_assert(obj_last || obj_first, "Missing object parameter");

if(obj_first)
return asCALL_CDECL_OBJFIRST;
else
return asCALL_CDECL_OBJLAST;
}
}

template <typename Class, typename R, typename... Args>
void method_wrapper_impl(const char* decl, R (*fn)(Args...))
{
method_impl(
decl, fn, call_conv<call_conv_from_args<Class, Args...>()>
decl, fn, call_conv<deduce_method_callconv<Class, Args...>()>
);
}

template <typename Class, typename R, typename... Args>
void behaviour_wrapper_impl(asEBehaviours beh, const char* decl, R (*fn)(Args...))
{
behaviour_impl(
beh, decl, fn, call_conv<call_conv_from_args<Class, Args...>()>
beh, decl, fn, call_conv<deduce_method_callconv<Class, Args...>()>
);
}

Expand Down Expand Up @@ -985,7 +1105,7 @@ class value_class : private detail::class_register_helper_base
return *this;
}

template <typename Fn>
template <native_function Fn>
requires(std::is_member_function_pointer_v<Fn>)
value_class& method(const char* decl, Fn&& fn)
{
Expand All @@ -994,14 +1114,22 @@ class value_class : private detail::class_register_helper_base
return *this;
}

template <typename Fn, asECallConvTypes CallConv>
template <native_function Fn, asECallConvTypes CallConv>
requires(CallConv != asCALL_GENERIC)
value_class& method(const char* decl, Fn&& fn, call_conv_t<CallConv>)
{
method_impl(decl, std::forward<Fn>(fn), call_conv<CallConv>);

return *this;
}

value_class& method(const char* decl, generic_function_t* gfn)
{
method_impl(decl, gfn, call_conv<asCALL_GENERIC>);

return *this;
}

template <typename R, typename... Args>
value_class& method(const char* decl, R (*fn)(Args...))
{
Expand Down
Loading

0 comments on commit 5f07ad1

Please sign in to comment.