Type Class – Mappable

Previously, we have introduced the concept of type classes with the monoid. The next type class to look at is the mappable type class, representing “things” that can be mapped. This class is quite similar to the Functor type class in Haskell.

An instance of mappable is something that can be queried for values, such as std::vector, std::shared_ptr or std::function. The function map basically lets you transform the values of a query. It is essentially a generalized functional style std::transform.

Mapping becomes an even more powerful as our repertoire of type classes expands, especially with folding and filtering, but let’s not get ahead of ourselves.

The mappable type class:

template<class T>
struct mappable
{
    // Type value_type
    // mappable<R> map(R(A),mappable<A>)
    static constexpr bool is_instance = false;
};

The type class lets you determine the result of a query to a mappable using value_type. It also contains the map function, which is specialized by each instance.

Mapping a function (or function-like object) will return a new function with its return value modified:

template<class Result,class... Params>
struct mappable<Result(Params...)>
{
    // Type value_type
    using value_type = Result;

    // mappable<R> map(R(A),mappable<A>)
    template<class F,class A>
    static auto map(F&& f, const A& in)
    {
        return [=](Params... params)
        {
            return eval(f,eval(in,std::forward<Params>(params)...));
        };
    }

    static constexpr bool is_instance = true;
};

// const member function
template<class Result, class Class, class... Params>
struct mappable<Result(Class::*)(Params...) const> :
    public mappable<Result(const Class&,Params...)>
{};

// member function
template<class Result, class Class, class... Params>
struct mappable<Result(Class::*)(Params...)> :
    public mappable<Result(Class&,Params...)>
{};

// member object
template<class Result, class Class>
struct mappable<Result(Class::*)> :
    public mappable<Result(const Class&)>
{};

// free function
template<class Result, class... Params>
struct mappable<Result(*)(Params...)> :
    public mappable<Result(Params...)>
{};

Notice that the Result type doesn’t matter for the purposes of map, only the parameters (Params...) do. For example:

template<int X>
int increment_by(int n){return n+X;}

// still returns an integer
auto func = mappable<char(const std::string&)>::map(&increment_by<1>,&std::string::size);

We have to specify const std::string& because we can’t turn Params... into universal references, though that would be a nice improvement.

Generally, it’s the “shape” of the mappable that determines what can be mapped. For functions, the parameters types determine the shape. For pointers, it might be the particular type of pointer. For containers, it would be the type of container used.

To reflect this, we will make use of container_traits to write the container instances:

template<class T>
struct default_container_mappable
{
    // Type value_type
    using value_type = typename container_traits<T>::value_type;

    // mappable<R> map(R(A),mappable<A>)
    template<class F,class A>
    static auto map(F&& f, const A& in)
        -> typename container_traits<T>::template rebind<
            decltype(eval(f,std::declval<typename container_traits<A>::value_type>()))>
    {
        using B = decltype(eval(f,std::declval<typename container_traits<A>::value_type>()));
        using T_B = typename container_traits<T>::template rebind<B>;

        T_B out;

        for (auto& a : in)
            container_traits<T_B>::add_element(out,eval(std::forward<F>(f),a));

        return out;
    }

    static constexpr bool is_instance = true;
};

#define FC_DEFAULT_CONTAINER_MAPPABLE(T)\
    template<class... Args>\
    struct mappable<T<Args...>> : public default_container_mappable<T<Args...>>\
    {};

FC_DEFAULT_CONTAINER_MAPPABLE(std::deque);
FC_DEFAULT_CONTAINER_MAPPABLE(std::list);
FC_DEFAULT_CONTAINER_MAPPABLE(std::multiset);
FC_DEFAULT_CONTAINER_MAPPABLE(std::set);
FC_DEFAULT_CONTAINER_MAPPABLE(std::basic_string);
FC_DEFAULT_CONTAINER_MAPPABLE(std::unordered_multiset);
FC_DEFAULT_CONTAINER_MAPPABLE(std::unordered_set);
FC_DEFAULT_CONTAINER_MAPPABLE(std::vector);

