Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would one write a "meta if else if.." in C++?

I am just learning basics of metaprogramming in C++, and I thought it would be nice to see how the following question would be solved by others. Also, it would be very nice to see a solution using Boost metaprogramming libraries because I consider them as the dark corner for me. So the question is, could this be rewritten more elegantly?

Assume that we have the following struct:

template <std::size_t size>
struct type_factory
{
  typedef typename type_factory_impl<size>::type type;
};

This struct is supposed to typedef type, depending on the value of size. type_factory_impl is the implementation of type_factory. The algorithm that is used to determine type is:

if(size % bits<unsigned long long>::value == 0)
  typedef unsigned long long type;
else if(size % bits<unsigned long>::value == 0)
  typedef unsigned long type;
else if(size % bits<unsigned int>::value == 0)
  typedef unsigned int type;
else if(size % bits<unsigned short int>::value == 0)
  typedef unsigned short int type;
else if(size % bits<unsigned char>::value == 0)
  typedef unsigned char type;
else
  static_assert(false, "The type should be multiple of 'unsigned char' size");

I have solved this metaprogram in two ways. The first one is using pattern matching directly, and the second one using meta if-else. Consider the following as common code between both solutions:

#include <cstddef>
#include <climits>

typedef unsigned char      uchar;
typedef unsigned short int usint;
typedef unsigned int       uint;
typedef unsigned long      ulong;
typedef unsigned long long ulonglong;

// Returns how many bits in Unsigned_Type
template <typename Unsigned_Type>
struct bits
{ enum { value = sizeof(Unsigned_Type)*CHAR_BIT }; };

// struct type_factory_impl ...

template <std::size_t size>
struct type_factory
{
  typedef typename type_factory_impl<size>::type type;
};

int main()
{
  auto a = type_factory<8>::type(0);  // unsigned char
  auto b = type_factory<16>::type(0); // unsigned short int
  auto c = type_factory<24>::type(0); // unsigned char
  auto d = type_factory<32>::type(0); // unsigned long
  auto e = type_factory<40>::type(0); // unsigned char
  auto f = type_factory<48>::type(0); // unsigned short int
  auto g = type_factory<56>::type(0); // unsigned char
  auto h = type_factory<64>::type(0); // unsigned long long
}

The first solution:

template <bool is_uchar>
struct unsigned_char
{
  typedef unsigned char type;
  static_assert(is_uchar,
     "error: size must be multiple of 'unsigned char' size"); 
};
template <>
struct unsigned_char <true>
{ typedef uchar type; };

template <bool is_usint, std::size_t size>
struct unsigned_short_int
{ typedef typename
   unsigned_char<size % bits<uchar>::value == 0>::type type; };
template <std::size_t size>
struct unsigned_short_int <true, size>
{ typedef usint type; };

template <bool is_uint, std::size_t size>
struct unsigned_int
{ typedef typename
   unsigned_short_int<size % bits<usint>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_int <true, size>
{ typedef uint type; };

template <bool is_ulong, std::size_t size>
struct unsigned_long
{ typedef typename
   unsigned_int<size % bits<uint>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_long <true, size>
{ typedef ulong type; };

template <bool is_ulonglong, std::size_t size>
struct unsigned_long_long
{ typedef typename 
   unsigned_long<size % bits<ulong>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_long_long <true, size>
{ typedef ulonglong type; };

template <std::size_t size>
struct type_factory_impl
{ typedef typename 
   unsigned_long_long<size % bits<ulonglong>::value == 0, size>::type type; };

The second solution:

template <bool condition, typename Then, typename Else>
struct IF
{ typedef Else type; };
template <typename Then, typename Else>
struct IF <true, Then, Else>
{ typedef Then type; };

template <std::size_t size>
struct type_factory_impl
{
  typedef typename
    IF<size % bits<ulonglong>::value == 0, ulonglong,
      typename IF<size % bits<ulong>::value == 0, ulong,
        typename IF<size % bits<uint>::value == 0, uint,
          typename IF<size % bits<usint>::value == 0, usint,
            typename IF<size % bits<uchar>::value == 0, uchar, uchar>::type
          >::type
        >::type
      >::type
    >::type type;
};
like image 854
Khaled Alshaya Avatar asked Nov 21 '10 18:11

Khaled Alshaya


2 Answers

Like you, I consider Boost.MPL as black magic, so I thought that could be the occasion to try and use it to answer your question. Please keep in mind that this is my very first attempt with this library, and that some guru out there will probably provide a better solution.

The idea is to use boost::mpl::find_if to find the first matching item in a sequence of types.

typedef boost::mpl::vector
    <
        unsigned long long,
        unsigned long,
        unsigned int,
        unsigned short,
        unsigned char
    > type_sequence;

template<std::size_t size>
struct predicate
{
    template<class T>
    struct apply {
        static const bool value = (size % bits<T>::value == 0);
    };
};

template<std::size_t size>
struct type_factory_impl
{
    typedef typename boost::mpl::find_if
        <
            type_sequence,
            typename predicate<size>::apply<boost::mpl::_1>
        >::type iterator_type;

    typedef typename boost::mpl::deref<iterator_type>::type type;
};

It seems to give me the good result :

alt text

I'm not handling the "default" case, but my brain just started bleeding through my nose, I'll try to complete my answer later and hope this will help.

like image 188
icecrime Avatar answered Oct 15 '22 06:10

icecrime


Something wrong with specialization?

template<size_t N>
struct lowest_bit
{
    enum
    {
        lowest_bit_removed = N & (N-1),
        value = N ^ lowest_bit_removed
    };
};

template<size_t size> struct type_factory_impl                  { typedef uchar     type; };
template<> struct type_factory_impl<sizeof(ushort)   *CHAR_BIT> { typedef ushort    type; };
template<> struct type_factory_impl<sizeof(uint)     *CHAR_BIT> { typedef uint      type; };
template<> struct type_factory_impl<sizeof(ulong)    *CHAR_BIT> { typedef ulong     type; };
template<> struct type_factory_impl<sizeof(ulonglong)*CHAR_BIT> { typedef ulonglong type; };

template<size_t size>
struct type_factory
{
    typedef typename type_factory_impl<lowest_bit<size>::value>::type type;
};
like image 35
Ben Voigt Avatar answered Oct 15 '22 05:10

Ben Voigt