C++编译期循环获取变量类型

Posted ithiker

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++编译期循环获取变量类型相关的知识,希望对你有一定的参考价值。

文章目录


问题

假设现在有一些属性以及这些属性对应的数值类型,比如:

"gender" --> char
"age" --> int
"height" --> float
"IQ" ---> int
"name" --> std::string
"weight" --> double

在C++中,如何在编译期依次循环获取这些属性的数值类型,并根据对应的数值类型做出相应的处理(属性可能会增加)


二、解决方案

1.定义类型

首先把所有可能的类型用std::tuple列出:

using Types = std::tuple<float, int, double, std::string, char>;

template<std::size_t N>
using AttributeType = typename std::tuple_element<N, Types>::type;

这里,通过AttributeType<0>就可以得到float 类型

2.定义属性集

将所有的属性和其类型的对应关系列出,由于需要是编译期获得,必须类似加入constexpr 关键字:

constexpr const char* FLOAT_TYPE = "float";
constexpr const char* INT_TYPE = "int";
constexpr const char* DOUBLE_TYPE = "double";
constexpr const char* STRING_TYPE = "std::string";
constexpr const char* CHAR_TYPE = "char";

constexpr std::array<std::pair<const char*, const char*>, 6> attribute2type = 
    "gender", CHAR_TYPE,
    "age", INT_TYPE,
    "height", FLOAT_TYPE,
    "IQ", INT_TYPE,
    "name", STRING_TYPE,
    "weight", DOUBLE_TYPE,
;

3. 获取类型索引

根据2中定义的类型字符串,获取1中需要的类型索引N:

constexpr std::size_t getTypeIndex(const char* name)

    return strings_equal(name, "float") ? 0:
        strings_equal(name, "int") ? 1:
        strings_equal(name, "double") ? 2:
        strings_equal(name, "std::string") ? 3:
        strings_equal(name, "char") ? 4:
        5; // compilation error


这里,需要一个编译期进行字符串比较的函数:

constexpr bool strings_equal(const char* a, const char* b) 
    return *a == *b && (*a == '\\0' || strings_equal(a + 1, b + 1));


4. 编译期循环

如何实现编译期的类似for循环呢,显然不能直接用for,模板的特性决定了可以使用编译期递归来进行替代for循环:

template <typename T>
void print(const char* attribute) 
    std::cout << "attribute = " << attribute << ",type=" <<  typeid(T).name() << std::endl;


constexpr size_t LAST_INDEX = attribute2type.size() - 1;

template <size_t T=LAST_INDEX>
struct PrintHelper 

    public:
        PrintHelper() 
            doPrint<T>();
        
    private:

        template <size_t N>
        void doPrint() 
            print<AttributeType<getTypeIndex(std::get<N>(attribute2type).second)>>(std::get<N>(attribute2type).first);
            doPrint<N-1>();

        

;

template <>
template <>
void PrintHelper<LAST_INDEX>::doPrint<0>() 
    print<AttributeType<getTypeIndex(std::get<0>(attribute2type).second)>>(std::get<0>(attribute2type).first);



将上面所有的代码放到一块,就得到了一个可以在编译期循环获取变量类型的程序:

#include <string>
#include <iostream>
#include <typeinfo>
#include <tuple>
#include <array>

using Types = std::tuple<float, int, double, std::string, char>;

template<std::size_t N>
using AttributeType = typename std::tuple_element<N, Types>::type;

constexpr bool strings_equal(const char* a, const char* b) 
    return *a == *b && (*a == '\\0' || strings_equal(a + 1, b + 1));


constexpr std::size_t getTypeIndex(const char* name)

    return strings_equal(name, "float") ? 0:
        strings_equal(name, "int") ? 1:
        strings_equal(name, "double") ? 2:
        strings_equal(name, "std::string") ? 3:
        strings_equal(name, "char") ? 4:
        5; // compilation error



constexpr const char* FLOAT_TYPE = "float";
constexpr const char* INT_TYPE = "int";
constexpr const char* DOUBLE_TYPE = "double";
constexpr const char* STRING_TYPE = "std::string";
constexpr const char* CHAR_TYPE = "char";

constexpr std::array<std::pair<const char*, const char*>, 6> attribute2type = 
    "gender", CHAR_TYPE,
    "age", INT_TYPE,
    "height", FLOAT_TYPE,
    "IQ", INT_TYPE,
    "name", STRING_TYPE,
    "weight", DOUBLE_TYPE,
;


template <typename T>
void print(const char* attribute) 
    std::cout << "attribute = " << attribute << ",type=" <<  typeid(T).name() << std::endl;



constexpr size_t LAST_INDEX = attribute2type.size() - 1;

template <size_t T=LAST_INDEX>
struct PrintHelper 

    public:
        PrintHelper() 
            doPrint<T>();
        
    private:

        template <size_t N>
        void doPrint() 
            print<AttributeType<getTypeIndex(std::get<N>(attribute2type).second)>>(std::get<N>(attribute2type).first);
            doPrint<N-1>();

        

;

template <>
template <>
void PrintHelper<LAST_INDEX>::doPrint<0>() 
    print<AttributeType<getTypeIndex(std::get<0>(attribute2type).second)>>(std::get<0>(attribute2type).first);



int main() 

    PrintHelper<LAST_INDEX>();

    return 0;


上面程序输出:

$ ./attributeWithType 
attribute = weight,type=d
attribute = name,type=NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
attribute = IQ,type=i
attribute = height,type=f
attribute = age,type=i
attribute = gender,type=c


总结

本文通过下面几个技术点实现了编译期循环获取变量类型:

  1. 通过std::tuple定义变量类型集合,通过typename std::tuple_element<N, Types>::type获取某个变量类型
  2. 通过编译期递归实现编译期字符串比较
  3. 通过std::get(attribute2type)获取属性
  4. 编译期递归实现循环获取变量类型

Reference

  1. https://stackoverflow.com/questions/27490858/how-can-you-compare-two-character-strings-statically-at-compile-time
  2. https://stackoverflow.com/questions/55183295/c-mapping-a-set-of-values-to-types
  3. https://stackoverflow.com/questions/6872919/compile-time-loops

以上是关于C++编译期循环获取变量类型的主要内容,如果未能解决你的问题,请参考以下文章

C++编译期循环获取变量类型

编译期多态和Sfinae

c++ int类型默认值是多少?

golang碎片整理之反射

Go语言基础之反射

Go语言基础之反射