lo tolmencre lo tolmencre - 2 months ago 9
C++ Question

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?

Answer

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.

Comments