As with functions, the type of container passed in doesn’t matter. The actual type returned by map has the same “shape” as specified in the specific instance of mappable, but rebound to a new value_type.

If you want to just keep the same “shape”, a convenience function can be used which automatically determines the output:

template<class F, class T>
auto map(F&& f, const T& in)
{
    return mappable<T>::map(std::forward<F>(f),in);
}

To demonstrate how mappable can be used:

int main()
{
    // function
    std::string words = "hello, world!";
    auto string_length_plus_one = 
        mappable<char(const std::string&)>::map(&increment_by<1>,&std::string::size);
    std::cout << "size + 1:         " << string_length_plus_one(words) << "\n";

    // container with convenience function
    auto shout = [](char c){return std::toupper(c,std::locale::classic());};
    std::cout << "shout:            " << map(shout,words) << "\n";

    // container to different container
    std::cout << "characters used:  ";
    for (char c : mappable<std::set<char>>::map([](char a){return a;},words))
        std::cout << "(" << c << ") ";
    std::cout << "\n";

    return 0;
}

Which gives the output:

size + 1:         14
shout:            HELLO, WORLD!
characters used:  ( ) (!) (,) (d) (e) (h) (l) (o) (w)

Working code available here, with a few more examples too!

Note: It goes without saying that you shouldn’t combine this with using namespace std because of std::map.

Advertisements

Tuple Forwarding

C++11 provides the very useful std::forward_as_tuple function. Unfortunately, there isn’t any standard way to unpack the tuple to the parameters of a function.

To do this, template recursion and forwarding can be used. Recursing over a tuple allows for the extraction of the arguments, at the end eval can evaluate the result:

template<size_t index>
struct tuple_eval_helper
{
    template<class Func,class TArgs,class... Args>
    static auto evaluate(Func&& f, TArgs&& t_args, Args&&... args)
        -> decltype(tuple_eval_helper<index-1>::evaluate(
            std::forward<Func>(f),
            std::forward<TArgs>(t_args),
            std::get<index>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            ))
    {
        return tuple_eval_helper<index-1>::evaluate(
            std::forward<Func>(f),
            std::forward<TArgs>(t_args),
            std::get<index>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            );
    }
};

template<>
struct tuple_eval_helper<0>
{
    template<class Func,class TArgs,class... Args>
    static auto evaluate(Func&& f, TArgs&& t_args, Args&&... args)
        -> decltype(eval(
            std::forward<Func>(f),
            std::get<0>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            ))
    {
        return eval(
            std::forward<Func>(f),
            std::get<0>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            );
    }
};

template<class Func,class TArgs>
auto tuple_eval(Func&& f, TArgs&& t_args)
    -> decltype(tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TArgs>::type>::value-1>::evaluate(
        std::forward<Func>(f),
        std::forward<TArgs>(t_args)
        ))
{
    return tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TArgs>::type>::value-1>::evaluate(
        std::forward<Func>(f),
        std::forward<TArgs>(t_args)
        );
}

Which you can use like this:

tuple_eval(std::plus<int>(),std::forward_as_tuple(1,2));

Now, suppose you have a tuple of functions that you want to call with the parameters? In similar fashion, recursively extract the functions, evaluate them and forward the result to the end where they are repackaged into another tuple:

template<size_t index>
struct multi_tuple_eval_helper
{
    template<class TFuncs,class TArgs,class... Results>
    static auto evaluate(TFuncs&& t_funcs, TArgs&& t_args, Results&&... results)
        -> decltype(multi_tuple_eval_helper<index-1>::evaluate(
            std::forward<TFuncs>(t_funcs),
            std::forward<TArgs>(t_args),
            tuple_eval(std::get<index>(t_funcs),t_args),
            std::forward<Results>(results)...
            ))
    {
        return multi_tuple_eval_helper<index-1>::evaluate(
            std::forward<TFuncs>(t_funcs),
            std::forward<TArgs>(t_args),
            tuple_eval(std::get<index>(t_funcs),t_args),
            std::forward<Results>(results)...
            );
    }
};

