Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++11

Get Integral Type By Size, Switch By Type ID

5.00/5 (3 votes)
18 Sep 2015MIT2 min read 19.5K   7  
Type tricks (above described and other)

Introduction

This tip is all about simple, but useful tricks with the template metaprogramming.

I used this trick to generate a versatile and optimal code.

Background

C++11 introduced a new <type_traits> (also here) library, which became a faithful assistant to me and many other C++ programmers, so if you've still missed it for any reason - it is a good time to take a closer look.

Using the Code

Code parts from the header module ("TypeHelpers.h"):

C++
#include <cstddef>
#include <cstdint>
#include <type_traits>

1) BoolType

C++
// bool val. to bool type
template <const bool Bool>
struct BoolType {};

template<>
struct BoolType<true> {
  typedef std::true_type Type;
};

template<>
struct BoolType<false> {
  typedef std::false_type Type;
};

#define BOOL_TYPE(BoolVal) BoolType<BoolVal>::Type

This struct is a simple converter from a bool value to the bool type (for reversed conversion the std::true_type and std::false_type itself can be used, as they have an operator bool defined)

2) AddRemoveConst

C++
template <typename T, const bool Constant>
struct AddRemoveConst {};

template <typename T>
struct AddRemoveConst<T, true> {
  typedef const T Type;
};

template <typename T>
struct AddRemoveConst<T, false> {
  typedef T Type;
};

#define ADD_REMOVE_CONST(Type, StaticPredicate) AddRemoveConst<Type, StaticPredicate>::Type

This struct is pretty self-explainable. Through std::add_const / std::remove_const can be used to achieve the same logic, I need it both in a one single class.

3) TypeTag

C++
// std::is_fundamental [http://www.cplusplus.com/reference/type_traits/is_fundamental/]
enum class ECFundamentalTypeTags {
  UNIDENTIFIED,
  BOOL,
  SIGNED_CHAR,
  UNSIGNED_CHAR,
  // Signedness of wchar_t is unspecified
  //  [http://stackoverflow.com/questions/11953363/wchar-t-is-unsigned-or-signed]
  WCHAR,

  //// 'char16_t' AND 'char32_t' SHOULD be a keywords since the C++11,
  ////  BUT MS VS Com 2013 Upd 5 CTP does NOT supports that
  ////  AND specifys 'char16_t' AND 'char32_t' as a typdef aliases instead
  ////  (so they are NOT presented here)

  SIGNED_SHORT_INT,
  UNSIGNED_SHORT_INT,
  SIGNED_INT,
  UNSIGNED_INT,
  SIGNED_LONG_INT,
  UNSIGNED_LONG_INT,
  SIGNED_LONG_LONG_INT, // C++11
  UNSIGNED_LONG_LONG_INT, // C++11
  FLOAT,
  DOUBLE,
  LONG_DOUBLE,
  VOID_,
  NULLPTR // C++11 std::nullptr_t
};

template <typename T, class TypeTags = ECFundamentalTypeTags>
struct TypeTag {
  static const auto TAG = TypeTags::UNIDENTIFIED;
};

template <class TypeTags>
struct TypeTag<bool, TypeTags> {
  static const auto TAG = TypeTags::BOOL;
};

template <class TypeTags>
struct TypeTag<signed char, TypeTags> {
  static const auto TAG = TypeTags::SIGNED_CHAR;
};

template <class TypeTags>
struct TypeTag<unsigned char, TypeTags> {
  static const auto TAG = TypeTags::UNSIGNED_CHAR;
};

template <class TypeTags>
struct TypeTag<wchar_t, TypeTags> {
  static const auto TAG = TypeTags::WCHAR;
};

template <class TypeTags>
struct TypeTag<signed short int, TypeTags> {
  static const auto TAG = TypeTags::SIGNED_SHORT_INT;
};

template <class TypeTags>
struct TypeTag<unsigned short int, TypeTags> {
  static const auto TAG = TypeTags::UNSIGNED_SHORT_INT;
};

template <class TypeTags>
struct TypeTag<signed int, TypeTags> {
  static const auto TAG = TypeTags::SIGNED_INT;
};

template <class TypeTags>
struct TypeTag<unsigned int, TypeTags> {
  static const auto TAG = TypeTags::UNSIGNED_INT;
};

template <class TypeTags>
struct TypeTag<signed long int, TypeTags> {
  static const auto TAG = TypeTags::SIGNED_LONG_INT;
};

template <class TypeTags>
struct TypeTag<unsigned long int, TypeTags> {
  static const auto TAG = TypeTags::UNSIGNED_LONG_INT;
};

template <class TypeTags>
struct TypeTag<signed long long int, TypeTags> {
  static const auto TAG = TypeTags::SIGNED_LONG_LONG_INT;
};

template <class TypeTags>
struct TypeTag<unsigned long long int, TypeTags> {
  static const auto TAG = TypeTags::UNSIGNED_LONG_LONG_INT;
};

template <class TypeTags>
struct TypeTag<float, TypeTags> {
  static const auto TAG = TypeTags::FLOAT;
};

template <class TypeTags>
struct TypeTag<double, TypeTags> {
  static const auto TAG = TypeTags::DOUBLE;
};

template <class TypeTags>
struct TypeTag<long double, TypeTags> {
  static const auto TAG = TypeTags::LONG_DOUBLE;
};

template <class TypeTags>
struct TypeTag<void, TypeTags> {
  static const auto TAG = TypeTags::VOID;
};

