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 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.
It seems to give me the good result :
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.
Something wrong with specialization?