template<>
struct multi_tuple_eval_helper<0>
{
    template<class TFuncs,class TArgs,class... Results>
    static auto evaluate(TFuncs&& t_funcs, TArgs&& t_args, Results&&... results)
        -> decltype(std::tuple<decltype(tuple_eval(std::get<0>(t_funcs),t_args)),Results...>(
            tuple_eval(std::get<0>(t_funcs),t_args),
            std::forward<Results>(results)...
            ))
    {
        return std::tuple<decltype(tuple_eval(std::get<0>(t_funcs),t_args)),Results...>(
            tuple_eval(std::get<0>(t_funcs),t_args),
            std::forward<Results>(results)...
            );
    }
};

template<class TFuncs,class TArgs>
auto multi_tuple_eval(TFuncs&& t_funcs, TArgs&& t_args)
    -> decltype(multi_tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TFuncs>::type>::value-1>::evaluate(
        std::forward<TFuncs>(t_funcs),
        std::forward<TArgs>(t_args)
        ))
{
    return multi_tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TFuncs>::type>::value-1>::evaluate(
        std::forward<TFuncs>(t_funcs),
        std::forward<TArgs>(t_args)
        );
}

Which can be used like this:

multi_tuple_eval(
    std::forward_as_tuple(std::plus<int>(),std::multiplies<int>()),
    std::forward_as_tuple(3,4)
    );

The result is the tuple (7,12).

Esoteric and abusive to the compiler, that’s what we’re about here at Functional C++!

Note: Be careful with move semantics and invalidating stuff when using this.

Edit: Changed multi_tuple_eval to use the tuple constructor. It will now correctly return references.

Container Traits

The containers defined in the standard template library all provide very similar functionalities, however they are difficult to use in a generic way because of the way they are templated.

In the STL, there are a few categories of containers which behave similarly and have similar templates:

  • Sequence containers take 2 arguments, the value type and an allocator. Eg. std::vector
  • Associative containers take 3 arguments, the value type, a comparison function and an allocator. Eg. std::multiset
  • Hash containers take 4 arguments, the value type, a hash, an equality test and an allocator. Eg. std::unordered_set
  • Strings take 3 arguments, the character type, character traits and an allocator. Eg. std::wstring

Hash containers are properly called unordered associative containers, but that’s a mouth full so I will just refer to them as hash containers. To simplify the discussion, forward lists and maps will be ignored.

When dealing with containers generically, the most common things we will want to do are add an element, concatenate two containers and rebind the value type. The first two seem like no brainers, but why rebind? Suppose you are doing something like this:

template<class F, class C>
auto my_transform(F&& f, const C& c) -> OutputType // What is this?
{
    OutputType out;
    for (const auto& a : c)
        out.insert(eval(f,a));
        
    return out;
}

That code conceptually works for the associative and hash containers, which use the insert to add elements. However, in order to determine OutputType, we would have to dig into their template parameters which would result in code which is complex at best and unmaintainable at worst.

Enter container traits:

template<class C>
struct container_traits
{
    // Type value_type
    // void add_element(C&,T)
    // void concat(C&,C)
    // Type rebind<U>
};

Here are the definitions for associative containers:

template<class C>
struct associative_container_traits;

// C: container, T: value type, O: compare, A: allocator
template<template<class,class,class> class C, class T, template<class> class O, class A>
struct associative_container_traits<C<T,O<T>,A>>
{
    using value_type = T;

    static void add_element(C<T,O<T>,A>& c, const T& t) {c.insert(t);}
    static void concat(C<T,O<T>,A>& lhs, const C<T,O<T>,A>& rhs)
    {
        lhs.insert(rhs.begin(),rhs.end());
    }

    template<class U>
    using rebind = C<U,O<U>,typename A::template rebind<U>::other>;
};

template<class... Args>
struct container_traits<std::set<Args...> :
    public associative_container_traits<std::set<Args...>>
{};

template<class... Args>
struct container_traits<std::multiset<Args...> :
    public associative_container_traits<std::multiset<Args...>>
{};

Similar code is written for the sequence, hash and string containers, but I will spare you the bore.

