跟我学c++高级篇——动态反射之一遍历
一、动态反射
前面讲一篇静态反射,今天在这个基础上对动态反射进行一下入门。动态反射前面提到过,一般是指在运行时动态获取类型或者生成实例。那么如何才能动态获得类型呢?方法有很多种,下面从最简单的开始。
二、入门程序
动态反射的一个简单例子如下:
#include <tuple>
#include <string>
#include <unordered_map>
#include <iostream>
enum class RgbColor {RGB_RED,RGB_BLACK,RGB_WHITE,NOTHING};
std::tuple<std::string, std::string, std::string> tDesc = {"red","black","white"};
std::unordered_map<std::string, RgbColor> mapColor = { {"red",RgbColor::RGB_RED},{"black",RgbColor::RGB_BLACK},{"white",RgbColor::RGB_WHITE}};
RgbColor strToType(const std::string& color)
{
auto num = mapColor.count(color);
if (num > 0) {
return mapColor[color];
}
else {
return RgbColor::NOTHING;
}
}
void GetColorTest(const std::string&str)
{
auto color = strToType(str);
std::cout << "str is:" << str << "," << "Color is:" << static_cast<int>(color) << std::endl;
}
但是这么做除了麻烦外还容易出错。另外为了一个简单的反射,写了如此多的不可重用的代码确实也让人觉得有点低效。
三、使用__PRETTY_FUNCTION__
其实动态反射的核心在动态类型的获取,那么可以在前面使用__PRETTY_FUNCTION__的基础上对其进行处理,从而使其可以在运行获取对象的类型:
#include <iostream>
#include <string>
#ifdef _WIN64
#define __FUNC__ __FUNCSIG__
#else
#define __FUNC__ __PRETTY_FUNCTION__
#endif
enum class DataType{USB,PCI,HD};
enum DType{USB,PCI,HD};
template<auto T>
auto TypeInfo()
{
std::string type = __FUNC__;
auto s = type.find("T = ") + 4;
auto e = type.find_last_of(']');
return std::string_view{ type.data() + s, e - s };
}
template<typename T>
std::string_view Typeof(T t)
{
int t1 = static_cast<int>(t);
if (t1 == 0)
{
return TypeInfo<DataType::USB>();
}
if (t1 == 1)
{
return TypeInfo<DataType::PCI>();
}
if (t1 == 2)
{
return TypeInfo<DataType::HD>();
}
return std::string_view("");
}
void getTypeTest()
{
std::cout << Typeof(DataType::HD) << std::endl;
std::cout << Typeof<DType>(DType::PCI) << std::endl;
}
int main()
{
getTypeTest();
return 0;
}
上面这些代码其实和最初的代码不同点在于他可以动态的适应名称的变化和相关内容的增加而不需要增加代码的编写和维护。可是上面的代码还是有些麻烦,比如枚举的值不断的增加,那么if语句就这么一直增加下去么?可行是可行的,但有点不爽,可以写成下面的形式:
#include <iostream>
#include <string>
#include <type_traits>
#ifdef _WIN64
#define __FUNC__ __FUNCSIG__
#else
#define __FUNC__ __PRETTY_FUNCTION__
#endif
enum class DataType{USB,PCI,HD,NOT};
enum DType{USB,PCI,HD,NOT};
template<auto T>
auto TypeInfo()
{
std::string type = __FUNC__;
auto begin = type.find("T = ") + 4;
auto end = type.find_last_of(']');
return std::string_view{ type.data() + begin, end - begin };
}
template <int s, int e>
struct static_for
{
template<typename F>
void operator()(const F& func) const
{
if (s < e)
{
//注意:一定要转成常量
func(std::integral_constant<int,s>());
static_for<s + 1, e>()(func);
}
}
};
template <int n>
struct static_for<n, n>
{
template<typename F>
void operator()( const F & func) const
{
// std::cout << "noting" << std::endl;
}
};
constexpr int s = static_cast<int>(DataType::USB);
constexpr int e = static_cast<int>(DataType::NOT);
template<typename T>
std::string_view Typeof(T t)
{
int t1 = static_cast<int>(t);
std::string_view sv;
static_for<s, e>()([&](auto num) {
if (t1 == num) {
//重点要理解const和num.value
const DataType d = DataType{num.value};
sv = TypeInfo<d>();
}
});
return sv;
}
void getTypeTest()
{
std::cout << Typeof(DataType::HD) << std::endl;
//std::cout << Typeof<DType>(DType::PCI) << std::endl;
}
int main()
{
getTypeTest();
return 0;
}
使用了一个static_for,它的实现方式有很多种,这里用的是类似变参模板的递归方式,也可以使用条件判断的方式,也可以直接写到一个模板函数中,都可以实现类似的方式。重点是需要它展开模板,实现类似循环即可。
四、总结
这篇内容就有点接近正常理解的反射的意思了,本来这篇想把一个动态表生成的方式也分析了,但是又觉得太长了,还是分到下一篇再一起分享。慢慢来,不要急。