I'm looking for a way to implement a function class
template<class ValueType>
class Function { ... };
which can be constructed with a function pointer (or functor) that takes any number of arguments of type ValueType and returns ValueType. For example, given these functions:
double foo(double);
double bar(double, double);
int baz(double, int);
a Function<double> object could be constructed with either foo or bar but not baz.
Then, a member function call will, given some container of ValueType (or an iterator), call the underlying function with the right number of arguments at runtime.
Is such thing possible?
It is indeed possible, you just have to "recursively" unpack arguments from the container and pass them to the function on the deepest level:
#include <cstddef>
#include <utility>
#include <stdexcept>
#include <functional>
#include <type_traits>
#include <vector>
#include <iostream>
namespace detail {
template <std::size_t argument_count>
struct arguments_unpacker {
template <typename Type, typename Function, typename InputIterator, typename... UnpackedArguments>
static Type unpack(Function&& function, InputIterator arguments_begin, InputIterator arguments_end, UnpackedArguments&&... unpacked_arguments) {
if (arguments_begin == arguments_end) {
throw std::invalid_argument("Not enough arguments.");
}
return arguments_unpacker<argument_count - 1>::template unpack<Type>(std::forward<Function>(function), std::next(arguments_begin), arguments_end, std::forward<UnpackedArguments>(unpacked_arguments)..., *arguments_begin);
}
};
template <>
struct arguments_unpacker<0> {
template <typename Type, typename Function, typename InputIterator, typename... UnpackedArguments>
static Type unpack(Function&& function, InputIterator arguments_begin, InputIterator arguments_end, UnpackedArguments&&... unpacked_arguments) {
if (arguments_begin != arguments_end) {
throw std::invalid_argument("Too many arguments.");
}
return function(std::forward<UnpackedArguments>(unpacked_arguments)...);
}
};
template <typename MemberFunction>
struct member_function_arity;
template <typename Result, typename Class, typename... Arguments>
struct member_function_arity<Result(Class::*)(Arguments...)> {
static constexpr std::size_t value = sizeof...(Arguments);
};
template <typename Result, typename Class, typename... Arguments>
struct member_function_arity<Result(Class::*)(Arguments...) const> {
static constexpr std::size_t value = sizeof...(Arguments);
};
template <typename Function>
struct function_arity : member_function_arity<decltype(&Function::operator())> {};
template <typename Result, typename... Arguments>
struct function_arity<Result(*)(Arguments...)> {
static constexpr std::size_t value = sizeof...(Arguments);
};
template <typename Result, typename... Arguments>
struct function_arity<std::function<Result(Arguments...)>> {
static constexpr std::size_t value = sizeof...(Arguments);
};
}
template <typename Type, typename InputIterator, typename Function>
std::function<Type(InputIterator, InputIterator)> variate(Function function) {
using namespace detail;
return [function](InputIterator arguments_begin, InputIterator arguments_end) {
return arguments_unpacker<function_arity<Function>::value>::template unpack<Type>(function, arguments_begin, arguments_end);
};
}
namespace demo {
double a(double x0) {
std::cout << "a(" << x0 << ")\n";
return 0.0;
}
double b(double x0, double x1) {
std::cout << "b(" << x0 << ", " << x1 << ")\n";
return 0.0;
}
double c(double x0, double x1, double x2) {
std::cout << "b(" << x0 << ", " << x1 << ", " << x2 << ")\n";
return 0.0;
}
auto l = [](double x0) mutable {
std::cout << "l(" << x0 << ")\n";
return 0.0;
};
void run() {
using it = std::vector<double>::const_iterator;
auto va = variate<double, it>(&a);
auto vb = variate<double, it>(&b);
auto vc = variate<double, it>(&c);
auto vl = variate<double, it>(l);
std::vector<double> a1 = {1.0};
std::vector<double> a2 = {1.0, 2.0};
std::vector<double> a3 = {1.0, 2.0, 3.0};
va(begin(a1), end(a1));
vb(begin(a2), end(a2));
vc(begin(a3), end(a3));
vl(begin(a1), end(a1));
}
}
int main()
{
demo::run();
return 0;
}
Note that this requres explicitly supplying iterator type. I don't see how it would be possible to remedy that without writing some kind of type erasing any_iterator.
#include <functional>
#include <boost/any.hpp>
template<class ValueType>
struct Function {
template <typename ...Args>
Function(const std::function<ValueType(Args...)>& f)
: fn(f)
{}
template <typename ...Args>
ValueType operator () (Args... args) {
auto f = boost::any_cast<const std::function<ValueType(Args...)>&>(fn);
return f(args...);
}
boost::any fn;
};
int a(int) { return 1; }
int b(int, double) { return 2; }
int main(int argc, char** argv)
{
typedef std::vector<Function<int>> Functions;
Functions functions {
std::function<int(int)>(a),
std::function<int(int, double)>(b)
};
std::cout << functions[0](1) << functions[1](1, 2.0) << std::endl;
}
Related
Expanding on #nes code (https://codereview.stackexchange.com/questions/67241/function-composition-using-stdbind), is there a way to edit the code, so that the input to make_composition_function could be a vector of functions instead of functions as separate arguments.
#include <iostream>
#include <functional>
#include <vector>
// traits to infer the return type of recursive binds
template<typename... Fn>
struct composite_function_traits;
// bind a single function with a placeholder
template<typename F1>
struct composite_function_traits<F1> { typedef decltype(std::bind(std::declval<F1>(), std::placeholders::_1)) type; };
template<typename F1>
typename composite_function_traits<F1>::type make_composite_function(F1&& f1)
{
return std::bind(std::forward<F1>(f1), std::placeholders::_1);
}
// recursively bind multiple functions
template<typename F1, typename... Fn>
struct composite_function_traits<F1, Fn...> { typedef decltype(std::bind(std::declval<F1>(), std::declval<typename composite_function_traits<Fn...>::type>())) type; };
template<typename F1, typename... Fn>
typename composite_function_traits<F1, Fn...>::type make_composite_function(F1&& f1, Fn&&... fn)
{
return std::bind(std::forward<F1>(f1), make_composite_function(std::forward<Fn>(fn)...));
}
int main() {
using namespace std;
auto f1 = [] (int x) { cout << "f1" << endl; return x; };
auto f2 = [] (int x) { cout << "f2" << endl; return x; };
auto f3 = [] (int x) { cout << "f3" << endl; return x; };
// this works -> int y = make_composite_function(f1, f2, f3)(1);
// what I would like to be able to do
std::vector<std::function<int(int)>> funvec;
funvec.push_back(f1);
funvec.push_back(f2);
funvec.push_back(f3);
int y = make_composite_function(funvec)(1);
// print result
cout << y << endl;
}
You might do something like:
template <typename T>
std::function<T(T)> make_composite_function(std::vector<std::function<T(T)>> v)
{
std::reverse(v.begin(), v.end());
return [=](T t) {
for (const auto& f : v) {
t = f(t);
}
return t;
};
}
Demo
You don't even have to use SFINAE for previous overloads by passing vector by value.
Consider the following code:
class Helper {
public:
template<typename taResult, typename taParam> static taResult Cast(const taParam par);
};
template<> inline __m256d Helper ::Cast(const __m256i par) {
return _mm256_castsi256_pd(par);
}
template<> inline __m256i Helper ::Cast(const __m256d par) {
return _mm256_castpd_si256(par);
}
I want to add to the Helper a function to handle casts where the parameter and the return types are equals. All my attempts to specialize/overload so far have failed with different compilation errors.
Something like the following in the class body:
template<typename T> static T Cast(const T par) {
return par;
}
You cannot partial specialize function, and your overload would be ambiguous.
You can add class which you can partial specialize though:
template <typename To, typename From> struct CastImpl;
template <typename T> struct CastImpl<T, T>
{
T operator()(T t) const { return t; }
};
template <> struct CastImpl<__m256d, __m256i>
{
__m256d operator()(__m256i t) const { return _mm256_castsi256_pd(t); }
};
template <> struct CastImpl<__m256i, __m256d>
{
__m256i operator()(__m256d t) const { return _mm256_castpd_si256(t); }
};
and then
class Helper {
public:
template<typename taResult, typename taParam>
static taResult Cast(const taParam par)
{
return CastImpl<taResult, taParam>{}(par);
}
};
No you can not, because that would be an attempt to partially specialize a function, which is not allowed. Instead, you'd have to use an intermediate template class, which than can be specialized.
I can provide example if needed.
You can use a helper class/struct template to implement Helper::Cast.
Here's a simple program that has uses a few shortcuts to demonstrate the concept.
using __m256d = double;
using __m256i = int;
template<typename taResult, typename taParam> struct RealHelper;
class Helper
{
public:
template<typename taResult, typename taParam> static taResult Cast(const taParam par)
{
return RealHelper<taResult, taParam>::doit(par);
}
private:
};
template <> struct RealHelper<__m256d, __m256i>
{
inline static __m256d doit(const __m256i par)
{
// return _mm256_castsi256_pd(par);
return par;
}
};
template <> struct RealHelper<__m256i, __m256d>
{
inline static __m256i doit(const __m256d par)
{
// return _mm256_castpd_si256(par);
return par;
}
};
template <typename T> struct RealHelper<T, T>
{
inline static T doit(const T par)
{
return par;
}
};
int main()
{
auto v1 = Helper::Cast<int, double>(10);
auto v2 = Helper::Cast<double, int>(20);
auto v3 = Helper::Cast<int, int>(30);
auto v4 = Helper::Cast<double, double>(40);
}
I want to add to the Helper a function to handle casts where the parameter and the return types are equals.
What about using SFINAE to enable/disable a Cast() version according the value of std::is_same<taResult, taParam>::value ?
A simplified example
#include <iostream>
#include <type_traits>
struct Helper
{
template <typename taR, typename taP>
static std::enable_if_t<false == std::is_same<taR, taP>::value, taR>
Cast (taP const & par)
{ std::cout << "different Cast" << std::endl; return {}; }
template <typename taR, typename taP>
static std::enable_if_t<true == std::is_same<taR, taP>::value, taR>
Cast (taP const & par)
{ std::cout << "equal Cast" << std::endl; return par; }
};
template <>
int Helper::Cast<int, long> (long const & par)
{ std::cout << "int/long Cast" << std::endl; return {}; }
template <>
long Helper::Cast<long, int> (int const & par)
{ std::cout << "long/int Cast" << std::endl; return {}; }
int main()
{
Helper::template Cast<int>(0); // print "equal Cast"
Helper::template Cast<int>(0L); // print "int/log Cast"
Helper::template Cast<long>(0); // print "long/int Cast"
Helper::template Cast<long>("foo"); // print "different Cast"
}
I know about ADL and the swap idiom:
using std::swap;
swap(x, y);
boost::swap() does the above for you. Now, I want to push it further. Specifically, Have the swap perform x.swap(y) if possible, and fallback to boost::swap() otherwise. So, you don't have to implement both a member swap and a free one, which is verbose and redundant. I tried to implement such a swap and ended up with the following. Implementing things like this can become quite tricky. So, I'm wondering whether there is any flaws in my implementation, or if more succinct implementations exist.
#include <algorithm>
#include <utility>
namespace cppu_detail_swap {
template <typename T>
void swap_impl(T& x, T& y) {
using std::swap;
swap(x, y);
}
} // namespace cppu_detail_swap
namespace cppu {
namespace detail {
template <typename T>
void swap(T& x, T& y, int) {
cppu_detail_swap::swap_impl(x, y);
}
template <typename T>
auto swap(T& x, T& y, char) -> decltype(x.swap(y)) {
return x.swap(y);
}
} // namespace detail
template <typename T>
void swap(T& x, T& y) {
detail::swap(x, y, ' ');
}
} // namespace cppu
Your current solution is flawed for objects from the cppu namespace, e.g.
// [insert your code here]
namespace cppu
{
struct X{};
struct Y{ void swap(Y& y) { }; };
}
int main()
{
auto x1 = cppu::X{};
auto x2 = cppu::X{};
swap(x1, x2);
auto y1 = cppu::Y{};
auto y2 = cppu::Y{};
swap(y1, y2);
}
g++ tells me:
taste.cpp:9:7: error: call of overloaded ‘swap(cppu::X&, cppu::X&)’ is ambiguous
To get rid of this, you need to explicitly call std::swap in swap_impl, which is OK, since you arrived here through the cppu::swap implementation already. But then you do not use overloads for other types. Thus, I think you need to distinguish three cases:
Has own swap member function
Has no swap member function and is from namespace cppu
Has no swap member function and is any other namespace (here you need to use the ADL swap idiom).
Also, I concur with #Yakk that I would be more direct instead of using the int/char hack.
So let's go for it:
A helper for checking the availability of the swap member:
namespace cppu
{
namespace detail
{
template <typename T>
using void_t = void;
template <typename T, typename = void>
struct has_member_swap
{
static constexpr bool value = false;
};
template <typename T>
struct has_member_swap<
T,
void_t<decltype(std::declval<T&>().swap(std::declval<T&>()))>>
{
static constexpr bool value = true;
};
}
}
And a helper to check if T is from namespace cppu, see also here:
namespace helper
{
template <typename T, typename = void>
struct is_member_of_cppu : std::false_type
{
};
template <typename T>
struct is_member_of_cppu<
T,
decltype(adl_is_member_of_cppu(std::declval<T>()))> : std::true_type
{
};
}
namespace cppu
{
template <typename T>
auto adl_is_member_of_cppu(T && ) -> void;
}
Now we can write all three overloads:
namespace cppu
{
namespace detail
{
template <
typename T,
typename = std::enable_if_t<helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>>
auto swap(T& x, T& y)
-> std::enable_if_t<helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>
{
std::cout << "cppu-type without member swap";
std::swap(x, y);
}
template <
typename T,
typename = std::enable_if_t<not helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>>
auto swap(T& x, T& y)
-> std::enable_if_t<not helper::is_member_of_cppu<T>::value and
not has_member_swap<T>::value>
{
std::cout << "not cppu-type without member swap";
using std::swap;
swap(x, y);
}
template <typename T, typename = std::enable_if_t<has_member_swap<T>::value>>
auto swap(T& x, T& y) -> decltype(x.swap(y))
{
std::cout << "member swap";
return x.swap(y);
}
}
}
Call this as you did before:
namespace cppu
{
template <typename T>
void swap(T& x, T& y)
{
detail::swap(x, y);
}
}
And finally: Test the whole thing.
namespace cppu
{
struct X{};
struct Y{ void swap(Y& y) { }; };
}
struct A{};
struct B{ void swap(B& y) { }; };
struct C{};
auto swap(C&, C&) -> void { std::cout << " with own overload"; }
static_assert(helper::is_member_of_cppu<cppu::X>::value, "");
static_assert(helper::is_member_of_cppu<cppu::Y>::value, "");
static_assert(not helper::is_member_of_cppu<A>::value, "");
static_assert(not helper::is_member_of_cppu<B>::value, "");
int main()
{
auto x1 = cppu::X{};
auto x2 = cppu::X{};
std::cout << "X: "; swap(x1, x2); std::cout << std::endl;
auto y1 = cppu::Y{};
auto y2 = cppu::Y{};
std::cout << "Y: "; swap(y1, y2); std::cout << std::endl;
auto a1 = A{};
auto a2 = A{};
std::cout << "A: "; cppu::swap(a1, a2); std::cout << std::endl;
auto b1 = B{};
auto b2 = B{};
std::cout << "B: "; cppu::swap(b1, b2); std::cout << std::endl;
auto c1 = C{};
auto c2 = C{};
std::cout << "C: "; cppu::swap(c1, c2); std::cout << std::endl;
}
The output is as expected (IMHO):
X: cppu-type without member swap
Y: member swap
A: not cppu-type without member swap
B: member swap
C: not cppu-type without member swap with own overload
Let me please consider the following synthetic example:
inline int fun2(int x) {
return x;
}
inline int fun2(double x) {
return 0;
}
inline int fun2(float x) {
return -1;
}
int fun(const std::tuple<int,double,float>& t, std::size_t i) {
switch(i) {
case 0: return fun2(std::get<0>(t));
case 1: return fun2(std::get<1>(t));
case 2: return fun2(std::get<2>(t));
}
}
The question is how should I expand this to the general case
template<class... Args> int fun(const std::tuple<Args...>& t, std::size_t i) {
// ?
}
Guaranteeing that
fun2 can be inlined into fun
search complexity not worse than O(log(i)) (for large i).
It is known that optimizer usually uses lookup jump table or compile-time binary search tree when large enough switch expanded. So, I would like to keep this property affecting performance for large number of items.
Update #3: I remeasured performance with uniform random index value:
1 10 20 100
#TartanLlama
gcc ~0 42.9235 44.7900 46.5233
clang 10.2046 38.7656 40.4316 41.7557
#chris-beck
gcc ~0 37.564 51.3653 81.552
clang ~0 38.0361 51.6968 83.7704
naive tail recursion
gcc 3.0798 40.6061 48.6744 118.171
clang 11.5907 40.6197 42.8172 137.066
manual switch statement
gcc 41.7236
clang 7.3768
Update #2: It seems that clang is able to inline functions in #TartanLlama solution whereas gcc always generates function call.
Ok, I rewrote my answer. This gives a different approach to what TartanLlama and also what I suggested before. This meets your complexity requirement and doesn't use function pointers so everything is inlineable.
Edit: Much thanks to Yakk for pointing out a quite significant optimization (for the compile-time template recursion depth required) in comments
Basically I make a binary tree of the types / function handlers using templates, and implement the binary search manually.
It might be possible to do this more cleanly using either mpl or boost::fusion, but this implementation is self-contained anyways.
It definitely meets your requirements, that the functions are inlineable and runtime look up is O(log n) in the number of types in the tuple.
Here's the complete listing:
#include <cassert>
#include <cstdint>
#include <tuple>
#include <iostream>
using std::size_t;
// Basic typelist object
template<typename... TL>
struct TypeList{
static const int size = sizeof...(TL);
};
// Metafunction Concat: Concatenate two typelists
template<typename L, typename R>
struct Concat;
template<typename... TL, typename... TR>
struct Concat <TypeList<TL...>, TypeList<TR...>> {
typedef TypeList<TL..., TR...> type;
};
template<typename L, typename R>
using Concat_t = typename Concat<L,R>::type;
// Metafunction First: Get first type from a typelist
template<typename T>
struct First;
template<typename T, typename... TL>
struct First <TypeList<T, TL...>> {
typedef T type;
};
template<typename T>
using First_t = typename First<T>::type;
// Metafunction Split: Split a typelist at a particular index
template<int i, typename TL>
struct Split;
template<int k, typename... TL>
struct Split<k, TypeList<TL...>> {
private:
typedef Split<k/2, TypeList<TL...>> FirstSplit;
typedef Split<k-k/2, typename FirstSplit::R> SecondSplit;
public:
typedef Concat_t<typename FirstSplit::L, typename SecondSplit::L> L;
typedef typename SecondSplit::R R;
};
template<typename T, typename... TL>
struct Split<0, TypeList<T, TL...>> {
typedef TypeList<> L;
typedef TypeList<T, TL...> R;
};
template<typename T, typename... TL>
struct Split<1, TypeList<T, TL...>> {
typedef TypeList<T> L;
typedef TypeList<TL...> R;
};
template<int k>
struct Split<k, TypeList<>> {
typedef TypeList<> L;
typedef TypeList<> R;
};
// Metafunction Subdivide: Split a typelist into two roughly equal typelists
template<typename TL>
struct Subdivide : Split<TL::size / 2, TL> {};
// Metafunction MakeTree: Make a tree from a typelist
template<typename T>
struct MakeTree;
/*
template<>
struct MakeTree<TypeList<>> {
typedef TypeList<> L;
typedef TypeList<> R;
static const int size = 0;
};*/
template<typename T>
struct MakeTree<TypeList<T>> {
typedef TypeList<> L;
typedef TypeList<T> R;
static const int size = R::size;
};
template<typename T1, typename T2, typename... TL>
struct MakeTree<TypeList<T1, T2, TL...>> {
private:
typedef TypeList<T1, T2, TL...> MyList;
typedef Subdivide<MyList> MySubdivide;
public:
typedef MakeTree<typename MySubdivide::L> L;
typedef MakeTree<typename MySubdivide::R> R;
static const int size = L::size + R::size;
};
// Typehandler: What our lists will be made of
template<typename T>
struct type_handler_helper {
typedef int result_type;
typedef T input_type;
typedef result_type (*func_ptr_type)(const input_type &);
};
template<typename T, typename type_handler_helper<T>::func_ptr_type me>
struct type_handler {
typedef type_handler_helper<T> base;
typedef typename base::func_ptr_type func_ptr_type;
typedef typename base::result_type result_type;
typedef typename base::input_type input_type;
static constexpr func_ptr_type my_func = me;
static result_type apply(const input_type & t) {
return me(t);
}
};
// Binary search implementation
template <typename T, bool b = (T::L::size != 0)>
struct apply_helper;
template <typename T>
struct apply_helper<T, false> {
template<typename V>
static int apply(const V & v, size_t index) {
assert(index == 0);
return First_t<typename T::R>::apply(v);
}
};
template <typename T>
struct apply_helper<T, true> {
template<typename V>
static int apply(const V & v, size_t index) {
if( index >= T::L::size ) {
return apply_helper<typename T::R>::apply(v, index - T::L::size);
} else {
return apply_helper<typename T::L>::apply(v, index);
}
}
};
// Original functions
inline int fun2(int x) {
return x;
}
inline int fun2(double x) {
return 0;
}
inline int fun2(float x) {
return -1;
}
// Adapted functions
typedef std::tuple<int, double, float> tup;
inline int g0(const tup & t) { return fun2(std::get<0>(t)); }
inline int g1(const tup & t) { return fun2(std::get<1>(t)); }
inline int g2(const tup & t) { return fun2(std::get<2>(t)); }
// Registry
typedef TypeList<
type_handler<tup, &g0>,
type_handler<tup, &g1>,
type_handler<tup, &g2>
> registry;
typedef MakeTree<registry> jump_table;
int apply(const tup & t, size_t index) {
return apply_helper<jump_table>::apply(t, index);
}
// Demo
int main() {
{
tup t{5, 1.5, 15.5f};
std::cout << apply(t, 0) << std::endl;
std::cout << apply(t, 1) << std::endl;
std::cout << apply(t, 2) << std::endl;
}
{
tup t{10, 1.5, 15.5f};
std::cout << apply(t, 0) << std::endl;
std::cout << apply(t, 1) << std::endl;
std::cout << apply(t, 2) << std::endl;
}
{
tup t{15, 1.5, 15.5f};
std::cout << apply(t, 0) << std::endl;
std::cout << apply(t, 1) << std::endl;
std::cout << apply(t, 2) << std::endl;
}
{
tup t{20, 1.5, 15.5f};
std::cout << apply(t, 0) << std::endl;
std::cout << apply(t, 1) << std::endl;
std::cout << apply(t, 2) << std::endl;
}
}
Live on Coliru:
http://coliru.stacked-crooked.com/a/3cfbd4d9ebd3bb3a
If you make fun2 into a class with overloaded operator():
struct fun2 {
inline int operator()(int x) {
return x;
}
inline int operator()(double x) {
return 0;
}
inline int operator()(float x) {
return -1;
}
};
then we can modify dyp's answer from here to work for us.
Note that this would look a lot neater in C++14, as we could have all the return types deduced and use std::index_sequence.
//call the function with the tuple element at the given index
template<class Ret, int N, class T, class Func>
auto apply_one(T&& p, Func func) -> Ret
{
return func( std::get<N>(std::forward<T>(p)) );
}
//call with runtime index
template<class Ret, class T, class Func, int... Is>
auto apply(T&& p, int index, Func func, seq<Is...>) -> Ret
{
using FT = Ret(T&&, Func);
//build up a constexpr array of function pointers to index
static constexpr FT* arr[] = { &apply_one<Ret, Is, T&&, Func>... };
//call the function pointer at the specified index
return arr[index](std::forward<T>(p), func);
}
//tag dispatcher
template<class Ret, class T, class Func>
auto apply(T&& p, int index, Func func) -> Ret
{
return apply<Ret>(std::forward<T>(p), index, func,
gen_seq<std::tuple_size<typename std::decay<T>::type>::value>{});
}
We then call apply and pass the return type as a template argument (you could deduce this using decltype or C++14):
auto t = std::make_tuple(1,1.0,1.0f);
std::cout << apply<int>(t, 0, fun2{}) << std::endl;
std::cout << apply<int>(t, 1, fun2{}) << std::endl;
std::cout << apply<int>(t, 2, fun2{}) << std::endl;
Live Demo
I'm not sure if this will completely fulfil your requirements due to the use of function pointers, but compilers can optimize this kind of thing pretty aggressively. The searching will be O(1) as the pointer array is just built once then indexed directly, which is pretty good. I'd try this out, measure, and see if it'll work for you.
I'm trying to create a function which can be called with a lambda that takes either 0, 1 or 2 arguments. Since I need the code to work on both g++ 4.5 and vs2010(which doesn't support variadic templates or lambda conversions to function pointers) the only idea I've come up with is to choose which implementation to call based on arity. The below is my non working guess at how this should look. Is there any way to fix my code or is there a better way to do this in general?
#include <iostream>
#include <functional>
using namespace std;
template <class Func> struct arity;
template <class Func>
struct arity<Func()>{ static const int val = 0; };
template <class Func, class Arg1>
struct arity<Func(Arg1)>{ static const int val = 1; };
template <class Func, class Arg1, class Arg2>
struct arity<Func(Arg1,Arg2)>{ static const int val = 2; };
template<class F>
void bar(F f)
{
cout << arity<F>::val << endl;
}
int main()
{
bar([]{cout << "test" << endl;});
}
A lambda function is a class type with a single function call operator. You can thus detect the arity of that function call operator by taking its address and using overload resolution to select which function to call:
#include <iostream>
template<typename F,typename R>
void do_stuff(F& f,R (F::*mf)() const)
{
(f.*mf)();
}
template<typename F,typename R,typename A1>
void do_stuff(F& f,R (F::*mf)(A1) const)
{
(f.*mf)(99);
}
template<typename F,typename R,typename A1,typename A2>
void do_stuff(F& f,R (F::*mf)(A1,A2) const)
{
(f.*mf)(42,123);
}
template<typename F>
void do_stuff(F f)
{
do_stuff(f,&F::operator());
}
int main()
{
do_stuff([]{std::cout<<"no args"<<std::endl;});
do_stuff([](int a1){std::cout<<"1 args="<<a1<<std::endl;});
do_stuff([](int a1,int a2){std::cout<<"2 args="<<a1<<","<<a2<<std::endl;});
}
Be careful though: this won't work with function types, or class types that have more than one function call operator, or non-const function call operators.
I thought the following would work but it doesn't, I'm posting it for two reasons.
To save people the time if they had the same idea
If someone knows why this doesn't work, I'm not 100% sure I understand (although I have my suspicions)
Code follows:
#include <iostream>
#include <functional>
template <typename Ret>
unsigned arity(std::function<Ret()>) { return 0; }
template <typename Ret, typename A1>
unsigned arity(std::function<Ret(A1)>) { return 1; }
template <typename Ret, typename A1, typename A2>
unsigned arity(std::function<Ret(A1, A2)>) { return 2; }
// rinse and repeat
int main()
{
std::function<void(int)> f = [](int i) { }; // this binds fine
// Error: no matching function for call to 'arity(main()::<lambda(int)>)'
std::cout << arity([](int i) { });
}
Compile time means of obtaining the arity of a function or a function object, including that of a lambda:
int main (int argc, char ** argv) {
auto f0 = []() {};
auto f1 = [](int) {};
auto f2 = [](int, void *) {};
std::cout << Arity<decltype(f0)>::value << std::endl; // 0
std::cout << Arity<decltype(f1)>::value << std::endl; // 1
std::cout << Arity<decltype(f2)>::value << std::endl; // 2
std::cout << Arity<decltype(main)>::value << std::endl; // 2
}
template <typename Func>
class Arity {
private:
struct Any {
template <typename T>
operator T ();
};
template <typename T>
struct Id {
typedef T type;
};
template <size_t N>
struct Size {
enum { value = N };
};
template <typename F>
static Size<0> match (
F f,
decltype(f()) * = nullptr);
template <typename F>
static Size<1> match (
F f,
decltype(f(Any())) * = nullptr,
decltype(f(Any())) * = nullptr);
template <typename F>
static Size<2> match (
F f,
decltype(f(Any(), Any())) * = nullptr,
decltype(f(Any(), Any())) * = nullptr,
decltype(f(Any(), Any())) * = nullptr);
public:
enum { value = Id<decltype(match(static_cast<Func>(Any())))>::type::value };
};
This way works:
template<typename F>
auto call(F f) -> decltype(f(1))
{
return f(1);
}
template<typename F>
auto call(F f, void * fake = 0) -> decltype(f(2,3))
{
return f(2,3);
}
template<typename F>
auto call(F f, void * fake = 0, void * fake2 = 0) -> decltype(f(4,5,6))
{
return f(4,5,6);
}
int main()
{
auto x1 = call([](int a){ return a*10; });
auto x2 = call([](int a, int b){ return a*b; });
auto x3 = call([](int a, int b, int c){ return a*b*c; });
// x1 == 1*10
// x2 == 2*3
// x3 == 4*5*6
}
It works for all callable types (lambdas, functors, etc)