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





5.00/5 (3投票s)
类型技巧(上述及其他)
引言
这个技巧主要介绍一些简单但有用的模板元编程技巧。
我使用这个技巧来生成一个通用且优化的代码。
背景
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_type
和 std::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_code 和 std::type_index 将 类型映射到整型数,但它们**不是编译时常量**,因此不能与 switch
语句一起使用(但可以与 if
、while
、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 引入的 固定宽度整数类型。
// <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 在线编译器兼容