C++ 类型系统增强

ぐ巨炮叔叔 提交于 2020-08-08 09:40:54

大家应该比较熟悉, C++中可以通过 操作符 typeid来获取类型名称

std::cout << typeid(int).name() << std::endl;

但是这个name()的返回值是取决于编译器的,在vc和gcc中打印出来的结果如下:

int // vc
i   // gcc

对于自定义类,输出的类型,也并不是原始类型

class TestType {};
std::cout << typeid(TestType).name() << std::endl;

//vs输出 class TestType

因此,  操作符 typeid的作用就局限于类型输出及类型与类型进行对比, 并不能用于跟字符串对比。

下面我们再看看,容器类型的输出:

std::cout << typeid(std::map<std::string, int>).name() << std::endl;

以下将自定义类型处理,对typeid进行封装,简化类型的输出

封装typeid,统一平台输出

template<typename T>
std::string getTypeName()
{
    std::string tyName;
#if defined(__GNUC__)
    char* real_name = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
    tyName = real_name;
    free(real_name);
#else
    tyName = typeid(T).name();
#endif
    return tyName;
}

定义类型处理模板

//非特化流程,进行自动化萃取分类
template<typename T>
struct type_system
{
	std::string name()
	{
		return type_tarits<T>().name();
	}
};

std::string 处理

// 支持std::string
template<>
struct type_system<std::string>
{
	std::string name()
	{
		return "std::string";
	}
};

单个参数的容器类型处理

// 支持一个参数的STL容器
#define __TYPE_SYSTEM_DEF_ONE(OPT) \
template<typename T> \
struct type_system< OPT<T> > \
{ \
	std::string name() \
	{\
		std::ostringstream oss;\
		oss << #OPT  << "<" << type_system<T>().name()  << ">";\
		return oss.str();\
	}\
};

__TYPE_SYSTEM_DEF_ONE(std::vector)

两个个参数的容器类型处理

// 支持两个参数的STL容器
#define __TYPE_SYSTEM_DEF_TWO(OPT) \
template<typename K, typename V> \
struct type_system< OPT<K, V> >\
{\
	std::string name()\
	{\
		std::ostringstream oss;\
		oss << #OPT << "<" << type_system<K>().name() << "," << type_system<V>().name() << ">";\
		return oss.str();\
	}\
};

__TYPE_SYSTEM_DEF_TWO(std::map)

支持std::tuple输出

struct TupleArgs
{
	template<typename Tuple, std::size_t... I>
	static void Get(std::ostringstream& oss, std::index_sequence<I...>)
	{
		std::vector<std::string> paramType{ _Get<typename std::tuple_element<I, Tuple>::type>()... };
		for (size_t i = 0; i < paramType.size(); i++)
		{
			oss << paramType[i];
			if (i != paramType.size() - 1)
			{
				oss << ",";
			}
		}
	}

	template<typename T>
	static std::string _Get()
	{
		return type_system<T>().name();
	}
};

// 支持std::tuple
template<typename...Args> 
struct type_system< std::tuple<Args...> >
{
	std::string name()
	{
		std::ostringstream oss; 
		oss << "std::tuple" << "<";
		TupleArgs::Get<std::tuple<Args...>>(oss, std::make_index_sequence<sizeof...(Args)>{});
		oss << ">";
		return oss.str(); 
	}
};

支持函数指针

// 支持函数指针
template<typename R, typename...Args>
struct type_system< R (*)(Args...) >
{
	std::string name()
	{
		std::ostringstream oss;
		oss << type_system<R>().name() << "(*)(";
		TupleArgs::Get<std::tuple<Args...>>(oss, std::make_index_sequence<sizeof...(Args)>{});
		oss << ")";
		return oss.str();
	}
};

支持成员变量

// 支持成员变量
template<typename T, typename C>
struct type_system<T C::* >
{
	std::string name()
	{
		std::ostringstream oss;
		oss << type_system<T>().name() << " " << type_system<C>().name() << "::*";

		return oss.str();
	}
};

支持成员函数指针

template<typename R, typename C, typename...Args>
struct type_system< R(C::*)(Args...) >
{
	std::string name()
	{
		std::ostringstream oss;
		oss << type_system<R>().name() << "(" << type_system<C>().name() << "::*)(";
		TupleArgs::Get<std::tuple<Args...>>(oss, std::make_index_sequence<sizeof...(Args)>{});
		oss << ")";
		return oss.str();
	}
};

出了特化类型,还有基础类型和自定义类型,需要进一步区分处理

//类型系统
template<typename T, typename P>
struct __type_system;

//非类类型
template<typename T >
struct __type_system<T, typename  std::enable_if<!std::is_class<T>::value, T>::type>
{
	std::string name()
	{
        return getTypeName<T>();
	}
};


//类类型
template<typename T >
struct __type_system<T, typename  std::enable_if<std::is_class<T>::value, T>::type>
{
	std::string name()
	{
		std::string tmpName = getTypeName<T>();
		int nPos = tmpName.find("class ");
		if (nPos != std::string::npos)
		{
			return tmpName.substr(nPos + 6);
		}
		return tmpName;
	}
};

template<typename T>
class type_tarits : public __type_system< T,  T>
{
public:
};

以上对所有类型进行分区分处理,接下来定义一个普通的函数模板来进行简化使用,支持参数值和参数类型两种方式的类型获取

template<typename T>
std::string type_name(T val)
{
	return std::move(type_system<T>().name());
}

template<typename T>
std::string type_name()
{
	return std::move(type_system<T>().name());
}

接下来测试一下,输出的效果:

int main()
{
	std::cout << type_name<int>() << std::endl;
	std::cout << type_name<std::map<std::string, std::string>>() << std::endl;
	std::cout << type_name<std::map<std::string, std::vector<std::string>>>() << std::endl;
	std::cout << type_name<Test>() << std::endl;

	std::unordered_map<std::string, std::string> v1;
	std::tuple<std::string, std::string, int, double> t1;
	std::cout << type_name(v1) << std::endl;
	std::cout << type_name(t1) << std::endl;

	std::cout << type_name(&FuncTest) << std::endl;
	std::cout << type_name(&Test::Func) << std::endl;
	std::cout << type_name(&Test::a) << std::endl;	
    return 0;
}

 

总结:

    因为项目需要对类型进行反向输出,因为暂停下来研究了一下类型系统,还是挺有意思的,以上内容大量使用了泛型编程技巧, 充分运用了C++在预处理期、编译期和运行期(RAII)的处理能力 ,在此基础上,我们可以进行进步一细化处理,也可以对不同平台进行字解析处理,已达到统一平台的目的。

    或许你会问,研究这些有什么用,其实这里还真不是兴起研究玩玩的,我的目的是在网络编程中,可以通过服务端自解析的方式输出客户端代码,已保证客户端与服务端的接口一致性,也简化开发流程。

    目前RPC框架中,大部分都是通过定义接口文件,然后通过接口解析程序生成服务端接口和客户端接口,但服务端接口代码继承到服务中也是非常繁琐,如果服务端能自解析生成客户端代码,那么我们面对网络编程时就像开发单机程序一样简单,这是一件多么令人兴奋的事情啊!

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!