Given typename T and int N, the templated value below generates a null function pointer of the type:
int (*) (T_0, ..., T_N)
While the code works, I don't like that it pollutes the namespace with the Temp bootstrap helper - Temp is required because types can't be enclosed in parentheses. For example, none of the following are valid.
(int (*)((int))
((int (*)(int)))
(int (*)( (I,T)... ))
The last entry shows how I would like to expand T into a list of N Ts - which of course isn't valid. It's a trick to make T depend on I but with a value only of T, thanks to the comma operator.
As a workaround, I'm forced to create the one-shot type Temp templated to make T depend on int, or in this case, I. Its usage as Temp<T,I> is valid because it doesn't enclose types in parenthesis.
However, like I said, I want to get rid of Temp because it pollutes the namespace. In the code below I restate the problem and demonstrate some attempted workarounds which sadly, all fail. For the record, I think an equivalence between template <typename T, int> using Temp = T; and template <... template<typename T1, int N1> typename Temp=T> should be allowed.
Followup: When I original published this question, I didn't know exactly why extra parentheses were disallowed, and I'm still not sure why some of my attempts failed. For example:
decltype(w<T,N>())...
result_of<w<T,N>()>::type...
I don't see any parentheses around types!
#include <iostream>
#include <typeinfo>
// Problem, #1
// This is a one-shot helper than pollutes the namespace
template <typename T, int>
using Temp = T;
// Idea #1
// Make the one-shot helper actuall useful, where ... represents either
// type or non-type parameters.
// Result
// Not possible.
//template <typename T, ...>
//using Dependent = T;
// Idea #2
// Make the types dependent within the template declaration
// Result
// Probably not possible
//template <typename T, int N, template<typename T1, int N1> typename F=T>
// Idea #6
// Replace the lambda with a struct (not shown)
// Result
// Crashes gcc
template <typename T, int N>
auto a =
[]<size_t... I>
(std::index_sequence<I...>) {
// Problem #2
// Function type declaration won't parse with extra parentheses
//return (int (*)( (I,T)... ))nullptr;
// Idea #3
// Move the templated helper into the function
// Result
// Not possible
//template <typename T, int>
//using Temp = T;
// Idea #4
// Replace the templated helper with a templated lambda which *is*
// allowed inside functions.
// Result
// Still requires parentheses, still breaks function type declaration
//auto w = []<typename T1, int N1>() -> T1 {};
//return (int (*)( decltype(w<T,N>())... ));
// Idea #5
// result_of (which is a template) instead of decltype
// Result
// Doesn't work even without parentheses, not sure why
//return (int (*)( result_of<w<T,N>>... ));
//return (int (*)( result_of<w<T,N>()>::type... ));
// Idea #7
// Use std::function
// Result
// Can't get function pointer from std::function
// Idea #2 implementation
//using F<T,I> = T;
//return (int (*)( F<T,I>... ))nullptr;
// So far, only this works:
return (int (*)( Temp<T,I>... ))nullptr;
}
(std::make_index_sequence<N>{});
int main () {
auto b = a<int, 4>;
std::cout << typeid(b).name() << std::endl;
}
You can replace Temp<T,I> with std::enable_if_t<(void(I), true), T>.
Function type declarations won't parse extra parentheses
that actually works! Why?
Types can't be enclosed in parentheses. But the first argument of enable_if_t is an expression rather than a type, so ( ) is allowed there.
This is pretty easy with Boost.Mp11:
template <typename... Args>
using into_func = int(Args...);
template <typename T, size_t N>
using result = mp_apply<func_type, mp_repeat_c<mp_list<T>, N>>;
mp_repeat_c<mp_list<T>, N> gives us mp_list<T, T, T, ..., T> with N parameters, and then we mp_apply into_func on that, which turns it into the function type we want.
As to why a few of your other versions didn't work:
This one:
decltype(w<T,N>())...
where:
auto w = []<typename T1, int N1>() -> T1 {};
That's not how you call that lambda. The call operator is a template, but you can't provide the template parameters like that. You have to write w.operator()<T, N>(). If you did that, that probably works.
Or you could do:
auto w = [](size_t) -> T { };
And then use decltype(w(N))....
Although it would be better to write something closer to a valid lambda, like:
auto w = [](size_t) { return std::type_identity<T>{}; };
And then use typename decltype(w(N))::type...
This approach:
result_of<w<T,N>()>::type...
result_of is used like result_of<F(Args...)> where F is a function or function object type. For instance:
struct F { double operator()(int); };
result_of_t<F(int)>; // this is double
This just isn't anything like that here at all - it's just a misuse of result_of. result_of is also deprecated in favor of invoke_result, which wouldn't work with this lambda as specified anyway since again it takes template parameters rather than function parameters.
With my last rewrite tho:
typename std::invoke_result_t<decltype(w), decltype(N)>::type...
Note both the decltype(w) (since you need a type, not an object) and the decltype(N) (since again, type not value).
Related
Consider the following example:
template<auto const fnc>
struct dummy_s {
typedef std::invoke_result<decltype(fnc), std::uint8_t >::type return_t;
};
int main() {
dummy_s<[](std::uint8_t const& n) -> bool { return true ^ n; }>::return_t s = true;
}
Is there anyway to get the return type without specifying std::uint8_t or whatever the number of arguments is, as template parameter as example.
You could write a metafunction that gives you the type of the first argument
template<typename Ret, typename Arg>
auto arg(Ret(*)(Arg)) -> Arg;
and then decay the lambda fnc to a function pointer (using + say), that you pass to arg, and then use that in the typedef.
typedef std::invoke_result<decltype(fnc),
decltype(arg(+fnc))>::type return_t;
This will only work for lambdas that don't capture anything, and that take a single argument.
You can also considerably simplify the typedef inside the struct by simply using arg directly like this
using return_t = decltype(arg(+fnc)); // using is cleaner than a typedef as well
This avoids using invoke_result entirely, and lets you define arg in a way that allows lambdas with multiple arguments to be passed to it
template<typename Ret, typename Arg, typename ...R>
auto arg(Ret(*)(Arg, R...)) -> Arg;
Here's a demo
As I said in my comment, each time I though I needed this feature I realized later I was going the wrong path. The reason is that as soon as the lambda is a template (auto) there is no hope to make it work.
More generally, if you don't have clue of the "universe" inputs of a function you don't really have a function, conceptually speaking.
If you think you still need it, you can use Boost.TypeTraits, lambda decay and function pointers.
#include<cstdint>
#include<boost/type_traits.hpp>
int main(){
auto f = [](std::uint8_t const& n) -> bool {return true ^ n;};
using f_return = boost::function_traits<decltype(*+f)>::result_type;
static_assert( std::is_same<f_return, bool>{} , "!");
}
With any generalization of f, overload or templates, will not work.
You are really lucky that this sort of works because of a series of quirks in the language starting from the existence of monomorphic functions (inherited from C, pointer decay, etc). Conceptually, it is horrible.
Having said that, there is also a potential problem that is very sensitive to the version of C++ you are using, that is the use of lambdas in template (non-evaluated) contexts.
This is working solution based on your code.
It really works as is with certain combinations of compilers and flags, for example https://godbolt.org/z/5x684nfWc :
#include<cstdint>
#include<boost/type_traits.hpp>
template<auto fnc>
struct dummy_s {
using return_t = typename boost::function_traits<decltype(*+fnc)>::result_type;
};
int main() {
typename dummy_s<[](std::uint8_t const& n) -> bool { return true ^ n; }>::return_t s = true;
}
While answering a question, I proposed utilizing template aliases for typedefing the signature of a member function; that is, not just typedefing a member function but being able to factor out the target class that contains the method:
template<typename T>
using memberf_pointer = int (T::*)(int, int);
Though this seems to cover what the question asked, I tried to generalize it for arbitrary function arguments:
template<typename T, typename... Args>
using memberf_pointer = int (T::*)(Args&&...);
It fails with argument deduction issues (basically it assumes an empty arument list). Here's a demo:
#include <iostream>
class foo
{
public:
int g (int x, int y) { return x + y ; }
};
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args&&...);
int main()
{
foo f ;
memberf_pointer<foo> mp = &foo::g ;
std::cout << (f.*mp) (5, 8) << std::endl ;
}
Why is this? Is there a way to get it to work?
The wording in both the title and body of the question is very misleading. There is zero template deduction going on in your example, anywhere. When you write:
memberf_pointer<foo> mp = &foo::g;
memberf_pointer<foo> is an alias template, yes, but it's a specific instantiation of one. There is no deduction going on because you are providing the exact type of mp. That line is exactly equivalent to:
int (foo:*mp)() = &foo::g;
which doesn't compile for the obvious reason that g takes arguments. The way to get template deduction in an assignment statement is to use auto:
auto mp = &foo::g;
The type of mp will be the same type as U had you called:
template <typename U> void meow(U );
meow(&foo::g);
which is to say, int (foo::*)(int, int).
Similarly, you could do:
decltype(&foo::g) mp = &foo::g;
Which would give you the same type as before.
Of course, even if you provided the correct argument list:
memberf_pointer<foo, int, int> mp = &foo::g;
That still wouldn't compile since your alias adds rvalue references to both arguments. The type of mp there is int (foo::*)(int&&, int&&), which would not match &foo::g. Perhaps you'd intended this to be deduction as if by forwarding-reference, but that is not the case here. In order to use the alias correctly you'd have to rewrite it:
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...);
memberf_pointer<foo, int, int> mp = &foo::g;
Had we had a member function that took an rvalue reference, we could then explicitly provide it:
class bar
{
public:
int h(int&& x, int&& y) { return x + y ; }
};
memberf_pointer<bar, int&&, int&&> mb = &bar::h;
Why not simply use an auto in this case? The only advantage of having a template in this case is being able to provide your types explicitly.
On the other hand, if you want your template to automatically deduce a function type, it needs to be directly parametrized on that type. If you also want it to provide all of the building blocks for you, the simplest way is to specialize it for functions or member functions. Example:
template<typename T> struct memberf_pointer_descriptor;
template<typename TOwner, typename TRet, typename... Args>
struct memberf_pointer_descriptor<TRet(TOwner::*)(Args...)>
{
// Your stuff goes here.
using type = TRet(TOwner::*)(Args...);
};
memberf_pointer_descriptor<decltype(&foo::g)>;
Or a function template that directly takes foo::g as an argument, to mitigate the need of using an explicit decltype. Depends on your needs.
A way to make your example work is the following:
#include <iostream>
class foo
{
public:
int g(int x, int y) { return x + y; }
};
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...);
int main()
{
foo f;
memberf_pointer<foo, int, int> mp = &foo::g;
std::cout << (f.*mp) (5, 8) << std::endl;
}
It removes the reference on the variadic template parameter and when instantiating the memberf_pointer it supplies also the member function parameters. But auto is probably the way to go...
C++ doesn't feature something like a Hindley-Milner type deduction system and it won't work for your specific rvalue assignment
memberf_pointer<foo> mp = &foo::g ;
As for some quick workarounds you could
Just drop the whole struct and use auto
auto mp = &foo::g;
Explicitly provide the types or the pointer type
template<typename T>
using memberf_pointer = T;
memberf_pointer<decltype(&foo::g)> mp = &foo::g;
Cfr. Template argument deduction
I am writing a wrapper class for callable types (pointer to function, functors, etc). I want to implement something like std::function.
I define constructor from pointer to function:
template <typename Ret, typename... Args>
class function<Ret(Args...)>
{
public:
function(Ret (func)(Args...))
{
m_fn_ptr = func;
}
}
Now, let's assume that i want to use my class like this:
int int_function(int n)
{
return n;
}
function<int(short)> wrapper(&int_function); // compile error
Despite that short are implicit convertable to int compiler cannot deduce template parameters and call appropriate constructor.
Then i tried this:
template <typename FRet, typename... FArgs>
function(FRet (func)(FArgs...))
{
m_fn_ptr = static_cast<Ret (*f)(Args...)>(func);
}
But I got invalid static cast.
How can I fix that ?
The super_func is a function object with no state that can convert to any compatible call signature.
template<class T>using type=T;
template<class Sig, Sig* func>
struct super_func;
template<class R, class...Args, R(*func)(Args...)>
struct super_func<R(Args...), func> {
using Sig = R(Args...);
using pSig = Sig*;
template<class R2, class...Args2, std::enable_if_t<
std::is_convertible<
std::result_of_t<pSig(Args2...)>,
R2
>{}
&& !std::is_same<R2, void>{},
bool
> =true>
constexpr operator type<R2(Args2...)>*() const {
return [](Args2...args)->R2{
return func(std::forward<Args2>(args)...);
};
}
template<class...Args2, std::enable_if_t<
std::is_same<
std::result_of_t<pSig(Args2...)>,
R
>{},
bool
> =true>
constexpr operator type<void(Args2...)>*() const {
return [](Args2...args)->void{
func(std::forward<Args2>(args)...);
};
}
constexpr operator pSig() const {
return func;
}
constexpr R operator()(Args...args)const{
return func(std::forward<Args>(args)...);
}
};
live example. A super_func is stateless. To use it on a function foo, do:
super_func< decltype(foo), &foo > super_foo;
and you get a callable stateless empty object which behaves a lot like foo does, except you can assign it to a pointer to any compatible function pointer and it generates it "on the fly" at compile time.
A super_foo can be fed to your function object.
Doing this on the fly doesn't work without the exterior help, as we need the foo to be a truly static bit of information. By the time it becomes a variable, it is too late to do this statelessly, so we cannot use the lambda trick (without an extra pvoid) to generate a function pointer for the exact signature we want.
You could do a macro:
#define SUPER(X) super_func< decltype(X), &X >{}
and then create your function object with function<double()> f(SUPER(foo));
Another approach is to store an extra pointer's worth of state, and create "the fastest possible delegate" style type erasure. (that term can be googled for one of many implementations, each faster than the last).
How can I fix that ?
Use the correct types when creating wrapper.
Instead of using
function<int(short)> wrapper(&int_function);
use
function<int(int)> wrapper(&int_function);
Remember that class templates instantiated with int and short are very different types and are not convertible to each other.
template <typename T> struct Foo {};
Foo<int> a;
Foo<short> b = a; // Not OK.
Foo<short> c;
Foo<int> d = c; // Not OK.
Your function constructor expects a pointer to a function that takes a short, not an int. The fix is to provide it such a function. The easiest way to do that is to use a lambda with an empty capture-list, that is implicitly convertible to a function pointer:
function<int(short)> wrapper( [](short s) { return int_function(s); } );
I was thinking about the implicit templates of C++14, and I'm trying to declare a function to match an specific argument type (SFINAE and traits still give me headaches). I'm not sure how to explain what I want, but I'm trying to make a Y combinator (just to see if it's possible, not intended for production).
I'm trying to declare a function:
template<typename T>
my_traits<T>::return_type Y(T t) {
// ...
};
Such that T is a function (or a functor) that matches
std::function<R(F, Args...)>
// where F (and above return_type) will be
std::function<R(Args...)>
Which would take any number of arguments, but the first should be a function with the same return type and the same arguments (except this function itself). The first parameter to the operator () of the functor is a template.
The usage I want to achieve:
auto fib = [](auto myself, int x) {
if(x < 2)
return 1;
return myself(x - 1) + myself(x - 2);
};
// The returned type of fib should be assignable to std::function<int(int)>
I wasn't able to take the return type of the T type (because of the overloaded operator ()). What I'm trying to make is possible? How could I make it?
Edit:
Seeing it from a different angle, I'm trying to make this work:
struct my_functor {
template<typename T>
char operator () (T t, int x, float y) { /* ... */ };
};
template<typename T>
struct my_traits {
typedef /* ... */ result_type;
/* ... */
};
// I want this to be std::function<char(int, float)>, based on my_functor
using my_result =
my_traits<my_functor>::result_type;
It is not possible in C++14 return type deduction to deduce int(int) out of int(T, int) as OP desires.
However, we can mask the first parameter of the result using the following approach. The struct YCombinator is instantiated with a non-recursive function object member, whose first argument is a version of itself without the first argument. YCombinator provides a call operator that receives the arguments of the non-recursive function and then returns its function object member after substituting itself for the first argument. This technique allows the programmer to avoid the messiness of myself(myself, ...) calls within the definition of the recursive function.
template<typename Functor>
struct YCombinator
{
Functor functor;
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return functor(*this, std::forward<Args>(args)...);
}
};
A make_YCombinator utility template allows for a streamlined usage pattern. This compiles run runs in GCC 4.9.0.
template<typename Functor>
decltype(auto) make_YCombinator(Functor f) { return YCombinator<Functor> { f }; }
int main()
{
auto fib = make_YCombinator([](auto self, int n) -> int { return n < 2 ? 1 : self(n - 1) + self(n - 2); });
for (int i = 0; i < 10 ; ++i)
cout << "fib(" << i << ") = " << fib(i) << endl;
return 0;
}
Since the non-recursive function is not defined at time that the recursive function is defined, in general the recursive function must have an explicit return type.
Edit:
However, it may be possible for the compiler to deduce the return type in certain cases if the programmer takes care to indicate the return type of the recursive function before use of the non-recursive function. While the above construction requires an explicit return type, in the following GCC 4.9.0 has no problem deducing the return type:
auto fib = make_YCombinator([](auto self, int n) { if (n < 2) return 1; return self(n - 1) + self(n - 2); });
To pin this down just a bit further, here is a quote from the draft C++14 standard on return type deduction [7.1.6.4.11]:
If the type of an entity with an undeduced placeholder type is needed
to determine the type of an expression, the program is ill-formed.
Once a return statement has been seen in a function, however, the
return type deduced from that statement can be used in the rest of the
function, including in other return statements. [ Example:
auto n = n; // error, n’s type is unknown
auto f();
void g() { &f; } // error, f’s return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum’s return type is int
else
return sum(i-1)+i; // OK, sum’s return type has been deduced
}
—end example ]
It's a really hacky approach, and has severe limitations, but here it goes:
First, we need a class that pretends to support every possible operation (as far as possible), such as the fake_anything class. Note that this isn't perfect since at a minimum . and :: won't work. To fake a functor, we give it a function call operator:
template<class... Ts> fake_anything operator()(Ts&&...) const;
Knowing that the lambda has only one operator(), and that operator() has only one template parameter allows us to extract its signature with decltype(&T::operator()<fake_anything>).
For this to work, the lambda's return type must be explicitly specified; it can't use deduction, since otherwise the deduced return types will probably conflict.
Finally we can obtain the other arguments to the lambda and the return type using the standard partial specialization approach:
template<class T>
struct extract_signature;
template<class T, class R, class FA, class...Args>
struct extract_signature<R (T::*)(FA, Args...)> {
static_assert(std::is_same<fake_anything, std::decay_t<FA>>::value, "Unexpected signature");
using type = std::function<R(Args...)>;
};
template<class T, class R, class FA, class...Args>
struct extract_signature<R (T::*)(FA, Args...) const> {
static_assert(std::is_same<fake_anything, std::decay_t<FA>>::value, "Unexpected signature");
using type = std::function<R(Args...)>;
};
// other cv- and ref-qualifier versions omitted - not relevant to lambdas
// we can also static_assert that none of Args is fake_anything, or reference to it, etc.
And add an alias template to hide all the ugliness of the hack:
template<class T>
using signature_t = typename extract_signature<decltype(&T::template operator()<fake_anything>)>::type;
And finally we can check that
static_assert(std::is_same<signature_t<decltype(fib)>,
std::function<int(int)>>::value, "Oops");
Demo.
The limitations:
The return type of operator() must be explicitly specified. You cannot use automatic return type deduction, unless all of the return statements return the same type regardless of the return type of the functor.
The faking is very imperfect.
This works for operator() of a particular form only: template<class T> R operator()(T, argument-types...) with or without const, where the first parameter is T or a reference to possibly cv-qualified T.
I wrote a program in C++ & boost. Is it possible to write a template class producing functors from functions with an unknown number of arguments, e.g. my_call<func>(vector<variant>), where fun can be bool fun(string) or bool fun(int, int, string), etc.?
First, it is important to recognize that boost::variant<> is a class template that requires the list of all the possible types it can hold. So, you won't have just a vector<variant>, but rather a vector<variant<string, double>>, or vector<variant<int, double, string, my_class>>, and you won't be able to mix them.
This made me think you might want to use boost::any rather than boost::variant<>. Thus, I present here a solution that works with boost::variant and can be slightly modified to use boost::any, so you can pick the version you prefer.
To begin with, I must admit that the solution is simple to use but not so simple to understand, so I will have to introduce some machinery first. This machinery is common to both the variant-based and the any-based solution.
//=============================================================================
// META-FUNCTIONS FOR CREATING INDEX LISTS
// The structure that encapsulates index lists
template <size_t... Is>
struct index_list
{
};
// Collects internal details for generating index ranges [MIN, MAX)
namespace detail
{
// Declare primary template for index range builder
template <size_t MIN, size_t N, size_t... Is>
struct range_builder;
// Base step
template <size_t MIN, size_t... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_list<Is...> type;
};
// Induction step
template <size_t MIN, size_t N, size_t... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{
};
}
// Meta-function that returns a [MIN, MAX) index range
template<size_t MIN, size_t MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;
The meta-class index_range allows defining compile-time sequences of integers. An interesting proposal have been made by Jonathan Wakely to standardize this kind of construct, so that this whole machinery would not be needed. For the moment, however, we have to hand code this as done above.
Now that we can build compile-time integer sequences, we can exploit variadic templates and argument unpacking to create a dispatching mechanism that translates a vector of variant arguments into a regular argument list. Notice how the concrete variant<> type must be provided as a template argument. This will not be needed for the solution based on any.
// Headers needed for the implementation of the dispatcher
#include <vector>
#include <functional>
#include <boost/variant.hpp>
// Just for convenience
using namespace std;
using boost::variant;
//============================================================================
// DISPATCHER IMPLEMENTATION
// Call dispatching mechanism: notice how the underlying variant type
// must be provided as a template argument (the first one)
template<typename VT, typename R, typename... Args>
struct dispatcher
{
template<typename F>
dispatcher(F f) : _f(f) { }
// The call operator which performs the variant dispatch
R operator () (vector<VT> const& v)
{
if (v.size() != sizeof...(Args))
{
// Wrong number of arguments provided!
return false;
}
// Delegates to internal function call: needed for deducing
// a sequence of integers to be used for unpacking.
index_range<0, sizeof...(Args)> indexes;
return do_call(v, indexes);
}
private:
// The heart of the dispatching mechanism
template<size_t... Is>
R do_call(vector<VT> const& v, index_list<Is...> indexes)
{
return _f((get_ith<Args>(v, Is))...);
}
// Helper function that extracts a typed value from the variant.
template<typename T>
T get_ith(vector<VT> const& v, size_t i)
{
return boost::get<T>(v[i]);
}
// Wrapper that holds the function to be invoked.
function<R(Args...)> _f;
};
// Helper function that allows deducing the input function signature
template<typename VT, typename R, typename... Args>
function<R (vector<VT> const&)> get_dispatcher(R (*f)(Args...))
{
dispatcher<VT, R, Args...> d(f);
return d;
}
Finally, a short demonstration of how you could use this. Suppose we have two test functions such as the ones below:
#include <iostream>
bool test1(string s, double d)
{
cout << s << " " << d << endl;
return true;
}
bool test2(int i1, int i2, string s1, string s2)
{
cout << i1 << " " << i2 << " " << s1 << " " << s2 << endl;
return true;
}
What we want is to invoke them by building a vector of variants and have it dispatched to the desired function. Once again, I must stress the fact that we need to specify the list of all the types our variant can hold. Here, I will assume these types are string, double, and int, but your program might work with different ones.
Also, the solution is based on std::function<> for realizing the type erasure that allows you creating functors of different types and yet invoke them uniformly. Thus, a convenience type definition for this std::function<> (which in turn depends on the variant<> type we use) is provided as well:
int main()
{
// A helper type definition for the variant
typedef variant<int, double, string> vt;
// A helper type definition for the function wrapper
typedef function<bool (vector<vt>)> dispatcher_type;
// Get a caller for the first function
dispatcher_type f1 = get_dispatcher<vt>(test1);
// Prepare arguments for the first function
vector<vt> v = {"hello", 3.14};
// Invoke the first function
f1(v);
// Get a caller for the second function
dispatcher_type f2 = get_dispatcher<vt>(test2);
// Prepare arguments for the second function
v.assign({1, 42, "hello", "world"});
// Invoke the second function
f2(v);
}
Since all dispatchers have type dispatcher_type, you can easily put them into a container. However, you must be aware of the fact that attempts to invoke a function with the wrong number of arguments will be detected only at run-time (it is impossible to know at compile-time how many elements an std::vector<> contains). Thus, proper care must be taken.
As promised, I will now slightly modify this solution to use boost::any rather than boost::variant. The advantage is that since boost::any can hold any value, it is not necessary to specify the list of the possible types which can be used as function arguments.
While the helper machinery is unchanged, the core dispatcher class template must be modified as follows:
#include <vector>
#include <functional>
#include <boost/any.hpp>
using namespace std;
using boost::any;
//=============================================================================
// DISPATCHER IMPLEMENTATION
template<typename R, typename... Args>
struct dispatcher
{
template<typename F>
dispatcher(F f) : _f(f) { }
// The call operator which performs the dispatch
R operator () (vector<any> const& v)
{
if (v.size() != sizeof...(Args))
{
// Wrong number of arguments provided!
return false;
}
// Delegates to internal function call: needed for deducing
// a sequence of integers to be used for unpacking.
index_range<0, sizeof...(Args)> indexes;
return do_call(v, indexes);
}
private:
// The heart of the dispatching mechanism
template<size_t... Is>
R do_call(vector<any> const& v, index_list<Is...> indexes)
{
return _f((get_ith<Args>(v, Is))...);
}
// Helper function that extracts a typed value from the variant.
template<typename T>
T get_ith(vector<any> const& v, size_t i)
{
return boost::any_cast<T>(v[i]);
}
// Wrapper that holds the function to be invoked.
function<R(Args...)> _f;
};
// Helper function
template<typename R, typename... Args>
function<R (vector<any> const&)> get_dispatcher(R (*f)(Args...))
{
dispatcher<R, Args...> d(f);
return d;
}
As you see, the VT template argument has vanished. In particular, it is possible to call get_dispatcher without explicitly specifying any template argument. Using the same test functions we have defined for the variant-based solution, here is how you would adapt the main() routine:
int main()
{
// Helper type definition
typedef function<bool (vector<any>)> dispatcher_type;
// Get a caller for the first function
dispatcher_type f1 = get_dispatcher(test1);
// Get a caller for the second function
dispatcher_type f2 = get_dispatcher(test2);
// Prepare arguments for the first function
vector<any> v = {string("hello"), 3.14};
// Invoke the first function
f1(v);
// Prepare arguments for the second function
v.assign({1, 42, string("hello"), string("world")});
// Invoke the second function
f2(v);
}
The only disadvantage is that with boost::any you cannot assign string literals explicitly, because string literals are of type char [], and arrays cannot be used to initialize objects of type any:
any a = "hello"; // ERROR!
Thus, you have to either wrap them into string objects, or explicitly convert them to a pointer to char const*:
any a = string("hello"); // OK
any b = (char const*)"hello"; // OK
If this is not a huge problem for you, it's probably better to go for this second solution.