template <class TypeTags>
struct TypeTag<std::nullptr_t, TypeTags> {
  static const auto TAG = TypeTags::NULLPTR;
};

#define TYPE_TAG(Object) TypeTag<std::decay<decltype(Object)>::type>::TAG

This type helper allows to do switch by type. Through both std::type_info::hash_code and std::type_index can be used to do type-to-integral-number mapping, they are not compile time constants, so cannot be used with the switch statement (but can be used with if, while, STL containers, etc.). Although, you can try to use C++11 constexpr, but my MS VS 2013 Community Update 5 does not support it (sadly).

As you can see, TypeTag can use any TypeTags and is easily expandable to support any user type.

4) IntegralTypeBySize

C++
// Size is in bytes
// Fixed width integer types (since C++11): http://en.cppreference.com/w/cpp/types/integer
// See also: http://www.viva64.com/en/t/0012/
template <const size_t Size, const bool Signed>
struct IntegralTypeBySize {
  static const auto TAG = ECFundamentalTypeTags::UNIDENTIFIED;
};

template<>
struct IntegralTypeBySize<1U, true> {
  typedef int8_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<2U, true> {
  typedef int16_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<4U, true> {
  typedef int32_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<8U, true> {
  typedef int64_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<1U, false> {
  typedef uint8_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<2U, false> {
  typedef uint16_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<4U, false> {
  typedef uint32_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

template<>
struct IntegralTypeBySize<8U, false> {
  typedef uint64_t Type;
  static const auto TAG = TypeTag<Type, ECFundamentalTypeTags>::TAG;
};

In template algorithms, I used this helper class to provide a memory chunk of an exact size and MSB (Most Significant Bit) handling. It based on C++11 introduced fixed width integer types.

Test code for Ideone online compiler:

C++
// <Include the code from TypeHelpers>

#include <iostream>

template<typename T>
void f(T obj) throw() {
    switch(TYPE_TAG(obj)) {
        case ECFundamentalTypeTags::SIGNED_INT:
        case ECFundamentalTypeTags::UNSIGNED_INT:
          std::cout << "int!" << std::endl;
        break;
        
        case ECFundamentalTypeTags::SIGNED_LONG_LONG_INT:
        case ECFundamentalTypeTags::UNSIGNED_LONG_LONG_INT:
          std::cout << "long long int!" << std::endl;
        break;
        
        case ECFundamentalTypeTags::FLOAT:
        case ECFundamentalTypeTags::DOUBLE:
        case ECFundamentalTypeTags::LONG_DOUBLE:
          std::cout << "floating point number!" << std::endl;
        break;
        
        default:
          std::cout << "unknown!" << std::endl;
    }
}

#include <cassert>
#include <typeinfo>

int main() {
    const BOOL_TYPE(true) btt;
    static_assert(btt(), "");
    std::cout << btt() << std::endl;
    
    const BOOL_TYPE(false) btf;
    static_assert(!btf(), "");
    std::cout << btf() << std::endl;
    
    auto v_1_ = false;
    static_assert(ECFundamentalTypeTags::BOOL == TYPE_TAG(v_1_), "");
    auto v_2_ = 0;
    static_assert(ECFundamentalTypeTags::SIGNED_INT == TYPE_TAG(v_2_), "");
    auto v_3_ = 0L;
    static_assert(ECFundamentalTypeTags::SIGNED_LONG_INT == TYPE_TAG(v_3_), "");
    auto v_4_ = 0ULL;
    static_assert(ECFundamentalTypeTags::UNSIGNED_LONG_LONG_INT == TYPE_TAG(v_4_), "");
    
    f(1), f(1ULL), f(1.0), f(1.0L);
    
    IntegralTypeBySize<sizeof(char), true>::Type t1_ = 0;
    static_assert(sizeof(t1_) == sizeof(char), "");
    static_assert(std::is_integral<decltype(t1_)>::value && 
    	std::is_signed<decltype(t1_)>::value, "");
  
    IntegralTypeBySize<sizeof(int), true>::Type t2_ = 0;
    static_assert(sizeof(t2_) == sizeof(int), "");
    static_assert(std::is_integral<decltype(t2_)>::value && 
    	std::is_signed<decltype(t2_)>::value, "");
    
    IntegralTypeBySize<sizeof(long long int), true>::Type t4_ = 0;
    static_assert(sizeof(t4_) == sizeof(long long int), "");
    static_assert(std::is_integral<decltype(t4_)>::value && 
    	std::is_signed<decltype(t4_)>::value, "");
    
    const IntegralTypeBySize<8U, false>::Type array[32U] = {0};
    std::cout << "\ni've got a " << 
    	sizeof(array) / sizeof(*array) << " length array of "
              << typeid(*array).name() << "s here!\n";
    
    return 0;
}

Output:

1
0
int!
long long int!
floating point number!
floating point number!

i've got a 32 length array of ys here!

Notes:

AddRemoveConst is removed as it is not compiled by Ideone (ok with MS VS 2013 Community Update 5)

TYPE_TAG macro is changed from:

C++
#define TYPE_TAG(Object) TypeTag<std::decay<decltype(Object)>::type>::TAG

to:

C++
#define TYPE_TAG(Object) TypeTag<decltype(Object)>::TAG

for the same reason.

Points of Interest

This module is just a small part of the library, which uses C++11 features and which I am working under now, I decided to make it a public property.

History

  • Update 1: Minor code fix, now OK with the Ideone online compiler

License

This article, along with any associated source code and files, is licensed under The MIT License