I am just learning the basics of metaprogramming in C ++, and I thought it would be nice to see how others can solve the following question. Also, it would be really nice to see a solution using Boost metaprogramming libraries because I find them a dark corner for me. So the question is, can this be rewritten more elegantly?
Suppose we have the following structure:
template <std::size_t size> struct type_factory { typedef typename type_factory_impl<size>::type type; };
This structure should typedef type , depending on the value of size . type_factory_impl is an implementation of type_factory . The algorithm used to determine type :
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 solved this metaprogram in two ways. The first uses direct pattern matching, and the second uses meta if-else . As a common code between both solutions, consider the following:
#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 }
First decision:
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; };