Care must be taken to ensure that the template parameters are properly matched in rebind. For example, the compare parameter simply substitutes its template parameter. Ideally, comparators would have their own rebind the way allocators do, however, the current code works with the default std::less so it serves our purposes for now.

With container traits in hand, we can now write the original function, and a few others, in a very generic way:

// Transform elements in a container
template<class F, class C>
auto my_transform(F&& f, const C& c)
{
    using OutputType = typename container_traits<C>::template rebind<
        decltype(eval(f,std::declval<typename container_traits<C>::value_type>()))>;

    OutputType out;
    for (const auto& a : c)
        container_traits<OutputType>::add_element(out, eval(f,a));

    return out;
}

// flattens a container of containers
template<class C>
auto flatten(const C& c)
{
    using OutputType = typename container_traits<C>::template rebind<
        typename container_traits<typename container_traits<C>::value_type>::value_type>;

    OutputType out;
    for (const auto& inner : c)
        for (const auto& a : inner)
            container_traits<OutputType>::add_element(out, a);

    return out;
}

// concatenates containers within a container
template<class C>
auto chain(const C& c)
{
    using OutputType = typename container_traits<C>::value_type;

    OutputType out;
    for (const auto& a : c)
        container_traits<OutputType>::concat(out, a);

    return out;
}

I omit the trailing return types because they are very long and unnecessary with c++14 return type deduction.

The functions can be used like this:

int main()
{
    // words in reverse lexographic order
    std::multiset<std::string,std::greater<std::string>> words{
        "my","hovercraft","is","full","of","eels"};

    std::cout << "word lengths: ";
    for (std::size_t length : my_transform(&std::string::size,words))
        std::cout << length << " ";
    std::cout << "\n";

    std::cout << "number of \'f\'s: " << flatten(words).count('f') << "\n";

    auto shout = [](char c){return std::toupper(c,std::locale::classic());};
    std::cout << "yelling: " << my_transform(shout,chain(words)) << "\n";

    return 0;
}

Output:

word lengths: 10 4 4 2 2 2
number of 'f's: 3
yelling: OFMYISHOVERCRAFTFULLEELS

You can download the complete code for this post here here.

Type Classes

Type classes are a feature of Haskell which is very similar to the upcoming Concepts. Both define interfaces to a data type, which can be defined separately from the data, in contrast to member functions.

To introduce the notion of type classes, let’s start with a simple one: the monoid. A monoid is an object which has a binary operation and an identity for that operation. For our purposes, we will call the identity empty() and the operation append.

The basic template for the type class follows:

template<class T>
struct monoid
{
    // T empty()
    // T append(T,T)
    static constexpr bool is_instance = false;
};

Most things are not monoids and so we also add a boolean which marks this. This allows us to do type checking using std::enable_if or static_assert.

When an object has the type class interface, we say that it is an instance of the type class. For example, the monoid instance for integers can be written as:

template<>
struct monoid<int>
{
    static int empty(){return 0;}
    static int append(int lhs, int rhs){return lhs+rhs;}
    static constexpr bool is_instance = true;
};

Now if we wanted to write a function using monoids, we could do so easily and with guarantees of type safety. For example, here is a function which “accumulates” monoid values from a vector:

template< 
    class T,
    class = typename std::enable_if<monoid<T>::is_instance>::type
    >
T accumulator(const std::vector<T>& in)
{
    T out{monoid<T>::empty()};

    for (const T& t : in)
        out = monoid<T>::append(out,t);

    return out;
}

int main()
{
    std::cout << accumulator(std::vector<int>{1,2,3}) << "\n";

    return 0;
}

Output:

6

Obviously, there are quite a few data types which display monoid behaviour, including all the fundamental data types. There’s quite a bit of repetition which can be removed, also reducing the chance for error.

Many of the monoids, including all the fundamental types implement operator+ and are value-initialized to a reasonable value. A default monoid can be written to represent this.

template<class T>
struct default_monoid
{
    static T empty(){return T{};}
    static T append(const T& lhs, const T& rhs){return lhs+rhs;}
    static constexpr bool is_instance = true;
};

