For my current project, I've been writing a lot of C/C++ to Lua wrappers. A large number of these are simple setters and getters, so I managed to write some templates that make it easy to generate these, like so:
// Class Return Field
template <typename T, typename U, U T::*Member>
int luaU_get(lua_State* L)
{
T* obj = luaW_check<T>(L, 1);
luaU_push<U>(L, obj->*Member);
return 1;
}
static luaL_reg Foo_Table[] =
{
...
// Now I can just use this generic template to avoid
// writing simple getter functions
{ "getbar", luaU_get<Foo, Bar, &Foo::bar> },
...
};
I would like to do something similar for simple function wrappers for arbitrary functions too. For example, it would be nice to be able to do this:
template <typename T, typename U, U (T::*Func)(), typename... Args>
int luaU_func(lua_State* L)
{
// ...?
}
static luaL_reg Foo_Table[] =
{
...
{ "baz", luaU_func<Foo, int, &Foo::baz, int, float> },
...
};
The idea is that the template effectively turn out to be this when compiled:
int luaU_func(lua_State* L)
{
luaU_push<int>(L, luaW_check<Foo>(L, 1)->baz(luaU_check<int>(L, 2), luaU_check<float>(L, 3)));
return 1;
}
I've tried just using the ... expander, the problem for me is the integer index values to map to the proper arguments. I can't think of a way to get them working right. Is such a thing even possible?
(there is a little bit of magic going on here already; I wrote some templated wrappers for things like lua_push and lua_check. All of those existing wrappers can be found here)
The trick is to exploit template argument deduction by partially specializing a class template that contains the wrapper function:
// Lua API dummies ...
struct lua_State {};
template<class T> void luaU_push(lua_State*,T);
template<class T> T* luaW_check(lua_State*,int);
template<class T> T luaU_check(lua_State*,int);
// metaprogramming for creating indices ...
template<int...Ints>
struct int_pack {};
template<int Begin, int Count, int...Tail>
struct make_int_range_type {
typedef typename make_int_range_type<Begin,Count-1,Begin+Count-1,Tail...>::type type;
};
template<int Begin, int...Tail>
struct make_int_range_type<Begin,0,Tail...> {
typedef int_pack<Tail...> type;
};
template<int Begin, int Count>
inline typename make_int_range_type<Begin,Count>::type
make_int_range()
{ return typename make_int_range_type<Begin,Count>::type(); }
// the actual wrapper ...
template<class MemFunPtrType, MemFunPtrType PMF>
struct lua_mem_func_wrapper;
template<class Clazz, class ReturnType, class...Args, ReturnType(Clazz::*PMF)(Args...)>
struct lua_mem_func_wrapper<ReturnType(Clazz::*)(Args...),PMF> {
static int doit(lua_State* L) {
return doit_impl(L,make_int_range<2,sizeof...(Args)>());
}
private:
template<int...Indices>
static int doit_impl(lua_State* L, int_pack<Indices...>) {
luaU_push<ReturnType>(L,
(luaW_check<Clazz>(L, 1)->*PMF)(
luaU_check<Args>(L, Indices)...
)
);
return 1;
}
};
#define GET_MEM_FUN_WRAPPER(...) &lua_mem_func_wrapper<decltype(__VA_ARGS__),__VA_ARGS__>::doit
// testing ...
struct foo {
int baz(int, float);
};
void test() {
auto* ptr = GET_MEM_FUN_WRAPPER(&foo::baz);
}
This code compiles under G++ 4.6.1 using the options -c --std=c++0x. To see whether it really does what you want, please test it ...
Reusing the indices generation code from this answer and ignoring the function call to Func (don't know how exactly this is intended to be used), this is how it could look like:
template <typename T, typename U, U (T::*Func)(),
typename... Args, size_t... Idx>
int luaU_func_impl(lua_State* L, Collection<Idx...>)
{
luaU_push<int>(L, luaW_check<U>(L, 1), luaU_check<Args>(L, Idx+2)...);
return 1;
}
template <typename T, typename U, U (T::*Func)(), typename... Args>
int luaU_func(lua_State* L)
{
typename GenerateCollection<Args...>::type Indices;
return luaU_func_impl<T, U, Func, Args...>(L, Indices);
}
Related
Hi I was wonder if anyone could help me with this. I'm trying to implement the CallFunction (the bit with the comment in). I'm not sure how to go about doing it.
[Question] I want to create a tuple of types created from the ...Arguments, but I want to
strip the qualifiers, const, &, etc, so then I can fill it up with my data and somehow pass onward to the function.
Im not bothered with the return type for now. Anyone point me in the right direction or have done something similar? Or is it completely crazy and not something that can be done. HMM! Anyway TY for checking it out :)
template <typename This, typename Func> class ForwardFunction
{
private:
template <typename Object, typename Return, typename ...Arguments> struct info_base
{
enum { nargs = sizeof...(Arguments) };
typedef Return return_type;
typedef std::tuple<Arguments...> arg_list_type;
template <size_t index> struct arg
{
typedef typename std::tuple_element<index, std::tuple<Arguments...>>::type type;
};
template <int argCount> static void CallFunction(Func function, CLuaHelper & helper, lua_State *pState)
{
/*
// temp - pseudo
arg_list_type argList;
for (int i = 0; i < argCount; ++i)
std::get<0>(argList) = helper.Get<arg<i>::type>(pState);
(This::GetThis(pState)->*(function))(argList...);
*/
}
template <> static void CallFunction<0>(Func function, CLuaHelper & helper, lua_State *pState)
{
(This::GetThis(pState)->*(function))();
}
static void Call(Func function, CLuaHelper & helper, lua_State *pState)
{
CallFunction<nargs>(function, helper, pState);
}
};
template <typename Func> struct info;
template <typename Object, typename Return, typename ...Arguments> struct info<std::function<Return (Object::*)(Arguments...)>> : info_base<Object, Return, Arguments...> { };
template <typename Object, typename Return, typename ...Arguments> struct info<std::function<Return (Object::*)(Arguments...) const>> : info_base<Object, Return, Arguments...> { };
public:
static int ForwardCall(Func function, lua_State *pState)
{
CLuaHelper helper(pState);
info<std::function<Func>>::Call(function, helper, pState);
return helper.ReturnValues();
}
};
Its used with
#define __LUA_FUNCTION_CALL [&](lua_State *pState) -> int
#define __LUA_INSTANT_ACCESS_CALL(name) \
{ #name, __LUA_FUNCTION_CALL { return ForwardFunction<CComponentScript, decltype(&CComponent::##name)>::ForwardCall(&CComponent::##name, pState); } }
const CLuaHelper::function_list CComponentScript::m_sRegisterFunctions[] =
{
__LUA_INSTANT_ACCESS_CALL(SetOnLoad),
__LUA_INSTANT_ACCESS_CALL(SetOnEvent),
__LUA_INSTANT_ACCESS_CALL(SetOnUpdate),
__LUA_INSTANT_ACCESS_CALL(SetOnClose),
__LUA_INSTANT_ACCESS_CALL(RegisterEvent),
__LUA_INSTANT_ACCESS_CALL(SetBasePriority),
{nullptr, nullptr}
};
I assume, you mean to assign the values with the corresponding index rather than always assigning to the 0 index inside the loop. The basic approach to call a function with the elements of a std::tuple<...> is rather straight forward assuming you have an implementation of std::integer_sequence (which is part of C++14):
template <int... N, typename... T>
void call_aux(std::integer_sequence<int, N...>, std::tuple<T...>&& value)
{
print(std::get<N>(value)...);
}
template <typename Tuple>
void call(Tuple&& value)
{
call_aux(std::make_integer_sequence<int, std::tuple_size<std::decay_t<Tuple>>::value>(),
std::forward<Tuple>(value));
}
The basic idea is that std::make_integer_sequence<...> creates a suitable sequence of integers. Once you got this, you can also add a corresponding operation fill the a std::tuple<...> with the values based on your pState. The logic to actually fill the std::tuple<...> could look something like this (I don't have Lua installed, i.e., I can't test whether this actually works but something like this does work):
template <int I, typename Tuple>
bool assign_helper2(Tuple& tuple, lua_Helper& helper, lua_State* pState) {
std::get<I>(tuple) = helper.Get<arg<I>::type>(pState);
}
template <typename... T>
void dummy(T&&...) {
}
template <int I, typename Tuple>
void assign_helper1(std::integer_sequence<int, I...>, Tuple& tuple,
lua_Helper& helper, lua_State* pState) {
dummy(assign_helper2<I>(tuple, helper, pState)...);
}
template <typename Tuple>
void assign(Tuple& tuple, lua_Helper& helper, lua_State* pState) {
assign_helper1(std::make_integer_sequence<int, std::tuple_size<std::decay_t<Tuple>>::value>(),
tuple, helper, pState);
}
Although the code uses C++14 functionality, the corresponding classes can be implemented using C++11. The implementation is fairly straight forward. Here is an implementation implementing the necessary integer generation using different names, though.
I want to write benchmark code for several combinations of several possible classes. If I write each combination myself it becomes an unmaintainable mess. Thus I'm looking for a way to automatically combine each type via templates, something akin to the following pseudo code:
for (typename HashFuction : Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512) {
for (typename KeyingWrapper : TwoPassKeyedHash, OnePassKeyedHash, PlainHash) {
for (typename InstantiatedGetLeaf: GetLeaf<8>, GetLeaf<1024>) {
for (typename algorithm : algA, algB, algC) {
runAndTime<HashFunction,KeyingWrapper,
InstantiatedGetLeaf,algorithm>(someArgs);
}
}
}
}
Where Sha256,… ,TwoPassKeyedHash,… are types.
The code I'm looking for is supposed to be functionally equivalent to the following:
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algA>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algB>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algC>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algA>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algB>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algC>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algA>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algB>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algC>(someArgs);
// And 99 further lines…
With Peregring-lk's help I have come as far as
#include <iostream>
template<typename Aux_type>
void test_helper()
{}
template<typename Aux_type, typename Head, typename... Tail>
void test_helper() {
std::cout << Head::i;
test_helper<Aux_type, Tail...>();
}
template<typename... Args>
void test()
{
test_helper<void, Args...>();
}
struct A{
static const int i=1;
};
struct B{
static const int i=2;
};
int main() {
test<A, B>();
return 0;
}
but I don't yet see how I could iterate that recursion to get nested loops. Any help would be appreciated.
(Edit: Code restructuring and inclusion of Peregring-lk's answer.)
Sometimes it helps to have an idea of what you are aiming for:
you need several parameter types
and for each parameter types, several possible "values"
And want to apply something on every single combination of values (one per parameter type at a time).
This looks like it could be expressed:
combine<
Set<Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512>,
Set<TwoPassKeyedHash, OnePassKeyedHash, PlainHash>,
Set<GetLeaf<8>, GetLeaf<1024>>,
Set<algA, algB, algC>
>(runAndTime);
if runAndTime is an instance of:
struct SomeFunctor {
template <typename H, typename W, typename L, typename A>
void operator()(cons<H>{}, cons<W>{}, cons<L>{}, cons<A>{});
};
and cons is just a way to pass a type as a regular parameter (much easier).
Let's go ?
First, some way to pass around types (cheaply):
template <typename T>
struct cons { using type = T; };
template <typename... T>
struct Set {};
An explicit bind (with no magic inside):
template <typename F, typename E>
struct Forwarder {
Forwarder(F f): inner(f) {}
template <typename... Args>
void operator()(Args... args) { inner(cons<E>{}, args...); }
F inner;
}; // struct Forwarder
And now we delve into the real task at hand:
we need to iterate on sets of types
within a set, we need to iterate on its elements (types too)
That calls for two levels of dispatch:
template <typename FirstSet, typename... Sets, typename F>
void combine(F func);
template <typename Head, typename... Tail, typename... Sets, typename F>
void apply_set(F func, Set<Head, Tail...>, Sets... others);
template <typename... Sets, typename F>
void apply_set(F func, Set<>, Sets... others);
template <typename E, typename NextSet, typename... Sets, typename F>
void apply_item(F func, cons<E>, NextSet, Sets...);
template <typename E, typename F>
void apply_item(F func, cons<E> e);
Where combine is the outer (exposed) function, apply_set is used to iterate on the sets and apply_item is used to iterate on the types within a set.
The implementations are simple:
template <typename Head, typename... Tail, typename... Sets, typename F>
void apply_set(F func, Set<Head, Tail...>, Sets... others) {
apply_item(func, cons<Head>{}, others...);
apply_set(func, Set<Tail...>{}, others...);
} // apply_set
template <typename... Sets, typename F>
void apply_set(F, Set<>, Sets...) {}
template <typename E, typename NextSet, typename... Sets, typename F>
void apply_item(F func, cons<E>, NextSet ns, Sets... tail) {
Forwarder<F, E> forwarder(func);
apply_set(forwarder, ns, tail...);
}
template <typename E, typename F>
void apply_item(F func, cons<E> e) {
func(e);
} // apply_item
template <typename FirstSet, typename... Sets, typename F>
void combine(F func) {
apply_set(func, FirstSet{}, Sets{}...);
} // combine
For each of apply_set and apply_item we have a recursive case and a base case, though it's some kind of co-recursion here as apply_item calls back to apply_set.
And a simple example:
struct Dummy0 {}; struct Dummy1 {}; struct Dummy2 {};
struct Hello0 {}; struct Hello1 {};
struct Tested {
Tested(int i): value(i) {}
void operator()(cons<Dummy0>, cons<Hello0>) { std::cout << "Hello0 Dummy0!\n"; }
void operator()(cons<Dummy0>, cons<Hello1>) { std::cout << "Hello1 Dummy0!\n"; }
void operator()(cons<Dummy1>, cons<Hello0>) { std::cout << "Hello0 Dummy1!\n"; }
void operator()(cons<Dummy1>, cons<Hello1>) { std::cout << "Hello1 Dummy1!\n"; }
void operator()(cons<Dummy2>, cons<Hello0>) { std::cout << "Hello0 Dummy2!\n"; }
void operator()(cons<Dummy2>, cons<Hello1>) { std::cout << "Hello1 Dummy2!\n"; }
int value;
};
int main() {
Tested tested(42);
combine<Set<Dummy0, Dummy1, Dummy2>, Set<Hello0, Hello1>>(tested);
}
Which you can witness live on Coliru prints:
Hello0 Dummy0!
Hello1 Dummy0!
Hello0 Dummy1!
Hello1 Dummy1!
Hello0 Dummy2!
Hello1 Dummy2!
Enjoy :)
Note: it was presumed that the functor was cheap to copy, otherwise a reference can be used, both when passing and when storing it in Forwarder.
Edit: removed the cons around Set (everywhere it appeared), it's unnecessary.
Functions doesn't allow partial specializations, unless the specialization is complete. Every new different function signature declares a new overload, unless their signatures are exactly the same.
Try instead the following code:
#include <iostream>
template<typename Aux_type>
void test_helper()
{}
template<typename Aux_type, typename Head, typename... Tail>
void test_helper() {
std::cout << Head::i;
test_helper<Aux_type, Tail...>();
}
template<typename... Args>
void test()
{
test_helper<void, Args...>();
}
struct A{
static const int i=1;
};
struct B{
static const int i=2;
};
int main() {
test<A, B>();
return 0;
}
and it does compile (and prints 12).
Anyway, I've not understood your pseudocode sample.
I think that C++ is not the right tool, when it comes to convenient and flexible code generation ... Just write a simple utility in a scripting language of your choice, like in python:
generate_test_code.py:
#!/usr/bin/python
for HashFuction in {"Sha256", "Sha512", "Sa512_256", "Sha3_256", "Sha3_512"}:
for KeyingWrapper in {"TwoPassKeyedHash", "OnePassKeyedHash", "PlainHash"}:
for InstantiatedGetLeaf in {"GetLeaf<8>", "GetLeaf<1024>"}:
for Algorithm in {"algA", "algB", "algC"}:
print("runAndTime<{},{},{},{}>(someArgs);".format(HashFuction,KeyingWrapper,InstantiatedGetLeaf,Algorithm))
... then in your Makefile:
generated_test_code.cpp: generate_test_code.py
python generate_test_code.py > generated_test_code.cpp
... and in your c++ code, simply #include "generated_test_code.cpp" where you want it.
Suppose I have the following function, that takes a function as a parameter.
template <typename F>
void test_func(F f)
{
// typedef typename function_traits<F>::return_type T;
typedef int T;
std::mt19937 rng(std::time(0));
std::uniform_int_distribution<T> uint_dist10(0, std::numeric_limits<T>::max());
f(uint_dist10(rng), uint_dist10(rng)); // Problem!
}
Usage would be:
int foo(int, int) { return 0; }
int bar(int, int, int, int) { return 0; }
int main()
{
test_func(foo);
// test_func(bar);
}
Just like foo and bar, I have several functions that return T, and take some amount of parameters of type T. I would like test_func to generate as many calls to my RNG as the function f takes parameters. In other words, we can assume T is always an integer type, and that each parameter will be the same, i.e. a function call to an RNG.
Using function_traits (such as the ones in Boost), I can fetch the return type of F, and that helps a little. Roughly, my question is
How can I generate a needed amount of function calls so that it matches the arity of the function F?
Before C++11, I would have looked at Boost.Preprocessor, or maybe relied on template specialization. Is there a nicer way of doing it now?
First define a meta function called arity to compute arity of the function (it is just a simple implementation; can be improved to compute arity of functors also. See my answer here.):
template<typename F>
struct arity;
template<typename R, typename ...Args>
struct arity<R (*)(Args...)>
{
static const std::size_t value = sizeof ... (Args);
};
then define another meta function called genseq to generate a compile time sequence of integral values:
template<int ... N>
struct seq
{
using type = seq<N...>;
template<int I>
struct push_back : seq<N..., I> {};
};
template<int N>
struct genseq : genseq<N-1>::type::template push_back<N-1> {};
template<>
struct genseq<0> : seq<> {};
template<int N>
using genseq_t = typename genseq<N>::type; //Just a friendly alias!
then a function invoker as:
template<typename F, typename ArgEvaluator, int ...N>
void invoke(seq<N...>, F f, ArgEvaluator arg_evaluator)
{
using arg_type = decltype(arg_evaluator());
constexpr std::size_t arity = sizeof ... (N);
arg_type args[] { (N, arg_evaluator()) ... }; //enforce order of evaluation
f( args[N] ... );
}
And then your code would become this:
template <typename F>
void test_func(F f)
{
// typedef typename function_traits<F>::return_type T;
typedef int T;
std::mt19937 rng(std::time(0));
std::uniform_int_distribution<T> uint_dist10(0, std::numeric_limits<T>::max());
//f(uint_dist10(rng), uint_dist10(rng)); // Problem!
auto arg_evaluator = [&]() mutable { return uint_dist10(rng); };
invoke(genseq_t<arity<F>::value>(), f, arg_evaluator);
}
Here is a sample demo.
Hope that helps.
No need for complicated meta calculations.
template <typename Ret, typename ... T>
void test_func (Ret f (T...))
{
std::mt19937 rng(std::time(0));
f((std::uniform_int_distribution<T>(0, std::numeric_limits<T>::max())(rng))...);
}
int moo(int, int, int){ return 0; }
int main ()
{
test_func(moo);
}
To support functors one needs a bit longer implementation, still not too complicated:
// separate arguments type from function/functor type
template <typename F, typename ... T>
void test_func_impl (F f)
{
std::mt19937 rng(std::time(0));
f((std::uniform_int_distribution<T>(0, std::numeric_limits<T>::max())(rng))...);
}
// overload for a straight function
template <typename Ret, typename ... T>
void test_func (Ret f (T...))
{
test_func_impl<decltype(f), T...>(f);
}
// forwarder for a functor with a normal operator()
template <typename F, typename Ret, typename... T>
void test_func_for_functor (F f, Ret (F::*)(T...))
{
test_func_impl<F, T...>(f);
}
// forwarder for a functor with a const operator()
template <typename F, typename Ret, typename... T>
void test_func_for_functor (F f, Ret (F::*)(T...)const)
{
test_func_impl<F, T...>(f);
}
// overload for anything that has operator()
template <typename F>
void test_func (F f)
{
test_func_for_functor(f, &F::operator());
}
I created a template class containing a std::function as a member the following way:
template<typename Ret, typename... Args>
class Foo
{
private:
std::function<Ret(Args...)> _func;
public:
Foo(const std::function<Ret(Args...)>& func):
_func(func)
{}
};
In order not to have to specify the arguments and return type of the passed function, I created some make_foo overloads:
template<typename Ret, typename... Args>
auto make_foo(Ret (&func)(Args...))
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
template<typename Ret, typename... Args>
auto make_foo(const std::function<Ret(Args...)>& func)
-> Foo<Ret, Args...>
{
return { func };
}
However, I was unable to create a make_foo overload that takes a lambda as parameter:
template<typename Ret, typename... Args>
auto make_foo(??? func)
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
I just can't find a way to have the return type and argument types automatically deduced from the lambda. Is there an idiomatic way to solve such a problem?
Ok, so I thought I would die, but I finally managed to do it ç_ç
First, I used the usual indices. Since I do not have the official ones, I used old indices I wrote some months ago:
template<std::size_t...>
struct indices {};
template<std::size_t N, std::size_t... Ind>
struct make_indices:
make_indices<N-1, N-1, Ind...>
{};
template<std::size_t... Ind>
struct make_indices<0, Ind...>:
indices<Ind...>
{};
Then, I used some function traits found somewhere on StackOverflow. They are nice, and I think that they are equivalent to the Boost library linked in the comments:
template<typename T>
struct function_traits:
function_traits<decltype(&T::operator())>
{};
template<typename C, typename Ret, typename... Args>
struct function_traits<Ret(C::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
using result_type = Ret;
template<std::size_t N>
using arg = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
Then, I was able to write a proper make_foo function and it implementation function, since both are required to use indices. Be careful, it's plain ugly:
template<typename Function, std::size_t... Ind>
auto make_foo_(Function&& func, indices<Ind...>)
-> Foo<
typename function_traits<typename std::remove_reference<Function>::type>::result_type,
typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...>
{
using Ret = typename function_traits<typename std::remove_reference<Function>::type>::result_type;
return { std::function<Ret(typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...)>(func) };
}
template<typename Function, typename Indices=make_indices<function_traits<typename std::remove_reference<Function>::type>::arity>>
auto make_foo(Function&& func)
-> decltype(make_foo_(std::forward<Function>(func), Indices()))
{
return make_foo_(std::forward<Function>(func), Indices());
}
The code is somehow ugly and unreadable, but it definitely works. Hope it does not rely on some implementation-defined behaviour now. Also, thanks all for your advice, it helped! :)
int main()
{
auto lambda = [](int i, float b, long c)
{
return long(i*10+b+c);
};
auto foo = make_foo(lambda);
std::cout << foo(5, 5.0, 2) << std::endl; // 57, it works!
}
And here is the live example :)
I have an example that works with mutable lambdas. I can't quite figure out how to get the CV member qualification right.
First, here's the function template we're after:
#include <functional>
template <typename R, typename ...Args>
void foo(std::function<R(Args...)> f)
{ }
Now we'll let a function template bar take an arbitrary lambda and call the right version of foo, by inspecting the type of the lambda's operator():
#include <type_traits>
template <typename> struct remove_member;
template <typename C, typename T>
struct remove_member<T C::*>
{ using type = T; };
template <typename F>
void bar(F f)
{
using ft = decltype(&F::operator());
foo(std::function<typename remove_member<ft>::type>(f));
}
Example:
int q;
bar([&](int a, int b) mutable -> int { q = a + b; return q / b; });
You can use normal, const lambdas with this modified trait, though I don't like having to spell the function type out:
template <typename C, typename R, typename ...Args>
struct remove_member<R (C::*)(Args...) const>
{ using type = R(Args...); };
I thought it might work with the original code if I use typename std::remove_cv<T>::type, but at least on GCC this doesn't work because of some strange __attribute__((const)) that's set on the lambda's operator type which seems to interfere with the template specialization.
If I have a templated function that takes a lambda expression how do I specialize it so that it can work with lambda expressions that take differing numbers of arguments ? The following code works:
template<typename Lambda>
void doSomething(Lambda callback)
{
callback();
}
doSomething([] () { /* some code here */ } );
but if I want to also be able to call it like this:
doSomething([&foo] (int x) { /* some code here */ } );
doSomething([&foo] (int x, int y) { /* some code here */ } );
what syntax do I use to provide the specialized versions of doSomething() ? This syntax:
template<>
void doSomething<[] (int)>([] (int) callback)
{
callback(3);
}
doesn't compile under Visual Studio 2012 and I haven't been able to find any references to what a valid syntax would look like. Is it possible ? Alternatively, is it possible to tell from inside doSomething() how many arguments the supplied callback takes so that I can do something like this:
template<typename Lambda>
void doSomething(Lambda callback)
{
int numArgs = ???
switch (numArgs) {
case 0: callback(); break;
case 1: callback(1); break;
case 2: callback(1,2); break;
}
}
There are 2 cases for lambdas: when they're capturing and when they're not.
For the first case, you need to extract the return type and parameter types of the lambda (from the operator() of the lambda), it is covered in this question.
For the last case, you can use function pointer specialization, because non-capturing lambdas have a function pointer conversion operator.
Regarding your doSomething() function, it is impossible to directly call the functor according to the deduced number of arguments, as you've done here:
template<typename Lambda>
void doSomething(Lambda callback)
{
int numArgs = ???
switch (numArgs)
{
case 0: callback(); break;
case 1: callback(1); break;
case 2: callback(1,2); break;
}
}
(The needed feature is static if and has been proposed for the next C++ standard, see n3322 and n3329)
You need to use a helper function, or structure, which is specialized with a specific function signature. Something like:
template<typename T> struct helper {};
template<typename R, typename P1>
struct helper<R(P1)>
{
static R call(R (*lambda)(P1)) { return lambda(magic_param); }
};
template<typename R, typename P1, typename P2>
struct helper<R(P1,P2)>
{
static R call(R(*lambda)(P1,P2)) { return lambda(magic_param_1,magic_param_2); }
};
// ...
template<typename R, typename... A>
void doSomething(R (*callback)(A...))
{
helper<R(A...)>::call(callback);
}
Which brings an important question: how would you generate parameters (magic_param...) ?
Using the link to ecatmur's answer provided by Synxis I have managed to code something which does what I wanted. I rewrote the templates to use typedef rather than the new template alias use of the using keyword so the code will work on older compilers. In case anyone else wants to do something similar I have included the code below to show how it can be done:
template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { typedef R(*type)(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { typedef R(*type)(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { typedef R(*type)(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { typedef R(*type)(A...); };
template<typename T>
struct get_function_signature {
typedef typename remove_class<decltype(&std::remove_reference<T>::type::operator())>::type type;
};
template<typename R, typename... A>
struct get_function_signature<R(A...)> { typedef R(*type)(A...); };
template<typename R, typename... A>
struct get_function_signature<R(&)(A...)> { typedef R(*type)(A...); };
template<typename R, typename... A>
struct get_function_signature<R(*)(A...)> { typedef R(*type)(A...); };
// ***************************************************************************
template<typename T> struct num_args {};
template<typename R, typename... A>
struct num_args<R(*)(A...)> { static const int value = sizeof...(A); };
template<typename C, typename R, typename... A>
struct num_args<R(C::*)(A...)> { static const int value = sizeof...(A); };
// ***************************************************************************
template<typename Lambda, int> struct callWithArgsImpl {};
template<typename Lambda>
struct callWithArgsImpl<Lambda, 1> {
static void doIt(Lambda callback, Object* pObj, int arg) { callback(pObj); }
};
template<typename Lambda>
struct callWithArgsImpl<Lambda, 2> {
static void doIt(Lambda callback, Object* pObj, int arg) { callback(pObj, arg); }
};
template<typename Lambda, int N>
void callWithArgs(Lambda callback, Object* pObj, int arg)
{
callWithArgsImpl<Lambda, N>::doIt(callback, pObj, arg);
}
// ***************************************************************************
template<typename Lambda>
void doSomething(int x, Lambda callback)
{
// some code here which gets a pointer to an Object (pObj) and an
// extra piece of information about where the object came from (arg1)
const int numArgs = num_args<typename get_function_signature<Lambda>::type>::value;
callWithArgs<Lambda, numArgs>(callback, pObj, arg1);
}
That allows code to call the doSomething() function passing in a lamba expression which always expects an Object* parameter but the 2nd argument can be omitted if not required. So both of the following lines work:
doSomething(5, [] (Object* pObj) { printf("lambda called with no arg1\n"); } );
doSomething(2, [] (Object* pObj, int arg1) { printf("lambda called with arg1=%d\n", arg); } );
It works but I can't help feeling that it works in spite of the language specification rather than because of it. Using lambda expressions as callback functions seems like a natural thing to do. And I can definitely see cases where I would want to specify callbacks with different signatures. This should be easier to do than this...