I am attempting to create a class template whose constructor(s) can take any kind of function as argument, that is, it takes a function pointer (which can be a member function pointer) and the corresponding function arguments. Additionally, there should be a static_assert that checks whether the function return type (taken from the function pointer) matches the class template parameter type. Thus, the code should look something like this:
template <class ReturnType>
struct Bar
{
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType (* func)(ParamType ...), ParamType && ... args) :
package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
template <class RetType, class ObjType, class ... ParamType>
Bar<ReturnType>(RetType (ObjType::* func)(ParamType ...), ObjType * obj, ParamType && ... args) :
package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
std::packaged_task<ReturnType()> package_;
std::function<void()> function_;
std::future<ReturnType> future_;
};
The idea is that the code compiles for these situations, and allows for Bar::function_ to be called (through the function call operator) without errors:
struct Foo
{
int foo(int i) {
return i;
}
int foo() {
return 1;
}
};
int foo(int i)
{
return i;
}
int foo()
{
return 1;
}
int main()
{
Foo f = Foo();
Bar<int> b1(&Foo::foo, &f, 1);
Bar<int> b2(&Foo::foo, &f);
Bar<int> b3(foo, 1);
Bar<int> b4(foo);
return 0;
}
Unfortunately, I have close to zero experience with template metaprogramming, and even though I have ran over several questions here in SO, and attempted several ways of solving my problem, such as using a more generalized approach to the constructor
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType func, ParamType && ... args)
and combining it with type_traits to determine the return type), I have yet to find a way to make this work. What changes can I do to the constructor(s) that allow this functionality?
Edit:
max66's answer solved my original problem, however, a new one arose, which I hadn't considered in the previous question. I also want to be able to pass variables to the constructor, like so:
int main()
{
Foo f = Foo();
int i = 1;
Bar<int> b1(&Foo::foo, &f, i); // Error
Bar<int> b2(&Foo::foo, &f, 1); // Ok
Bar<int> b3(&Foo::foo, &f); // Ok
Bar<int> b4(foo, i); // Error
Bar<int> b5(foo, 1); // Ok
Bar<int> b6(foo); // Ok
return 0;
}
however, as it is, a compiler error shows up in the cases marked with Error. I am guessing this is because the parameter func in the constructor uses ParamType to determine its type (which doesn't match with the actual ParamTypes in the case of b1 and b4), but I have no idea how to solve this...
You probably want to use std::invoke. It handles working with member function pointers and regular functions for you.
As an outline of the sort of stuff you can do:
#include <functional>
#include <type_traits>
#include <utility>
template<typename F>
class Bar
{
F f_;
public:
template<typename TF>
Bar(TF && f)
: f_{ std::forward<TF>(f) }
{}
template<typename... Args>
decltype(auto) operator()(Args &&... args) {
return std::invoke(f_, std::forward<Args>(args)...);
}
};
template<typename F>
auto make_bar(F && f)
{
return Bar<std::decay_t<F>>{ std::forward<F>(f) };
}
It can be used like so:
auto b1 = make_bar(&f);
auto result = b1(myArg1, myArg2); // etc
auto b2 = make_bar(&Foo::fn);
auto result = b1(foo, arg1);
In the very least, I would recommend having Bar take the function object type as a template parameter so that you don't have to use std::function, but if you do want to use the exact calling syntax you have, it can be done using std::invoke and std::invoke_result as well.
Sorry but... if you want that the return type of the funtion is equal to the template parameter of the class... why don't you simply impose it?
I mean... you can use ReturnType instead of RetType, as follows
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...), ParamType && ... args)
: package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType && ... args)
: package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
-- EDIT --
To solve the second problem, IF your not interested in moving parameters, you can throw away std::forward and &&, and simply write
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...),
ParamType const & ... args)
: package_(std::bind(func, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType const & ... args)
: package_(std::bind(func, obj, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
Related
The goal
I try to create a set of classes that removes boilerplate code for implementing extensions to a game in C++.
For that, I have a designated value class, that can hold one of the following types:
float, std::string, bool, std::vector<value>, void
For that, I would like to have a host class to which I can add one or more method instances like follows:
using namespace std::string_literals;
host h;
h.add(
method<bool, req<std::string>, req<std::string>, opt<bool>>("compare_strings"s,
[](std::string s_orig, std::string s_comp, std::optional<bool> ingore_case) -> bool {
if (ignore_case.has_value() && ignore_case.value()) {
// ... lowercase both
}
return s_orig.compare(s_comp) == 0;
}));
Note that req<T> should be a meta info that a given value is required, opt<T> a meta info that a given value is not required and may only be provided after all required parameters.
The host class now contains a method execute(std::string function, std::vector<value> values) with function and values originating from a method getting char* for method and ´char** argv+ int argcfor values. Theexecutemethod now is supposed to call the correctmethod` instances function
value host::execute(std::string function, std::vector<value> values) {
// get matching method group
std::vector<method> mthds = m_methods[function];
// get matching parameter list
for (method& mthd : mthds) {
if (mthd.can_call(mthds, values)) {
// call generic method
auto res = mthd.call_generic(values);
// pass result back to callee
// return [...]
}
}
// return error back to callee
// return [...]
}
which means that the actual method class now needs to mangle two methods properly can_call and call_generic.
The value class has corresponding template<typename T> bool is() and template<typename T> T get() methods.
What remains
I did have other attempts at this, but as those failed, I deleted them (not very smart in hindside, but needed to get the whole thing out as another person relied on the results working) and now cannot figure out another attempt then prior ... so this is what I am left with as of now:
class method_base
{
public:
template<typename T> struct in { using type = T; };
template<typename T> struct opt { using type = T; };
public:
virtual bool can_call(std::vector<sqf::value> values) = 0;
virtual sqf::value call_generic(std::vector<sqf::value> values) = 0;
};
template<typename T, typename ... TArgs>
class method : public method_base
{
func m_func;
sqf::value val
public:
using func = T(*)(TArgs...);
method(func f) : m_func(f) {}
virtual retval can_call(std::vector<sqf::value> values) override
{
}
};
Appendix
If something is unclear, confusing or you just have further questions, please do ask them. I will try my best to rephrase whatever is unclear as this will help greatly with developing further extensions in the future, possibly defining a "go to" way for how to create extensions in the community for the game in question (Arma 3 just in case somebody wondered)
I may note that this is pretty much my first deep dive into meta programming so things I present may not be possible at all. If so, I kindly would like to ask you if you may also explain why that is so and the thing I attempt is not possible.
The Solution
I do want to express my thanks to all who answered this question again. I ended up combining pretty much parts of all solutions here and pretty much learned a lot on the way. The final implementation I ended up with looks like the following:
namespace meta
{
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
template <typename ArgType>
struct def_value { static ArgType value() { return {}; } };
template <typename ArgType>
struct get_type { using type = ArgType; };
template <typename ArgType>
struct get_type<std::optional<ArgType>> { using type = ArgType; };
}
struct method {
std::function<bool(const std::vector<value>&)> m_can_call;
std::function<value(const std::vector<value>&)> m_call;
template <typename ... Args, std::size_t... IndexSequence>
static bool can_call_impl(const std::vector<value>& values, std::index_sequence<IndexSequence...> s) {
// values max args
return values.size() <= sizeof...(Args) &&
// for every Arg, either...
(... && (
// the value provides that argument and its the correct type, or...
(IndexSequence < values.size() && sqf::is<sqf::meta::get_type<Args>::type>(values[IndexSequence])) ||
// the value does not provide that argument and the arg is an optional
(IndexSequence >= values.size() && sqf::meta::is_optional_v<Args>)
));
}
template <typename Ret, typename ... Args, std::size_t... IndexSequence>
static value call_impl(std::function<Ret(Args...)> f, const std::vector<value>& values, std::index_sequence<IndexSequence...>) {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(f,
(IndexSequence < values.size() ? sqf::get<sqf::meta::get_type<Args>::type>(values[IndexSequence])
: sqf::meta::def_value<Args>::value())...)
};
}
public:
template <typename Ret, typename ... Args>
method(std::function<Ret(Args...)> f) :
m_can_call([](const std::vector<value>& values) -> bool
{
return can_call_impl<Args...>(values, std::index_sequence_for<Args...>{});
}),
m_call([f](const std::vector<value>& values) -> value
{
return call_impl<Ret, Args...>(f, values, std::index_sequence_for<Args...>{});
})
{
}
bool can_call(const std::vector<value>& values) const { return m_can_call(values); }
value call_generic(const std::vector<value>& values) const { return m_call(values); }
// to handle lambda
template <typename F>
method static create(F f) { return method{ std::function{f} }; }
};
Assumming a way to check current type of value (template <typename T> bool value::isA<T>()) and a way to retrieve the value (template <typename T> /*const*/T& get(/*const*/ value&))
It seems you might do:
struct method
{
template <typename Ret, typename ... Ts>
method(std::function<Ret(Ts...)> f) : method(std::index_sequence<sizeof...(Ts)>(), f)
{}
template <typename Ret, typename ... Ts, std::size_t ... Is>
method(std::index_sequence<Is...>, std::function<Ret(Ts...)> f) :
isOk([](const std::vector<value>& values) {
return ((values.size() == sizeof...(Is)) && ... && values[Is].isA<Ts>());
}),
call([f](const std::vector<value>& values){
return f(get<Ts>(values[Is])...);
})
{}
// to handle lambda
template <typename F>
static fromCallable(F f) { return method{std::function{f}}; }
std::function<bool(const std::vector<value>&)> isOk;
std::function<value(const std::vector<value>&)> call;
};
Here's a quick example including the machinery for ret<T> and opt<T>. You haven't given any information on what value is, so I'm going to assume something like:
struct value {
// using `std::monostate` instead of `void`
std::variant<float, std::string, bool, std::vector<value>, std::monostate> data;
};
(I'm assuming c++17 for this answer.)
From there, we need our metatypes and a few traits to branch off them. I implement them using partial specialisations, but there are other ways too.
// types to determine optional vs. required
template <typename T>
struct req { using type = T; };
template <typename T>
struct opt { using type = T; };
// trait to determine if it's an optional type
template <typename ArgType>
struct is_optional : std::false_type {};
template <typename T>
struct is_optional<opt<T>> : std::true_type {};
template <typename ArgType>
inline constexpr bool is_optional_v = is_optional<ArgType>::value;
// get the "real" function parameter type
template <typename ArgType>
struct real_type;
template <typename ArgType>
using real_type_t = typename real_type<ArgType>::type;
template <typename T>
struct real_type<req<T>> { using type = T; };
template <typename T>
struct real_type<opt<T>> { using type = std::optional<T>; };
Now we implement method. I'll use a similar polymorphic relationship with method_base as you do in your partial demo; I also template on the function type passed in, to allow e.g. the functions to use const references to the type instead of the type itself.
The implementation itself uses the common trick of delegating to helper functions with std::index_sequence and fold expressions to "iterate" through the variadic template args.
// base class for polymorphism
struct method_base {
virtual ~method_base() = default;
virtual bool can_call(const std::vector<value>& values) const = 0;
virtual value call_generic(const std::vector<value>& values) const = 0;
};
// provide a different method implementation for each set of args
// I also overload on
template<typename RetType, typename Fn, typename... Args>
struct method : method_base {
private:
Fn func;
static_assert(std::is_invocable_r_v<RetType, Fn, real_type_t<Args>...>,
"function must be callable with given args");
public:
// accept any function that looks sort of like what we expect;
// static assert above makes sure it's sensible
template <typename G>
method(G&& func) : func(std::forward<G>(func)) {}
template <std::size_t... Is>
bool can_call_impl(const std::vector<value>& values, std::index_sequence<Is...>) const {
// for every Arg, either...
return (... and (
// the value provides that argument and its the correct type, or...
(Is < values.size() and std::holds_alternative<typename Args::type>(values[Is].data))
// the value does not provide that argument and the arg is an optional
or (Is >= values.size() and is_optional_v<Args>)
));
}
bool can_call(const std::vector<value>& values) const override {
return can_call_impl(values, std::index_sequence_for<Args...>{});
}
template <std::size_t... Is>
value call_generic_impl(const std::vector<value>& values, std::index_sequence<Is...>) const {
return {
// call the function with every type in the value set,
// padding with empty std::optionals otherwise
std::invoke(func,
(Is < values.size() ? std::get<typename Args::type>(values[Is].data)
: real_type_t<Args>{})...)
};
}
value call_generic(const std::vector<value>& values) const override {
return call_generic_impl(values, std::index_sequence_for<Args...>{});
}
};
I'll also create a helper function to make methods:
template <typename RetType, typename... Args, typename Fn>
std::unique_ptr<method_base> make_method(Fn&& func) {
return std::make_unique<method<RetType, std::decay_t<Fn>, Args...>>(std::forward<Fn>(func));
}
Live example.
It's not perfect, but this should give you a general idea of how to do it.
Change your method to:
method< R(Args...) >
your tags seem useless. Detect optional with ... std::optional.
For storage, use std variant. Use some non-void type for void (I don't care what).
As a first pass we aim for perfect compatibility.
template<class...Args>
struct check_signature {
bool operator()( std::span<value const> values ) const {
if (sizeof...(Args) != values.size()) return false;
std::size_t i=0;
return (std::holds_alternative<Args>(values[i++])&&...);
}
};
this can be stored in a std::function<bool(std::span<value const>)> or just called in your class impementation.
Similar code can store the callable.
template<class F, class R, class...Args>
struct execute {
F f;
template<std::size_t...Is>
R operator()( std::index_sequence<Is...>, std::span<value const> values ) const {
if (sizeof...(Args) != values.size()) return false;
return f( std::get<Args>(values[Is])... );
}
R operator()( std::span<value const> values ) const {
return (*this)( std::make_index_sequence<sizeof...(Args)>{}, values );
}
};
some work may have to be done for the fake void.
Your method is now a aggregate.
struct method {
std::function<bool(std::span<value const>)> can_call;
std::function<value(std::span<value const>)> execute;
};
if you want it to be. The two template objects above can be stored in these two std functions.
There are probably tpyos, I just wrote this on my phone and have not tested it.
Extending this to cover optional args is a little bit of work. But nothing hard.
In both cases, you'll write a helper function that says if the argument is compatible or generates the value based on if you are past the end of the incoming vector.
Ie, std::get<Args>(values[Is])... becomes getArgFrom<Is, Args>{}(values)..., and we specialize for std optional producing nullopt if Is>values.size().
Given two or more example functions, is it possible to write templated code which would be able to deduce the arguments of a function provided as a template parameter?
This is the motivating example:
void do_something(int value, double amount) {
std::cout << (value * amount) << std::endl;
}
void do_something_else(std::string const& first, double & second, int third) {
for(char c : first)
if(third / c == 0)
second += 13.7;
}
template<void(*Func)(/*???*/)>
struct wrapper {
using Args = /*???*/;
void operator()(Args&& ... args) const {
Func(std::forward<Args>(args)...);
}
};
int main() {
wrapper<do_something> obj; //Should be able to deduce Args to be [int, double]
obj(5, 17.4); //Would call do_something(5, 17.4);
wrapper<do_something_else> obj2; //Should be able to deduce Args to be [std::string const&, double&, int]
double value = 5;
obj2("Hello there!", value, 70); //Would call do_something_else("Hello there!", value, 70);
}
In both uses of /*???*/, I am trying to work out what I could put there that would enable this kind of code.
The following doesn't appear to work, due to Args not being defined before its first use (along with what I have to assume are numerous syntax errors besides), and even if it did, I'm still looking for a version that doesn't require explicit writing of the types themselves:
template<void(*Func)(Args ...), typename ... Args)
struct wrapper {
void operator()(Args ...args) const {
Func(std::forward<Args>(args)...);
}
};
wrapper<do_something, int, double> obj;
With C++17 we can have auto template non-type parameters which make possible the Wrapper<do_something> w{} syntax 1).
As for deducing Args... you can do that with a specialization.
template <auto* F>
struct Wrapper {};
template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<F>
{
auto operator()(Args... args) const
{
return F(args...);
}
};
Wrapper<do_something> w{};
w(10, 11.11);
1) Without C++17 it's impossible to have the Wrapper<do_something> w{} nice syntax.
The best you can do is:
template <class F, F* func>
struct Wrapper {};
template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<Ret (Args...), F>
{
auto operator()(Args... args) const
{
return F(args...);
}
};
Wrapper<declype(do_something), do_something> w{};
With C++17, you can do this:
template <auto FUNC, typename = decltype(FUNC)>
struct wrapper;
template <auto FUNC, typename RETURN, typename ...ARGS>
struct wrapper<FUNC, RETURN (*)(ARGS...)> {
RETURN operator()(ARGS ...args) {
return FUNC(args...);
}
};
I've learned this technique from W.F.'s answer
Further improvement of C++17 version: less template parameters and proper noexcept annotation:
template<auto VFnPtr> struct
wrapper;
template<typename TResult, typename... TArgs, TResult ( * VFnPtr)(TArgs...)> struct
wrapper<VFnPtr>
{
TResult
operator ()(TArgs... args) const noexcept(noexcept((*VFnPtr)(::std::forward<TArgs>(args)...)))
{
return (*VFnPtr)(::std::forward<TArgs>(args)...);
}
};
With C++11 you can consider a templated make_wrapper helper function. However, with this approach the function pointer is not a template parameter. Instead, the function pointer is "carried" by the non-static data member called f_ in the following example:
#include <iostream>
void do_something(int value, double amount) {
std::cout << (value * amount) << std::endl;
}
void do_something_else(std::string const& first, double & second, int third) {
for(char c : first)
if(third / c == 0)
second += 13.7;
}
template<class Ret, class... Args>
using function_pointer = Ret(*)(Args...);
template<class Ret, class... Args>
struct wrapper {
using F = function_pointer<Ret, Args...>;
F f_;
explicit constexpr wrapper(F f) noexcept : f_{f} {}
template<class... PreciseArgs>// not sure if this is required
Ret operator()(PreciseArgs&&... precise_args) const {
return f_(std::forward<PreciseArgs>(precise_args)...);
}
};
template<class Ret, class... Args>
constexpr auto make_wrapper(
function_pointer<Ret, Args...> f
) -> wrapper<Ret, Args...> {
return wrapper<Ret, Args...>(f);
}
int main() {
constexpr auto obj = make_wrapper(do_something);
obj(5, 17.4);
constexpr auto obj2 = make_wrapper(do_something_else);
double value = 5;
obj2("Hello there!", value, 70);
return 0;
}
I'm trying to implement bind function from boost library.
Below you can see the main struct bind_t with defined operator().
My question is as follows: Why should we specify in decltype in returning type of operator() returning type of call() explicitly as member function (if I remove this-> before call, the template argument deduction fails in g++.)
Also interesting, that using clang++ there's no such problem.
I have no idea, why this happens.
template <typename F, typename ... P>
struct bind_t {
private:
std::tuple<typename holder<P>::type...> p;
F func;
template <size_t ... N, typename ... Args>
auto call(index_list<N ...>, Args const& ... args) const -> decltype(func(std::get<N>(p)(args...)...)) {
return func(std::get<N>(p)(args...)...);
}
public:
bind_t(F f, P ... p):
p(std::move(p)...),
func(std::move(f))
{}
template <typename ... Args>
auto operator()(Args const& ... args) const -> decltype(this->call(typename indices_by_num<sizeof...(P)>::type(), args...)) {
typename indices_by_num<sizeof...(P)>::type indices;
return call(indices, args...);
}
};
full source of implementation
simple usecase
This is a gcc bug and is documented in the bug report decltype needs explicit 'this' pointer in member function declaration of template class with trailing return type which says:
When using trailing return-type for member functions of a template
class, the 'this' pointer must be explicitly mentioned. This should
not be necessary (The implicit 'this' works with a non-template
class).
Example:
template <typename T>
struct DecltypeConstThis {
T f() const { return T{}; }
auto g() -> decltype(this->f()) { return this->f(); }
auto h() const -> decltype(f()) { return f(); } // this should work the same as g() above (with implicit 'this')
};
struct Working {
int f() const { return 0; }
auto h() const -> decltype(f()) { return 0; }
};
int main() {
Working w;
w.h();
DecltypeConstThis<int> d;
d.g();
d.h();
return 0;
}
The report was marked as fixed and it looks like this works starts working in gcc 5.1 (see it live).
I have the following template specializations which wrap C++ functions to Lua:
template<class ...Args>
struct Wrapper<void (*)(Args...)> {
using F = void (*)(Args...);
static int f (lua_State *L)
{
Lua lua(L);
// Grab the function pointer.
F f = (F) lua_touserdata(L, lua_upvalueindex(1));
// Build a tuple of arguments.
auto args = lua.CheckArgs<1, Args...>();
// Apply the function to the tuple.
FunctionPointer<F> fp(f);
fp.Apply(args);
return 0;
}
};
template<class R, class ...Args>
struct Wrapper<R (*)(Args...)> {
using F = R (*)(Args...);
static int f (lua_State *L)
{
Lua lua(L);
// Grab the function pointer.
F f = (F) lua_touserdata(L, lua_upvalueindex(1));
// Build a tuple of arguments.
auto args = lua.CheckArgs<1, Args...>();
// Apply the function to the tuple.
FunctionPointer<F> fp(f);
lua.Push( fp.Apply(args) );
return 1;
}
};
Notice how they differ rather minimally. In the first specialization, FunctionPointer<F>::Apply returns void. In the second, it's result is pushed onto the Lua stack.
Can I combine these two specializations into one?
I realize this may seem pedantic, but I have had to write a lot of these wrappers elsewhere in my code, because of variations in the type of the function being wrapper (free function, or PMF, const or not). I have a total of 14 such specializations.
Here are two more which differ only by whether the PMF is const or not:
template <typename Self, typename ...Args>
struct MethodWrapper<void (Self::*)(Args...) >
{
using F = void (Self::*)(Args...);
static int f (lua_State *L)
{
Lua lua(L);
F f = *(F *)lua_touserdata(L, lua_upvalueindex(1));
Self* self = lua.CheckPtr<Self>(1);
auto args = lua.CheckArgs<2, Args...>();
FunctionPointer<F> fp(f);
try {
fp.Apply(self, args);
} catch(std::exception& e) {
luaL_error(L, e.what());
}
return 0;
}
};
template <typename R, typename Self, typename ...Args>
struct MethodWrapper<R (Self::*)(Args...) const >
{
// exactly the same as above
};
Can I avoid this cut and paste? (Without using macros though)
Related, but suffers from the same number of required specializations: How to use variadic templates to make a generic Lua function wrapper?
You should be able to make a generic functor which takes fp, args, and lua, and calls lua.Push(), with a partial specialization for when R is void which just invokes the function and ignores the (void) result. You would then invoke it like this:
ApplyAndPushIfNotVoid<R>()(lua, fp, args);
It's definitely possible to eliminate all that repetitive template specialization. In fact, for a one-off branching case, like in your free-function struct Wrapper, you don't even need to write a specialization to hide it -- just use std::is_void from type_traits:
template<typename R, typename ...Args>
struct Wrapper
{
using F = R (*)(Args...);
static int f (lua_State *L, F f)
{
// ...
FunctionPointer<F> fp {f};
if (std::is_void<R>::value)
{
fp.Apply(args);
return 0;
}
else
{
lua.Push( fp.Apply(args) );
return 1;
}
}
};
The compiler will optimize out one of the branches depending on how it gets instantiated.
There is a slight wrinkle though, when the return type is R = void the falsey branch still gets type-checked during instantiation which results in the body being ill-formed.
Using template specialization like in the other answer is one obvious solution. There is an alternative workaround: have FunctionPointer<F>::Apply return a dummy void_type when R = void. For example using std::conditional, FunctionPointer can be modified to work like:
template <typename F>
class FunctionPointer
{
template <typename R, typename ...Args>
static R func_return( R(*)(Args...) )
{ return {}; }
using R_ = decltype( func_return( (F)nullptr ) );
struct void_type {};
public:
F f;
using R = typename std::conditional<std::is_void<R_>::value,
void_type, R_>::type;
template <typename ...Args>
R Apply(std::tuple<Args...> &args)
{
// ...
return {};
}
};
IDEone Demo with external dependent types stubbed out.
For the MethodWrapper, I would identify the different 'traits' and aspects it needs from the member pointer and extract all those and hide it behind some trait class. Let's call it PMF_traits:
template <typename T, typename ...Args>
struct PMF_traits
{
private:
using T_traits = decltype( PMF_trait_helper( (T)nullptr ) );
public:
using class_type = typename T_traits::class_type;
using return_type = typename T_traits::return_type;
static const bool const_member = T_traits::const_member;
using type = T;
};
The PMF_trait_helper itself is just an empty function to help deduce and extract type information out of PMF. Here is where the const and non-const PMF is handled. That information is captured using PMF_trait_detail and passed back up to PMF_traits.
template <typename R, typename Class, bool Is_Const>
struct PMF_trait_detail
{
using class_type = Class;
using return_type = R;
static const bool const_member = Is_Const;
};
template <typename R, typename Class, typename ...Args>
PMF_trait_detail<R, Class, false> PMF_trait_helper( R (Class::*)(Args...) )
{ return PMF_trait_detail<R, Class, false> (); }
template <typename R, typename Class, typename ...Args>
PMF_trait_detail<R, Class, false> PMF_trait_helper( R (Class::*)(Args...) const)
{ return PMF_trait_detail<R, Class, true> (); }
With that setup MethodWrapper no longer needs to handle const non-const cases separately
template <typename PMF, typename ...Args>
struct MethodWrapper
{
typedef typename PMF_traits<PMF>::class_type Self;
int f (lua_State *L)
{
// ...
FunctionPointer<PMF> fp { (PMF) lua_touserdata(L, lua_upvalueindex(1)) };
Self *self = lua.CheckPtr<Self>(1);
// ...
try
{
// Almost like 'Wrapper' above
// handle void and non-void case etc.
if (std::is_void< typename PMF_traits<PMF>::return_type >::value)
{
fp.Apply(self, args);
return 0;
}
else { // ... }
}
catch(std::exception& e)
{
return luaL_error(L, e.what());
}
}
};
Note I didn't capture the variadic arguments in the PMF_traits just to keep the template complexity and syntactic verbiage down but it should be possible to encode and save this info too using std::tuple if you need that.
Using this technique you should be able to refactor and significantly reduce the number of specializations you need.
Consider this code:
#include <iostream>
using namespace std;
class hello{
public:
void f(){
cout<<"f"<<endl;
}
virtual void ff(){
cout<<"ff"<<endl;
}
};
#define call_mem_fn(object, ptr) ((object).*(ptr))
template<R (C::*ptr_to_mem)(Args...)> void proxycall(C& obj){
cout<<"hello"<<endl;
call_mem_fn(obj, ptr_to_mem)();
}
int main(){
hello obj;
proxycall<&hello::f>(obj);
}
Of course this won't compile at line 16, because the compiler doesn't know what R, C and Args, are. But there's another problem: if one tries to define those template parameters right before ptr_to_mem, he runs into this bad situation:
template<typename R, typename C, typename... Args, R (C::*ptr_to_mem)(Args...)>
// ^variadic template, but not as last parameter!
void proxycall(C& obj){
cout<<"hello"<<endl;
call_mem_fn(obj, ptr_to_mem)();
}
int main(){
hello obj;
proxycall<void, hello, &hello::f>(obj);
}
Surprisingly, g++ does not complain about Args not being the last parameter in the template list, but anyway it cannot bind proxycall to the right template function, and just notes that it's a possible candidate.
Any solution? My last resort is to pass the member function pointer as an argument, but if I could pass it as a template parameter it would fit better with the rest of my code.
EDIT:
as some have pointed out, the example seems pointless because proxycall isn't going to pass any argument. This is not true in the actual code I'm working on: the arguments are fetched with some template tricks from a Lua stack. But that part of the code is irrelevant to the question, and rather lengthy, so I won't paste it here.
You could try something like this:
template <typename T, typename R, typename ...Args>
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
Usage: proxycall(obj, &hello::f);
Alternatively, to make the PTMF into a template argument, try specialization:
template <typename T, T> struct proxy;
template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)>
struct proxy<R (T::*)(Args...), mf>
{
static R call(T & obj, Args &&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
};
Usage:
hello obj;
proxy<void(hello::*)(), &hello::f>::call(obj);
// or
typedef proxy<void(hello::*)(), &hello::f> hello_proxy;
hello_proxy::call(obj);
In modern C++ one can use template<auto> and generic lambda-wrapper:
#include <utility>
#include <functional>
template<auto mf, typename T>
auto make_proxy(T && obj)
{
return [&obj] (auto &&... args) { return (std::forward<T>(obj).*mf)(std::forward<decltype(args)>(args)...); };
}
struct R {};
struct A {};
struct B {};
struct Foo
{
R f(A &&, const B &) { return {}; }
//R f(A &&, const B &) const { return {}; }
};
int main()
{
Foo foo;
make_proxy<&Foo::f>(foo)(A{}, B{});
//make_proxy<static_cast<R (Foo::*)(A &&, const B &) const>(&Foo::f)>(std::as_const(foo))(A{}, B{});
//make_proxy<static_cast<R (Foo::*)(A &&, const B &)>(&Foo::f)>(foo)(A{}, B{});
}
If there are overloadings one should to specify member function type explicitly as in commented code.