mardi 1 septembre 2015

How to use type traits to define a partially abstract template base class?

I'm working on the following idea:

There exists a generally abstract templated base class with mutiple template parameters. This class defines a contract guaranteeing the presence of certain methods i.e. a method getMax(). This method among others is generally purely virtual. Except there are special cases in which a sensible implementation can be given without the need to implement it manually every time in a derived class. So basically what I'm trying to achive is to (partially) implement the contracted methods already inside the abstract base class if the template arguments allow this.

I made a small example to illustrate the idea. (Note: The example is not perfect, for instance the implementation for std::string is very special and already implicitly forces TSize to be std::size_t)

#ifndef ABSTRACTBASE_H
#define ABSTRACTBASE_H

#include <type_traits>
#include <string>
#include <limits>

template <typename TSize, typename TVal1, typename TVal2>
class AbstractBase
{
    public:
        AbstractBase(){};
        virtual ~AbstractBase() {};
        virtual TSize getMax() const = 0; // <-- getMax should be generally 
                                          //     purely virtual.

    private:
        TVal1 value1;
        TVal2 value2;
};

//except when TVal1 is an arithmetic type in that case the following definition 
//shall become active.
template <typename TSize, 
          typename TVal1, 
          typename TVal2, 
          typename = typename std::enable_if<std::is_arithmetic<TVal1>::value, TSize>::type>
TSize AbstractBase<TSize, TVal1, TVal2>::getMax()
{
    return std::numeric_limits<TVal1>::max();
}

//... or when TVal1 is a string where this defintion makes sense
template <typename TSize, 
          typename TVal1, 
          typename TVal2, 
          typename = typename std::enable_if<std::is_same<TVal1, std::string>::value, TSize>::type>
TSize AbstractBase<TSize, TVal1, TVal2>::getMax()
{
    return value1.max_size();
}

//... in all other cases the getMax() method shall stay purely virtual and an 
//appropriate definition must be implemented inside a derived class

#endif //ABSTRACTBASE_H


#include "AbstractBase.h"
#include <string>
#include <iostream>

int main()
{
    AbstractBase<int, int, int> arthBase();
    AbstractBase<std::size_t, std::string, long> alphaBase();

    std::cout << arthBase.getMax() << std::endl;
    std::cout << alphaBase.getMax() << std::endl;
}

So I guess what is missing here is a method to actually also alter the declaration ov getMax() as virtual, though im not really sure if/how this is possible using type_traits.

Side note: I haven't worked with type traits very much yet. I know about the SFINAE principle behind it, which basically states that if the substitution for a template parameter fails the following code will be excluded from compilation rather than causing an error. What I haven't found out if the type_trait argument responsible for enabling/disabling the method has to be merged into the class template argument list like I did above, or if it is legal/possible to provide the type trait arguments in a seperate template argument list. In this case I guess it is impossible because the enable_if statement tests the classes template arguments which have to be declared/valid in this context.

Just in case you use really sophisticated type trait magic a more elaborate comment on that part is greatly appreciated.

Aucun commentaire:

Enregistrer un commentaire