65.9K
CodeProject 正在变化。 阅读更多。
Home

通过大小获取整型类型,通过类型 ID 切换

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2015 年 9 月 18 日

MIT

2分钟阅读

viewsIcon

19985

downloadIcon

64

类型技巧(上述及其他)

引言

这个技巧主要介绍一些简单但有用的模板元编程技巧。

我使用这个技巧来生成一个通用且优化的代码。

背景

C++11 引入了一个新的 <type_traits> (也在这里) 库,它成为了我以及许多其他 C++ 程序员忠实的助手,所以如果你因为任何原因仍然错过了它 - 现在是时候仔细看看了。

Using the Code

代码片段来自头文件模块 ("TypeHelpers.h"):

#include <cstddef>
#include <cstdint>
#include <type_traits>

1) BoolType

// 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

这个 struct 是一个简单的从 布尔值布尔类型 的转换器(对于反向转换,可以使用 std::true_typestd::false_type 本身,因为它们定义了一个 operator bool

2) AddRemoveConst

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

这个 struct 顾名思义。虽然可以通过 std::add_const / std::remove_const 实现相同的逻辑,但我需要在同一个类中同时使用它们。

3) TypeTag

// 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

这个类型辅助工具允许通过类型进行 switch。虽然可以通过 std::type_info::hash_codestd::type_index类型映射到整型数,但它们**不是编译时常量**,因此不能与 switch 语句一起使用(但可以与 ifwhile、STL 容器等一起使用)。虽然,你可以尝试使用 C++11 constexpr,但我的 MS VS 2013 Community Update 5 不支持它(很遗憾)。

如你所见,TypeTag 可以使用任何 TypeTags 并且易于扩展以支持任何用户类型。

4) IntegralTypeBySize

// Size is in bytes
// Fixed width integer types (since C++11): https://cppreference.cn/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;
};

在模板算法中,我使用这个辅助类来提供一个精确大小的内存块和 MSB最高有效位)处理。它基于 C++11 引入的 固定宽度整数类型

Ideone 在线编译器测试代码:

// <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;
}

输出

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

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

注释

AddRemoveConst 已被移除,因为它不能被 Ideone 编译(在 MS VS 2013 Community Update 5 中可以正常编译)

TYPE_TAG 宏已更改为

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

to

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

出于同样的原因。

关注点

这个模块只是 使用 C++11 特性的库 的一小部分,我目前正在开发它,我决定将其设为 public 属性。

历史

  • 更新 1:代码小幅修复,现在与 Ideone 在线编译器兼容
© . All rights reserved.