// example use
template<>
struct monoid<int> : public default_monoid<int>
{};

template<>
struct monoid<char> : public default_monoid<char>
{};

We can further reduce the repetition through the use of x macros:

#define FUNDAMENTAL_TYPES\
    X(bool)\
    X(signed char)\
    X(unsigned char)\
    X(char)\
    X(wchar_t)\
    X(char16_t)\
    X(char32_t)\
    X(short)\
    X(unsigned short)\
    X(int)\
    X(unsigned)\
    X(long)\
    X(unsigned long)\
    X(long long)\
    X(unsigned long long)

#define X(a)\
    template<>\
    struct monoid<a> : public default_monoid<a>\
    {};

FUNDAMENTAL_TYPES;
#undef X

This code will generate monoid instances based on default_monoid for all the fundamental data types.

We can also use default_monoid for strings:

template<>
struct monoid<std::string> : public default_monoid<std::string>
{};

But for some types we need to customize a bit more:

template<class T>
struct monoid<std::vector<T>>
{
    static std::vector<T> empty(){return std::vector<T>{};}

    static std::vector<T> append(std::vector<T> lhs, const std::vector<T>& rhs)
    {
        for (const T& t : rhs)
            lhs.push_back(t);

        return lhs;
    }

    static constexpr bool is_instance = true;
};

I’m not going to go into every single type but, suffice it to say that containers, string streams, numerical types and many more can all instantiate the monoid type class.

The combination of generic programming and functional programming is very powerful. The accumulator function from earlier can now work with every type that is an instance of monoid! Pretty neat.

int main()
{
    int a = accumulator(std::vector<int>{1,2,3});
    std::cout << a << "\n";

    int b = accumulator(accumulator(std::vector<std::vector<int>>{{1,2,3},{4,5,6},{7,8,9}}));
    std::cout << b << "\n";

    std::string c = accumulator(std::vector<std::string>{"hello ","world","!"});
    std::cout << c << "\n";

    return 0;
}

Output:

6
45
hello world!

Function Composition

The usefulness of higher order functions is hard to dispute, you can safely and easily write functions which combine the functionality of other functions. The most basic of higher order functions is function composition, the topic of this post.

There is no standard way to compose functions, especially since there is no standard way to resolve a function’s argument and return types. Good thing that we have function traits! I will also be making use of the generalized function evaluation discussed previously.

To keep the discussion simple, I will only discuss unary functions, though conceivably one could make some clever use of std::forward_as_tuple and make a very complete and complex implementation.

Let’s start with the basics:

#include <functional>

#include "eval.h"
#include "function_traits.h"

template<class F, class G>
auto compose(F&& f, G&& g)
    -> std::function<typename function_traits<F>::return_type(
        typename function_traits<G>::template argument<0>::type)>
{
    using F_traits = typename function_traits<F>;
    using G_traits = typename function_traits<G>;

    static_assert(F_traits::arity == 1, "error: only unary functions can be composed.");
    static_assert(G_traits::arity == 1, "error: only unary functions can be composed.");

    using Arg = typename G_traits::template argument<0>::type;

    return [=](Arg arg){return eval(f,eval(g,arg));};
}

Given two functions, f(x) and g(y), compose will give us h(y) = compose(f,g) = f(g(y)). Note the use of static asserts to enforce the use of unary functions as well as to determine the argument type for the returned function.

The trailing return type uses the function_traits to cast the lambda to a valid function object. In c++14, the return type can be omitted entirely, and frankly it’s a huge improvement. Just look at that thing.

An example of this in action:

#include <iostream>

int main()
{
    auto double_then_add_three = compose(
        [](int n){return n+3;},
        [](int n){return n*2;}
    );

    for (unsigned i = 0; i < 10; ++i)
        std::cout << double_then_add_three(i) << " ";

    return 0;
}

Which outputs as expected:

3 5 7 9 11 13 15 17 19 21

Let’s try making an operator version of this. I will be choosing operator* as my composition operator.

