Sunday, 22 February 2015

Fun with Template Meta-programming, enable_if_all

It is true that sometimes ignorance is bliss...sometimes. In my case ignorance or to be more specific, lack of curiosity over this weekend to "just try it out once" lead me to do a lot of crazy stuff with template meta-programming.

What I wanted
While working on my side project work, I came across with the need of having a class with forwarding constructor. Now, people familiar with C++11 would know how greedy a forwarding constructor can be, it's so greedy that it starts taking up the role of copy constructor (for non const object) until we limit it by SFINAE (Substitution Failure Is Not An Error). 

One can lookup here to know what I am talking about.

So, back to my problem. I am sitting here with a need to have a forwarding constructor taking variable template parameters or variadic templates, but also have my copy constructor work for me without any issues. The known solution for such cases is to use some kind of SFINAE or to be more specific use 'std::enable_if'. But, here I am working with variadic templates instead of just known number of template parameters. At this point, I just made one heck of an 'assumption' that 'std::enable_if' won't work with variadic templates and immediately (out of too much excitement) sat down to implement my own generic 'enable_if_all' type trait. Oh, boy! that was embarassing !

What was the big idea ?
The idea was to create a type trait that would take any other type_trait structures (C++11 has got so many of them, check it out here) which accepts 1 or 2 template parameter in general along with bunch variable number of other types on which the type_trait would be applied to get the final result, which is either 'true' or 'false', just like 'std::enable_if'.

But, after a lot of attempts I figured out many things that won't work with template meta-programming. Finally I had to settle for a very naive solution and at the same time figured out that 'std::enable_if' also works with variadic templates and also with type_traits requiring more that 1 template parameter (for eg. std::is_constructible, std::is_same).

In the process, I discovered a missing feature that if present would have been so beautiful and powerful. Currently, there are two ways by which you can pass a template class as a template parameter:

1. Along with the type, i.e as a complete type.

template <typename T>
class Test {
};

template <class ClsT>
class Composer {
};

int main() {
    Composer<Test<int>> cmp;
    return 0;
}

2. Without the type information i.e as template template parameter


template <typename T>
class Test {
};

template <template <typename> class ClsT, typename T>
class Composer {
private:
    ClsT<T> cmp_;
};

int main() {
    Composer<Test, int> cmp;
    return 0;
}

Now, what would have been more beautiful is if we had something like template type 'explosion'. Something like below:

template <typename A, typename B> class Test {};

template <template <typename T1, typename T2> class ClsT>
class Composer {
};


In this case, we would be able to make use of template parameters T1 and T2 as well. But, currently c++ does not allow that. Had it worked, I would be still ignorant about 'std::enable_if'.

What I have ?
Nothing much to be proud showing off, but some important lessons learnt (which I may forget over a period of time :)) related to template meta-programming. It is surely a different language within C++. It might be harsh to untrained eyes, but a real beauty to someone who understands it. Hope it won't be killed by 'auto' in future c++ standards.

Here is my version of 'enable_if_all'. Note that, a lot of things are still missing in it.

#include <type_traits>

template <typename Type>
struct SType {
    typedef Type type;
};

template <template <typename...> class ValidatnFn, typename... Types>
struct enable_if_all;

template <template <typename> class ValidatnFn, typename LastT>
struct enable_if_all<ValidatnFn, LastT> {
    static bool const value = ValidatnFn<LastT>::value;
};

// This is for type_trait checks taking in only single template parameter
template <template <typename> class ValidatnFn, typename FirstT, typename... RestT>
struct enable_if_all<ValidatnFn, FirstT, RestT...> {
    static bool const value = ValidatnFn<FirstT>::value & enable_if_all<ValidatnFn, RestT...>::value ;
};

template <template <typename, typename> class ValidatnFn, typename FirstT, typename LastT>
struct enable_if_all<ValidatnFn, FirstT, LastT> {
    static bool const value = ValidatnFn<typename FirstT::type, LastT>::value;
};

// This is for type_trait checks taking in 2 template parameters
template <template <typename, typename> class ValidatnFn, typename FirstT, typename SecT, typename... RestT>
struct enable_if_all<ValidatnFn, FirstT, SecT, RestT...> {
    static bool const value = ValidatnFn<typename FirstT::type, SecT>::value & enable_if_all<ValidatnFn, FirstT, RestT...>::value ;
};

If you are eyes are itching, please pay a visit to doctor!  :)

No comments:

Post a Comment