std::bind is sometimes described as "partial application". Any reasons why when all parameters of a function are bound, the function itself isn't applied?
For example, the following code prints nothing.
#include <functional>
#include <iostream>
using namespace std;
using namespace std::placeholders;
void f(int a,string b) {cout << a << b << endl;};
int main() {
bind(bind(f,1,_1),"Hi!");
return 0;
}
Is there a way to write a bind variant that can apply the function when all parameters are fixed?
--Update--
I understand from the responses now that std::bind is not exactly partial application. So, on the second part of the question, how can I write something like std::bind but does partial application.
I know bind(bind(f,1,_1),"Hi!")() will call the final 0-ary function and return the result value (printing 1Hi in the example). Is it possible to do template programming to call the function call operator () in the terminal case of bind?
In other words, is it possible to write a function bind1:
template< class R, class F, class... Args >
bind1( F f, Args... args )
, such that when std::is_placeholder<T>::value == 0 for each member of args,
bind1() can, in addition to what std::bind() does, call the operator()?
A function with no arguments is just a value in Haskell. You don't call it, you just use it. Since there are no side effects, there is no observable difference.
In OCaml there are simply no parameter-less functions, to get something like that you need to add a dummy unit argument.
Not so in C++. C++, unlike Haskell and OCaml, maintains clear difference between f and f(). bind gives you the former because you can always turn it into the latter by adding (). You can write your own wrapper for bind that does just that quite easily. Going the other way around would be a tad more difficult.
Here's a possible implementation of such wrapper:
#include <functional>
#include <utility>
#include <iostream>
template <typename T>
struct is_noargs_callable {
private:
typedef char(&yes)[1];
typedef char(&no)[2];
template<typename U>
static yes test(decltype((std::declval<U>())())*);
template<typename>
static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
template <typename T>
struct is_noargs_callable<T()> {
static const bool value = true;
};
template <typename T>
struct is_noargs_callable<T(...)> {
static const bool value = true;
};
template <typename T>
auto call_me_if_you_can(T t) -> typename std::enable_if<is_noargs_callable<T>::value, decltype(t())>::type
{
return t();
}
template <typename T>
auto call_me_if_you_can(T t) -> typename std::enable_if<!is_noargs_callable<T>::value, T>::type
{
return t;
}
template <typename... Args>
auto apply(Args&&... args) -> decltype(call_me_if_you_can(std::bind(args...))) {
return call_me_if_you_can(std::bind(args...));
}
// testing
void foo(int a, int b, int c) { std::cout << "foo(" << a << "," << b << "," << c << ")"; }
int main ()
{
using namespace std::placeholders;
std::cout << "zero : " ; apply(foo, _1, _2, _3); std::cout << " : " ; apply(foo, _1, _2, _3)(1,2,3); std::cout << std::endl;
std::cout << "one : " ; apply(foo, 1, _1, _2); std::cout << " : " ; apply(foo, 1, _1, _2)(2,3); std::cout << std::endl;
std::cout << "two : " ; apply(foo, 1, 2, _1); std::cout << " : " ; apply(foo, 1, 2, _1)(3); std::cout << std::endl;
std::cout << "three: " ; apply(foo, 1, 2, 3); std::cout << " : "; /* nothing to test here */ std::cout << std::endl;
}
However, killing the difference between f and f() just in this one place does not IMHO contribute to the overall consistency of C++ programming. If you don't like the distinction, kill it everywhere (or just use you a Haskell for great good).
No sources for this, just my opinion.
The reason that wasn't done is because there is no reason to do it. If you know all of the input to the function, just call it.
And if you were doing something with templates that resulted in this, you would need to write all of the code consistently anyway. A special case here would only require a special case somewhere else.
Related
I need some macro or other way to print current function and its arguments. For the function name it is easy, as we can use e.g. __PRETTY_FUNCTION__. Is there some macro that holds function arguments? Or can you use va_list and va_start for non-variadic functions?
Something similar to __VA_ARGS__ but for a regular function, not macros. I don't mind taking this out of some symbol table etc. as long as it works.
To do something like this:
#define FUNC_ENTER printName(__PRETTY_FUNCTION__); printArgs(__WHAT_IM_MISSING__);
void someFunc(int a, int b)
{
FUNC_ENTER;
// ... rest of the function ...
}
void main()
{
someFunc(5, 10);
// Should print "Entering 'someFunc' with '5 10'" or something similar.
}
P.S.1. I know I could make it work with writing FUNC_ENTER(a, b) - but I'd like to save myself typing the arguments
P.S.2. Don't suggest GCC's __cyg_profile_func_enter - as I don't want this macro for every function.
Note: the following is all specific to C++.
I don't know of a clean way of doing this inside the function in question. But you can pretty easily do it as a wrapper for the function, on this general order:
#include <iostream>
// just a couple functions with different number/types of arguments
// to demonstrate invoking
int someFunc(int a, int b) {
return a + b;
}
void otherFunc(std::string const &a, int b, long long c) {
std::cout << "a: " << a << ", b: " << b << ", c: " << c;
}
// Print out arguments, then invoke function:
template <class F, typename ...Args>
auto invoke(F f, Args && ...args) {
(std::cout << "Args: " << ... << args) << "\n";
return f(std::forward<Args>(args)...);
}
int main() {
std::cout << invoke(someFunc, 1, 2) << "\n";
invoke(otherFunc, "first", 2, 3);
std::cout << "\n";
}
If you want to "hide" the implementation from the user so they only have to call an ordinary looking function, you can pretty easily add a little wrapper for each individual function, something along this line:
namespace {
// this is the actual implementation, hidden from the user
void function_implementation(/* whatever args*/) {
// ...
}
}
// this is all the user sees--print args, then invoke implementation.
// For each wrapper, you edit the name you want the user to see, and the
// implementation it invokes.
template <typename ...Args>
void function(Args && ...args) {
std::cout << __PRETTY_FUNCTION__;
invoke(function_implementation, std::forward<Args>(args)...);
}
As this code stands, it has one minor difficulty: it doesn't insert a delimiter between each argument as it's printing them out. If you need that, you can (for one possibility) expand the parameter pack recursively instead of using a fold expression:
template <class T>
void print(T const &t) {
std::cout << t << "\t";
}
template<typename T, typename... Args>
void print(T n, Args ... args) {
print(n);
print(args...);
std::cout << "\n";
}
template <class F, typename ...Args>
auto invoke(F f, Args && ...args) {
print(std::forward<Args>(args)...);
return f(std::forward<Args>(args)...);
}
Hi is there a container where a key is a typename and value is an object/instance in boost or std???
What I want to achieve is I have a object pool for each data type and when I want to construct that object I want to just fetch by Key. I already have working code but I would be happier if i used something more standard.
Currently I have a code like this:
template<size_t index, size_t counter, typename T0, typename ...T>
struct PoolBuilderInternal
{
typedef typename boost::mpl::insert<
typename PoolBuilderInternal<index - 1, counter + 1, T...>::m
, boost::mpl::pair<T0, std::integral_constant<size_t, counter> > >::type m;
};
template<size_t counter, typename T0, typename ...T>
struct PoolBuilderInternal<1, counter, T0, T...>
{
typedef typename boost::mpl::map<boost::mpl::pair<T0, std::integral_constant<size_t, counter> > > m;
};
template<typename ...T>
struct Pools
{
std::tuple<
boost::object_pool<T>...> m_pools;
typedef typename PoolBuilderInternal<sizeof...(T), 0, T...>::m poolType;
template<typename T>
boost::object_pool<T>& GetPool()
{
return std::get< boost::mpl::at<poolType, T>::type::value>(m_pools);
}
};
Pools<int, std::string, int64_t> m_pools;
m_pools.Get<int>();
EDIT: What I want is a COMPILE-TIME map. You can imagine a std::tuple<> but some that wouldnt not be accessed by index std::get<0,1,2>... but rather by a key (?std::tuple_map)
If types in the pool are unique use c++ 14 std::get< T >(std::tuple(s))
#include <iostream>
#include <string>
#include <tuple>
struct A
{
int value = 17;
};
int main()
{
auto t = std::make_tuple(1, std::string{"Foo"}, 3.14, A{});
std::cout << "(" << std::get<int>(t) << ", "
<< std::get<std::string>(t)
<< ", " << std::get<double>(t)
<< ", " << std::get<A>(t).value << ")\n";
}
If I understand this question right (and I'm not sure I do), what you really want is some kind of class factory, and that, in various forms, is a well-known design pattern because it allows users of the factory to construct objects whose constructors (and indeed types, quite often, when a class hierarchy is involved) are unknown or off-limits to them.
On that basis, I humbly offer you the following proof-of-concept code. Please note that I threw this together in rather a hurry and it's probably not optimal. I'm sure more is possible, including passing parameters to the relevant constructors to make_string() and make_foo() from the call site (e.g. factory [t_string] ("My string"). I'll look into that when I have time, if you show any interest in this post.
OK, so here's your class factory. You should be able to apply it to any types, including boost types. As coded, any parameters that need to be passed to the object in question are currently defined when the factory function (e.g. make_string) is added to the map (which is probably not ideal). These factory functions also could / should be lambdas. Again, I'll add that in later.
#include <functional>
#include <unordered_map>
#include <variant>
#include <iostream>
struct Foo
{
Foo (int x) : x (x) { }
int x;
};
enum ObjectType { t_string, t_foo }; // ...
using all_possible_types = std::variant <std::string, Foo>; // ...
static all_possible_types make_string (const std::string& s)
{
std::cout << "make_string " << s << "\n";
return all_possible_types (std::string (s));
}
static all_possible_types make_foo (int x)
{
std::cout << "make_foo " << x << "\n";
return all_possible_types (Foo (x));
}
// ...
int main()
{
std::unordered_map <ObjectType, std::function <all_possible_types ()>> factory;
factory.insert ({t_string, std::bind (make_string, "Initial string value")});
factory.insert ({t_foo, std::bind (make_foo, 42)});
// ...
all_possible_types variant_string = factory [t_string] ();
std::cout << std::get <std::string> (variant_string) << "\n\n";
all_possible_types variant_foo = factory [t_foo] ();
std::cout << std::get <Foo> (variant_foo).x << "\n";
}
Output:
make_string Initial string value
Initial string value
make_foo 42
42
Live demo.
As I say, this doesn't look like much now but I will improve it later. In the meantime, I suggest you take a look at it to get your head around what I'm doing here.
I am currently trying to teach myself variadic templates. However I am having trouble understanding anything past a simple adding template.
Currently I would want a template that would do the following:
Take any number of types
Takes parameters that requires the user to enter them in the following format:
T value, string descriptor
It then goes through each variable one by one, printing the descriptor before reading the variable
For example the output should look like this:
x (int) //this is the descriptor
//here something is being read into the variable x
y (int) //this is another descriptor
//something else is being read into y
.
.
.
Since its always the same operation, this should be possible. However my best try looked like this
template<typename t,typename... Args>
void generic_reader(t first,string desc,Args... args)
{
cout<<desc<<endl;
cin>>first;
generic_reader(args);
}
Obviously this doesnt work. However I cant think of another way of doing this. Again I have only started to work with variadic templates.
Can someone show me a solution with a detailed explanation?
Here's one way, using recursion.
#include <iostream>
// provide a terminating case
void generic_read()
{
}
// provide the general case which picks off the first 2 arguments
// and forwards the rest to another version of itself.
template<typename T, typename Printable, typename...Rest>
void generic_read(T& value ,Printable&& desc,Rest&&...rest)
{
std::cout << desc << std::endl;
std::cin >> value;
generic_read(std::forward<Rest>(rest)...);
}
// test
int main()
{
int x;
double y;
generic_read(x, "an integer:", y, "a double");
}
You're basically there -- you're just missing a base case. Also, you're missing the ... on your recursive call to generic_reader; it should be generic_reader(args...).
Here's some working code that does what you're trying to do:
#include <string>
#include <iostream>
void generic_reader()
{
std::cout << "no more stuff!" << std::endl;
}
template <typename T, typename... Args>
void generic_reader(T& first, const std::string& desc, Args&... args)
{
std::cout << desc << std::endl;
std::cin >> first;
std::cin.ignore(100, '\n');
generic_reader(args...);
}
int main()
{
int x, y, z;
generic_reader(x, "x", y, "y", z, "z");
std::cout << "x: " << x << " y: " << y << " z: " << z << std::endl;
return 0;
}
`
Walking through the code: your approach was correct, but there's no base case when you run out of arguments. On the second to last call, the remaining arguments are (z, "z"), which substitutes into the template successfully. But after that, there is a final call to generic_reader(), with no remaining arguments. You need to provide a candidate that can accept the final (empty) argument list.
One final note -- you'll notice that I passed in first by reference, so I could write to the original variables. If you do this, make sure that the remaining Args... is passed by reference as well! Otherwise, recursive calls will pass the remaining args by value, and calls after the first one will no longer reference the original variables.
It seems to me that you're using a sequence of std::pairs where the first type is fixed, std::string, and the second one is a variable type.
So you can write your function as
template <typename ... Args>
void generic_reader (std::pair<std::string, Args> & ... ps)
{ /* do something */}
and call it as
auto a = std::make_pair<std::string>("a", short(0));
auto b = std::make_pair<std::string>("b", 1);
auto c = std::make_pair<std::string>("c", 2L);
auto d = std::make_pair<std::string>("d", 3LL);
generic_reader(a, b, c, d);
Unfortunately I don't know (before c++17) how to use ps... in the body of the function so, in C++11 and in C++17, the best I can think is a solution based on recursion (as your original, with the recursion call corrected in generic_reader(args...);)
Starting from C++17 it's available a new (and more powerful) mode of use variadic arguments (look for "fold expression") and your function ca be simply written as
template <typename ... Args>
void generic_reader (std::pair<std::string, Args> & ... ps)
{ ( (std::cout << ps.first << std::endl, std::cin >> ps.second), ... ) ; }
The following is a full working C++17 example
#include <utility>
#include <iostream>
template <typename ... Args>
void generic_reader (std::pair<std::string, Args> & ... ps)
{ ( (std::cout << ps.first << std::endl, std::cin >> ps.second), ... ) ; }
template <typename ... Args>
void variadic_printer (Args & ... as)
{ ( (std::cout << as.first << ", " << as.second << std::endl), ... ) ; }
int main ()
{
auto a = std::make_pair<std::string>("a", short(0));
auto b = std::make_pair<std::string>("b", 1);
auto c = std::make_pair<std::string>("c", 2L);
auto d = std::make_pair<std::string>("d", 3LL);
generic_reader(a, b, c, d);
variadic_printer(a, b, c, d);
}
If you prefer not to use recursion you can always use this (c++14, but there exist implementations of index_sequence for c++11):
#include <utility>
#include <iostream>
#include <tuple>
template <class Tuple, std::size_t... Is>
void generic_reader_impl(std::index_sequence<Is...>, Tuple&& tuple) {
std::size_t dummy[] = { 0ul,
(static_cast<void>(std::cout << std::get<2ul*Is + 1ul>(tuple) << std::endl),
static_cast<void>(std::cin >> std::get<2ul*Is>(tuple)),
Is)...
};
static_cast<void>(dummy);
}
template <class... Args>
void generic_reader(Args&&... args) {
generic_reader_impl(std::make_index_sequence<sizeof...(Args) / 2>{}, std::forward_as_tuple(std::forward<Args>(args)...));
}
int main() {
int x;
double y;
generic_reader(x, "an integer:", y, "a double");
std::cout << x << std::endl;
std::cout << y << std::endl;
}
Output:
1
1.2
[live demo]
I would like to have a general function 'request' which could accept a tuple of any number of arguments. I want the 'request' function to dispatch the call to a number of other functions, depending on the number of arguments (of course the interface of the functions must match).
I wrote this code, but it only works if I call function of one type inside the 'request'. As soon as I uncomment the dispatching mechanism (else -> dispatch to fun5) everything stops compiling.
The problem is that the body of function 'request', created for the case of dispatching to function with two parameters, must compile, and then there is a function with 5 arguments inside it, to which the tuple of 2 arguments cannot be applied. And vice versa. Classic problem with templates. I know that I could somehow apply SFINAE concept to this problem, but I somehow don't know how (I am not as strong in MPL programming).
#include <iostream>
#include <experimental/tuple>
enum class type { v2, v5 };
void fun2(int i1, int i2)
{
std::cout << "fun2 called, i1 = " << i1 << ", i2 = " << i2 << std::endl;
}
void fun5(bool b1, float f1, int i, char c, bool b2)
{
std::cout << "fun5 called with: " << std::boolalpha << b1 << ", " << f1 << ", " << i << ", " << c << ", " << b2 << std::endl;
}
template <typename F, typename... T>
void dispatch(F f, T... args)
{
std::experimental::apply(f, args...);
}
template <typename... T>
void request(type t, T... args)
{
if (t == type::v2)
dispatch(fun2, args...);
// else
// dispatch(fun5, args...);
}
int main()
{
auto v2 = std::make_tuple(1,1);
request(type::v2, v2);
// auto v5 = std::make_tuple(true, 1.5f, 3, 'c', false);
// request(type::v5, v5);
}
How can I make this work? What kind of dispatching mechanism I need here to make this work?
Instead of using an enumeration to select what to do, I suggest you use tags and tag structures instead. Then you can simply select the right dispatch function using simple function overloading.
Perhaps something like
namespace type
{
struct v2_tag {};
struct v5_tag {};
v2_tag v2;
v5_tag v5;
}
template<typename... T>
void request(type::v2_tag, T... args)
{
dispatch(fun2, args...);
}
template<typename... T>
void request(type::v5_tag, T... args)
{
dispatch(fun5, args...);
}
The rest of the code stays the same.
An alternative to tag dispatch (which I highly recommend as per #Some programmer dude) would be to create your own function object that accepts a type as a non-type template argument so that we can take advantage of constexpr if:
template<type t>
struct request
{
template<class... T>
void operator()(T... args) const
{
if constexpr(t == type::v2)
dispatch(fun2, args...);
else
dispatch(fun5, args...);
}
};
The downside is that you have to construct one to make your call:
auto v2 = std::make_tuple(1, 1);
request<type::v2>()(v2);
auto v5 = std::make_tuple(true, 1.5f, 3, 'c', false);
request<type::v5>()(v5);
Demo
A variation on this approach is to instead have a static apply function in your request class like so:
template<type t>
struct request{
template<class... T>
static void apply(T... args){/*..*/}
}
And then a call to it would look like this instead (no funky empty braces):
request<type::v2>::apply(v2);
Demo2
I want to write an event manager that supports passing an arbitrary number of arguments. To show you the form, here is an example. Please note that one goal is to not need a class definition for every event. Instead, events are represented by string names. First, lets register four listeners to the same event. They differ in the number of parameters they accept.
Events events;
events.listen("key", [=] {
cout << "Pressed a key." << endl;
});
events.listen("key", [=](int code) {
cout << "Pressed key with code " << code << "." << endl;
});
events.listen("key", [=](int code, string user) {
cout << user << " pressed key with code " << code << "." << endl;
});
events.listen("key", [=](int code, string user, float duration) {
cout << user << " pressed key with code " << code << " for " << duration
<< " seconds." << endl;
});
events.listen("key", [=](string user) {
cout << user << " pressed a key." << endl;
});
Now fire the event with some arguments. events.fire("key", {42, "John"}); This should call registered lambdas that match some or all of the arguments. For example, this call should produce the following result for the five listeners we registered.
Print "Pressed a key."
Print "Pressed key with code 42."
Print "John pressed key with code 42."
Throw exception because listener doesn't match signature.
Throw exception because listener doesn't match signature.
Is it possible to achieve this behavior in C++? If so, how can I store the different callbacks in a collection while still being able to cast them back for calling with different numbers of parameters? I think this task is not easy so every hint helps.
I agree with Luc's point that a type-safe approach is probably more appropriate, but the following solution does more or less what you want, with a few limitations:
Argument types must be copyable;
Arguments are always copied, never moved;
A handler with N parameters is invoked if and only if the types of the first N arguments to fire() match exactly the types of the handler's parameters, with no implicit conversions being performed (e.g. from string literal to std::string);
Handlers cannot be functors with more than one overloaded operator ().
This is what my solution eventually allows you to write:
void my_handler(int x, const char* c, double d)
{
std::cout << "Got a " << x << " and a " << c
<< " as well as a " << d << std::endl;
}
int main()
{
event_dispatcher events;
events.listen("key",
[] (int x)
{ std::cout << "Got a " << x << std::endl; });
events.listen("key",
[] (int x, std::string const& s)
{ std::cout << "Got a " << x << " and a " << s << std::endl; });
events.listen("key",
[] (int x, std::string const& s, double d)
{ std::cout << "Got a " << x << " and a " << s
<< " as well as a " << d << std::endl; });
events.listen("key",
[] (int x, double d)
{ std::cout << "Got a " << x << " and a " << d << std::endl; });
events.listen("key", my_handler);
events.fire("key", 42, std::string{"hi"});
events.fire("key", 42, std::string{"hi"}, 3.14);
}
The first call to fire() will produce the following output:
Got a 42
Got a 42 and a hi
Bad arity!
Bad argument!
Bad arity!
While the second call will produce the following output:
Got a 42
Got a 42 and a hi
Got a 42 and a hi as well as a 3.14
Bad argument!
Bad argument!
Here is a live example.
The implementation is based on boost::any. The heart of it is the dispatcher functor. Its call operator takes a vector of type-erased arguments and dispatches them to the callable object with which it is constructed (your handler). If the arguments type don't match, or if the handler accepts more arguments than provided, it just prints an error to the standard output, but you can make it throw if you wish or do whatever you prefer:
template<typename... Args>
struct dispatcher
{
template<typename F> dispatcher(F f) : _f(std::move(f)) { }
void operator () (std::vector<boost::any> const& v)
{
if (v.size() < sizeof...(Args))
{
std::cout << "Bad arity!" << std::endl; // Throw if you prefer
return;
}
do_call(v, std::make_integer_sequence<int, sizeof...(Args)>());
}
private:
template<int... Is>
void do_call(std::vector<boost::any> const& v, std::integer_sequence<int, Is...>)
{
try
{
return _f((get_ith<Args>(v, Is))...);
}
catch (boost::bad_any_cast const&)
{
std::cout << "Bad argument!" << std::endl; // Throw if you prefer
}
}
template<typename T> T get_ith(std::vector<boost::any> const& v, int i)
{
return boost::any_cast<T>(v[i]);
}
private:
std::function<void(Args...)> _f;
};
Then there are a couple of utilities for creating dispatchers out of a handler functor (there is a similar utility for creating dispatchers out of function pointers):
template<typename T>
struct dispatcher_maker;
template<typename... Args>
struct dispatcher_maker<std::tuple<Args...>>
{
template<typename F>
dispatcher_type make(F&& f)
{
return dispatcher<Args...>{std::forward<F>(f)};
}
};
template<typename F>
std::function<void(std::vector<boost::any> const&)> make_dispatcher(F&& f)
{
using f_type = decltype(&F::operator());
using args_type = typename function_traits<f_type>::args_type;
return dispatcher_maker<args_type>{}.make(std::forward<F>(f));
}
The function_traits helper is a simple trait to figure out the types of the handler so we can pass them as template arguments to dispatcher:
template<typename T>
struct function_traits;
template<typename R, typename C, typename... Args>
struct function_traits<R(C::*)(Args...)>
{
using args_type = std::tuple<Args...>;
};
template<typename R, typename C, typename... Args>
struct function_traits<R(C::*)(Args...) const>
{
using args_type = std::tuple<Args...>;
};
Clearly this whole thing won't work if your handler is a functor with several overloaded call operators, but hopefully this limitation won't be too severe for you.
Finally, the event_dispatcher class allows you storing type-erased handlers in a multimap by calling listen(), and invokes them when you call fire() with the appropriate key and the appropriate arguments (your events object will be an instance of this class):
struct event_dispatcher
{
public:
template<typename F>
void listen(std::string const& event, F&& f)
{
_callbacks.emplace(event, make_dispatcher(std::forward<F>(f)));
}
template<typename... Args>
void fire(std::string const& event, Args const&... args)
{
auto rng = _callbacks.equal_range(event);
for (auto it = rng.first; it != rng.second; ++it)
{
call(it->second, args...);
}
}
private:
template<typename F, typename... Args>
void call(F const& f, Args const&... args)
{
std::vector<boost::any> v{args...};
f(v);
}
private:
std::multimap<std::string, dispatcher_type> _callbacks;
};
Once again, the whole code is available here.
one goal is to not need a class definition for every event.
That’s a good sign that you want something else than C++ for your purposes, since it has no dynamic reflection capabilities. (If you do use something more dynamic but still need to interface with C++, you would need to bridge the gap though, so this answer may or may not still be useful for that.)
Now while it is possible to build a (limited) dynamic system, you should ask yourself if it is what you really want to do. E.g. if you ‘close the world’ of events and their callback signatures, you would retain a lot of type-safety:
// assumes variant type, e.g. Boost.Variant
using key_callback = variant<
function<void(int)> // code
, function<void(int, string)> // code, user
, function<void(int, string, float)> // code, user, duration
, function<void(string)> // user
>;
using callback_type = variant<key_callback, …more event callbacks…>;
In the spirit of sticking to your requirement though, here’s how to store any† callback, and still be able to call it:
using any = boost::any;
using arg_type = std::vector<any>;
struct bad_signature: std::exception {};
struct bad_arity: bad_signature {};
struct bad_argument: bad_signature {
explicit bad_argument(int which): which{which} {}
int which;
};
template<typename Callable, typename Indices, typename... Args>
struct erased_callback;
template<typename Callable, std::size_t... Indices, typename... Args>
struct erased_callback<Callable, std::index_sequence<Indices...>, Args...> {
// you can provide more overloads for cv/ref quals
void operator()(arg_type args)
{
// you can choose to be lax by using <
if(args.size() != sizeof...(Args)) {
throw bad_arity {};
}
callable(restore<Args>(args[Indices], Indices)...);
}
Callable callable;
private:
template<typename Arg>
static Arg&& restore(any& arg, int index)
{
using stored_type = std::decay_t<Arg>;
if(auto p = boost::any_cast<stored_type>(&arg)) {
return std::forward<Arg>(*p);
} else {
throw bad_argument { index };
}
}
};
template<
typename... Args, typename Callable
, typename I = std::make_index_sequence<sizeof...(Args)>
>
erased_callback<std::decay_t<Callable>, I, Args...> erase(Callback&& callback)
{ return { std::forward<Callback>(callback) }; }
// in turn we can erase an erased_callback:
using callback_type = std::function<void(arg_type)>;
/*
* E.g.:
* callback_type f = erase<int>([captures](int code) { ... });
*/
Coliru demo.
If you have a type trait that can guess the signature of a callable type, you can write an erase that uses it (while still allowing the user to fill it in for those cases where it can’t be deduced). I’m not using one in the example because that’s another can of worms.
†: ‘any‘ meaning any callable object accepting some numbers of copyable arguments, returning void—you can relax the requirements on arguments by using a move-only wrapper similar to boost::any