template<class F, class G>
auto operator*(F&& f, G&& g)
{
    return compose(std::forward<F>(f),std::forward<G>(g));
}

This works quite well, until we try and combine function pointers.

#include <iostream>

int add_one(int n){return n+1;}
int times_two(int n){return n*2;}

int main()
{
    // fine
    auto double_then_add_three = [](int n){return n+3;} * [](int n){return n*2;};

    // error!
    auto double_then_add_one = &add_one * &times_two;

    return 0;
}

This is because the compiler is trying to multiply the two pointers and that makes absolutely no sense. How can we get around this? We can write our own custom operators. (I feel really bad about this! Don’t hate me!)

First we have to wrap our pointer in another object, we also add a test to see if a type is a wrapper:

template<class T>
struct wrapper
{
    wrapper(T t):value(t){};

    T value;
};

template<>
struct wrapper<void>
{};

template<class T>
struct is_wrapper
{
    enum{ value = false };
};

template<class T>
struct is_wrapper<wrapper<T>>
{
    enum{ value = true };
};

Now for the new composition operator, very similar but with some minor changes:

template<
    class F, class G,
    class = typename std::enable_if<!is_wrapper<F>::value>::type
    >
auto operator*(F&& f, G&& g)
{
    return compose(std::forward<F>(f),std::forward<G>(g));
}

template<class T>
auto operator*(T&& t, wrapper<void> v)
{
    return wrapper<T>(std::forward<T>(t));
}

template<class T, class F>
auto operator*(wrapper<T>&& t, F&& f)
{
    return compose(t.value,f);
}

#define COMPOSE * wrapper<void>() *

We can now use the macro COMPOSE as an operator which behaves exactly like compose. The macro causes the left operand to be placed in a wrapper, which then can be passed to the compose operator.

This workaround also works for objects which have their own operator* but it’s not pretty and will definitely raise some flags if you use this in production.

The end result is quite nice though, and you can see this for yourself:

#include <iostream>

int increment(int n){return n+1;}
int times_two(int n){return n*2;}

int main()
{
    auto double_then_add_three = [](int n){return n+3;} * [](int n){return n*2;};
    std::cout << double_then_add_three(20) << " ";

    auto echo = [](const std::string& s){return s+" "+s;};
    auto echo_length = &std::string::size * echo;
    std::cout << echo_length("hello") << " ";

    auto some_func = &increment COMPOSE &times_two COMPOSE &increment COMPOSE &increment;
    std::cout << some_func(-4);

    std::cout << "\n";
}

Which gives the output:

43 11 -3

Function Traits

C++11 has added the incredibly useful type_traits library. However, beyond std::is_function there isn’t really much going on for functions. Rolling your own function traits is not very difficult, and this post will show you how!

A good place to start is free functions. Using templates, it’s easy to unpack the return type, arity and argument types for a function:

template<class F>
struct function_traits;

// function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};

template<class R, class... Args>
struct function_traits<R(Args...)>
{
    using return_type = R;

    static constexpr std::size_t arity = sizeof...(Args);

    template <std::size_t N>
    struct argument
    {
        static_assert(N < arity, "error: invalid parameter index.");
        using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
    };
};

Which can be used like this:

float free_function(const std::string& a, int b)
{
    return (float)a.size() / b;
}

int main()
{
    using Traits = function_traits<decltype(free_function)>;

    static_assert(Traits::arity == 2,"");
    static_assert(std::is_same<Traits::return_type,float>::value,"");
    static_assert(std::is_same<Traits::argument<0>::type,const std::string&>::value,"");
    static_assert(std::is_same<Traits::argument<1>::type,int>::value,"");

    return 0;
}

The use of decltype is necessary because we need the type of the function, not the function itself. Here, decltype(free_function) resolves to the function pointer, but it is entirely possible to do something like function_traits<int(char)>.

Through template pattern matching, the compiler is capable of determining R and Args.... We can then alias return_type and get the arity from the parameter pack. Getting the arguments is slightly more complex, unpacking Args... into a tuple and accessing elements using std::tuple_element.

