Search code examples
c++boostwrappertypedefgeneric-programming

Writing a Generic Wrapper: Conditionally Map different Types from Template Arguments onto a Single Class-Internal Type


TL;DR: How can I perform conditional typedefs depending on the type of the template argument.

I am writing a Wrapper for different bitset implementations. For that I am referencing std::bitset<> and boost::dynamic_bitset<>. I observe that boost::dynamic_bitset<> follows the implementation of std::bitset<> by for example calling the proxy class for index handling returned by operator[] with a class called reference, just as std::bitset<> does. So, that is useful because in my wrapper -- that is templated with the bitset type BITSET -- I can then do

typename BITSET::reference operator[](INDEX_TYPE i)
    { return bitset[i]; }

and it will work for both, std::bitset<> and boost::dynamic_bitset<>.

However, there are features that are not parallelized like that. For instance declares boost::dynamic_bitset<> typedef std::size_t size_type; as the index type, as in:

bool operator[](size_type pos) const { return test(pos); }

But std::bitset<> simply uses size_t for that:

_GLIBCXX_CONSTEXPR bool                                                                          
operator[](size_t __position) const
{ return _Unchecked_test(__position); }

So, in my Wrapper I could do typedef size_t INDEX_TYPE or something like that, but that might not work for another implementation that does not use size_t, as these two coincidentally (or not) do.

Obviously there is no really generic way to do this, but could I at least somehow conditionally define my INDEX_TYPE, akin to this:

template <class BITSET, class T>
/**
 *
 * @tparam T Use this parameter to pass on the template parameter that
 *         was used to template BITSET
 */
class BitsetWrapper
{
public: // typedefs
    if (typeid(BITSET) == bool::dynamic_bitset<T>)
        typedef tyeanme BITSET::size_type INDEX_TYPE;
    else if (typeid(BITSET) == std::bitset<T>)
        typedef size_t INDEX_TYPE;
    else if (typeid(BITSET) == BitSet<T>) // my own implementation
        typedef BITSET::INDEX_TYPE INDEX_TYPE;
    else
        throw std::invalid_argument("unsupported type: "+typeid(BITSET).name());

The above approach does not work and even if it did looks very clunky.

It is besides the point of my question, but just for completeness this is the error:

bitsetWrapper.hpp:34:5: error: expected member name or ';' after declaration specifiers
    if (typeid(BITSET) == bool::dynamic_bitset<T>)
    ^
bitsetWrapper.hpp:36:5: error: expected member name or ';' after declaration specifiers
    else if (typeid(BITSET) == std::bitset<T>)
    ^
bitsetWrapper.hpp:38:5: error: expected member name or ';' after declaration specifiers
    else if (typeid(BITSET) == BitSet<T>)
    ^
bitsetWrapper.hpp:40:5: error: expected member name or ';' after declaration specifiers
    else

I guess you can't just put conditionals randomly into the class space outside of functions, and this was the first time I ever tried this, because of the weird circumstances.

But how can this be approached correctly?


Solution

  • You cannot use if/else logic to defined a typedef like that. You can use a helper class to deduce it.

    template <typename T> struct TypedefSelector;
    
    template <typename T> struct TypedefSelector<boost::dynamic_bitset<T>>
    {
       using IndexType = BITSET::size_type;
    };
    
    template <typename T> struct TypedefSelector<std::bitset<T>>
    {
       using IndexType = size_t;
    };
    
    template <typename T> struct TypedefSelector<BitSet<T>>
    {
       using IndexType = BITSET::INDEX_TYPE;
    };
    

    And then use:

    template <class BITSET, class T>
    class BitsetWrapper
    {
       using INDEX_TYPE = typename TypedefSelector<T>::IndexType;
       ...
    };
    

    For typenames that don't have a TypedefSelector, you will get compile time error.