Functionality can be extended to function-like objects such as member function pointers and member object pointers:

// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};

// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&,Args...)>
{};

// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};

Const and non-const member functions are treated as separate types. This basically treats member function/object pointers as functions which take a reference to the appropriate class.

To handle functors and std::function objects (technically also a functor), we can now implement the default specialization:

// functor
template<class F>
struct function_traits
{
    private:
        using call_type = function_traits<decltype(&F::type::operator())>;
    public:
        using return_type = typename call_type::return_type;

        static constexpr std::size_t arity = call_type::arity - 1;

        template <std::size_t N>
        struct argument
        {
            static_assert(N < arity, "error: invalid parameter index.");
            using type = typename call_type::template argument<N+1>::type;
        };
};

template<class F>
struct traits<F&> : public traits<F>
{};

template<class F>
struct traits<F&&> : public traits<F>
{};

This will determine the type traits of the member operator() function then use that to determine the function traits of the functor itself. The two extra specializations will also strip any reference qualifiers to prevent wierd errors.

Like type traits, these function traits are very useful to debug templates. What was once an indecipherable wall of compiler errors can now be easily caught by a single static assertion. As we move on to more complex functional concepts, these will become invaluable.

I leave the implementation of is_callable, which determines if an object is a function or function-like (function pointer, member function/object pointer, functor), to the reader.

Generalized Function Evaluation

I often find myself wishing there was a generalized way of calling functions and function-like “things”.

Functors are obviously quite function-like. Member function pointers can be treated like function where the object pointer as just another parameter. Member object pointers can be treated like a function which retrieves a member from a pointed object.

Now with the new c++11 standard, it is possible to do this quite easily!

#include <type_traits>
#include <utility>

// functions, functors, lambdas, etc.
template<
    class F, class... Args,
    class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type,
    class = typename std::enable_if<!std::is_member_object_pointer<F>::value>::type
    >
auto eval(F&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...))
{
    return f(std::forward<Args>(args)...);
}

// const member function
template<class R, class C, class... Args>
auto eval(R(C::*f)() const, const C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

template<class R, class C, class... Args>
auto eval(R(C::*f)() const, C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

// non-const member function
template<class R, class C, class... Args>
auto eval(R(C::*f)(), C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

// member object
template<class R, class C>
auto eval(R(C::*m), const C& c) -> const R&
{
    return c.*m;
}

template<class R, class C>
auto eval(R(C::*m), C& c) -> R&
{
    return c.*m;
}

The first overload of eval covers almost every single case. Furthermore, note the use of universal references and forwarding which automatically handles const and reference qualifiers.

The next three overloads handle const and non-const member function pointers. Additional parameters of C& are added as if they were parameters of the original functions.

Member object pointers are then treated as functions which take a reference to an object then return a reference to its member.

The use of std::enable_if prevents the first overload from greedily instantiating for member function pointers.

Now to show it in action:

#include <iostream>

struct Bloop
{
    int a = 10;
    int operator()(){return a;}
    int operator()(int n){return a+n;}
    int triple(){return a*3;}
};

int add_one(int n)
{
    return n+1;
}

int main()
{
    Bloop bloop;

    // free function
    std::cout << eval(add_one,0) << "\n";

    // lambda function
    std::cout << eval([](int n){return n+1;},1) << "\n";

    // functor
    std::cout << eval(bloop) << "\n";
    std::cout << eval(bloop,4) << "\n";

    // member function
    std::cout << eval(&Bloop::triple,bloop) << "\n";

    // member object
    eval(&Bloop::a,&bloop)++; // increment a by reference
    std::cout << eval(&Bloop::a,bloop) << "\n";

    return 0;
}

Which gives the expected output:

1
2
10
14
30
11

Note: Be careful with pointers to overloaded functions, the compiler will not be able to determine which overload to use. You will need to either explicitly cast to a function pointer or std::function object with the desired type.

Edit: Thank you to STL for pointing out member object pointers should return references, not values.

Edit2: Thanks to Aaron McDaid for pointing out a few more mistakes.