﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-huaxiazhihuo-随笔分类-C++代码自动生成</title><link>http://www.cppblog.com/huaxiazhihuo/category/21252.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 05 Jul 2017 06:10:31 GMT</lastBuildDate><pubDate>Wed, 05 Jul 2017 06:10:31 GMT</pubDate><ttl>60</ttl><item><title>完备的运行时类型信息</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/05/215052.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Wed, 05 Jul 2017 03:45:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/05/215052.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215052.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/05/215052.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215052.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215052.html</trackback:ping><description><![CDATA[<div>众所周知，码猿写代码，自然要求严谨周密，殊不知想象力也很重要。本座阅码几十年，很是感概很多码猿的脑洞被大大禁锢，鲜有人能越雷池一步，特别是c++的同学，连同委员会的那一坨老头子，都很让人无语至极，出自这些人的作品，都是一个死鱼眼睛样子，千人一面，毫无灵动之生趣可言。stl，boost这些库都是这样子（虽然它们确实可以完成大多数日常任务），更别说其他的库，没有什么让人耳目一新之处。<br /><br />就说说动态类型信息这块，又或者说是反射。自然，语言本身提供的废物type_info就懒得说了，除了证明c++也东施效颦，也能支持动态信息之外，就别无用处了，有谁会正儿八经的用type_info做点正儿八经的事情呢。因此，各路人马纷纷上阵，都要弥补c++在运行时类型信息上的缺失。因为类型的反射信息实在太重要，或者说，反射的用武之地太多太多，表面上很多事情不需要反射，或者字面代码上就看不到反射的痕迹，但是内里的实现，大把大把的反射在发光发热。c++坚持不在动态信息上给予一点点多余的支持，并不表示c++就不需要反射了，看看标准库这个极力回避动多态的典范，是一个怎样的失败作品，嗯，这个以后再谈吧。假如stl一开始就没有如此大力排斥动多态，你看看就连内存分配的allocator都可以做到静态类型信息里面（最新版的c++终于也要接受多态的allocator，c++界居然一片欢呼鼓舞，真是悲哀），今时今日的c++就不会在很多领域上到处割地求和。<br /><br />总的来说，现在市面上的c++反射库，都是侵入式，都学着mfc那一套，都是要求继承自一个基类Object，然后才能对外提供反射信息的功能，先不说它们提供的类型信息是否完备，这样子就把用途广泛限制死在一个很窄很窄的小圈子里面了。这些反射库，1、不能反射基本类型，int、char、double、const char*、&#8230;&#8230;等；2、不能反射非继承自Object的class或者struct，3、也不能反射模板类，比如vector&lt;int&gt;、list&lt;vector&lt;vector&lt;int&gt;&gt;&gt;。虽然typeid千般弱鸡，但也非一无是处，起码非侵入、平等、多态。所以，理想的反射，应该像c++原生的typeid那样无色无味：1、非侵入式的；2、可以对所有的类型都提供反射，基本类型、非Object系的struct或者class、template类型的；3、多态的，只要改类型需要运行时的类型识别，那么就返回其本身的类型（子类），而非字面上的声明类型；4、支持类型参数，也即是说，以类型传递给该函数时，就返回相应的类型信息对象。<br /><br />说得具体一点，我们要求的反射库是这样子的。当然，首先要有一个类型信息对象TypeInfo，里面装满了关于对于类型的所有详细信息。如下所示：可以猜到这种反射下框架，只支持单继承，这是故意的。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TypeInfo<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />&nbsp;Args</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;ConstructObject(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;obj,&nbsp;MemoryAllocator</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;alloc,&nbsp;Args</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; "><img src="http://www.cppblog.com/Images/dot.gif"  alt="" />&nbsp;args)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;IsDerviedOf(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">base</span><span style="color: #000000; ">)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;TIType&nbsp;GetTIType()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;InterfaceMap</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;GetInterfaces()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;jushort&nbsp;GetMemorySize()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;ConstText&nbsp;GetName()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;AString&nbsp;GetFullName()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;jushort&nbsp;GetAlignSize()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;ConstText&nbsp;GetSpaceName()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;GetBaseTypeTI()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;GetPointeedTI()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">；<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;size_t&nbsp;GetHashCode(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;obj)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;IsValueType()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;IsClass()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;DoInitAllocator(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;obj,&nbsp;MemoryAllocator</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;memAlloc)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;NeedDestruct()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;DoDefaultConstruct(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;obj)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;CanDefaultConstruct()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;DoAssign(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;dest,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;src)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;Equals(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;objA,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;objB)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;DoDestruct(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;obj)</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;};</span></div>然后，就要有一个函数TypeOf，应该是两个，一个是无参数的类型模板函数，可以这样调用，TypeOf&lt;type&gt;()；一个是有一个参数的类型模板函数，可以这样调用，TypeOf(obj)。不管是那一个，其返回结果都是const TypeInfo*。TypeOf的要做到的事情是，对于每一种类型，有且只有一个唯一的TypeInfo对象与之对应，不管是template的还是非template的；比如，以下的几个判断必须成立。<br />TypeOf&lt;int&gt;() == TypeOf&lt;int&gt;();<br />TypeOf&lt;int&gt;() == TypeOf(n);&nbsp;&nbsp; &nbsp;//n为整型<br />TypeOf&lt;vector&lt;int&gt;&gt;() == TypeOf(nums);//nums的类型为vector&lt;int&gt;<br />Object* a = new ObjectA; TypeOf(a) == TypeOf&lt;ObjectA&gt;(); <br />其实这里面的原理也没什么神奇，无非就是trait配合sfine，接下来就全部都是苦力活，就是为每一种类型都专门特化一个详细描述的类型对象，用宏可以节省大量的代码。但是整个反射库，本座前前后后重构了十几次，现在也还在重构之中，终究还是解决了开发上所遇到的各种事情。比如，序列化（支持指针、支持多态）、对象与xml的互换、对象与json的互换、数据库表读写对象、格式化、Any类型、非侵入式接口、消息发送、字符串生成对象等等。<br />其实现方式，概括起来，就是引入间接层元函数TypeInfoImp专门用于返回一个类型type，type里面有一个GetTypeInfo()的函数。然后TypeOf调用TypeInfoImp里的type的GetTypeInfo()最终得到TypeInfo对象。代码如下所示。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TypeInfoImp<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Ty&nbsp;type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;THasGetTypeInfoMethod</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::value;<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;:&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;typename&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::type&nbsp;type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::value;<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;TypeOf()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;typename&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::type&nbsp;TypeInfoProvider;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;TypeInfoProvider::GetTypeInfo();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;TypeOf(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;Ty</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;obj)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;typename&nbsp;IsRttiType</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::type&nbsp;is_rtti;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">又是间接层，对动态类型和非动态类型分别处理</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;ImpTypeOf(obj,&nbsp;is_rtti());<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TypeInfoImp&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;GetTypeInfo();<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;TypeInfoImp</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::GetTypeInfo()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;ti&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;CreateNativeTypeInfo</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">bool</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;ti;<br />&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>可能可以有简洁的方式，比如不需要引入TypeInfoImp，但是实际最终证明TypeInfoImp的方式最具灵活性也最能节省代码。最起码，它在自定义的struct或者class就很方便，只要改struct内部包含一个GetTypeInfo()的函数，它就可以被纳入TypeOf体系中，非常方便。对于模板类型的TypeInfoImp，就要用到哈希表了。比如，对于std::paira的类型信息，如下实现，<br />&nbsp;&nbsp; &nbsp;template&lt;typename FstTy, typename SndTy&gt;<br />&nbsp;&nbsp; &nbsp;struct TypeInfoImp &lt; std::pair&lt;FstTy, SndTy&gt; &gt;<br />&nbsp;&nbsp; &nbsp;{<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static const bool value = true;<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;typedef TypeInfoImp &lt; std::pair&lt;FstTy, SndTy&gt; &gt; type;<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static TypeInfo* GetTypeInfo()<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ParamsTypeInfo&lt;FstTy, SndTy&gt; args;<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return PodPair::LookupTemplateTypeInfo(args);<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />&nbsp;&nbsp; &nbsp;};<br />提取其类型参数的const TypeInfo*，生成数组。用此数组到PodPair的哈希表里面查找，如果哈希表中以有此类型数组参数的对象就返回，否则见创建一个添加一条哈希条目，然后返回。每一个泛型类型，比如vector，list，pair都有一个属于自己的哈希表。<br />打完收工。原理很简单，但是对于工业级的反射库，要考虑很多细节，比如，TypeInfo对象的内存管理；怎么为enum类型生成一堆字符串，以支持字符串和enume值的互相转换；生成并保存class的构造函数和析构函数指针；命名空间的支持；仿真C#里面的attribute；如何以最方便的方式生成成员字段或者成员函数信息等等，一句话，就是他妈的体力活。但是，回报是很丰盛的，这里的苦力活做完之后，程序的其他地方上，基本上，就没有什么重复相似的代码，一切的体力工作全部就可以压在类型信息这里了。</div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215052.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-05 11:45 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/05/215052.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>预处理之正整型</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/04/215050.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Tue, 04 Jul 2017 06:21:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/04/215050.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215050.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/04/215050.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215050.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215050.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 虽然通过一系列的奇技淫巧，让预处理也图灵完备一把，但是用预处理来做计算，真的很吃力不讨好。因为预处理一开始设计出来的目的，就没什么野心，原本就仅仅只是为了做简简单单的文本替换工作，并没有想过要成为正儿八经的编程语言，即便是最最缩水版脚本语言的功能要求都达不到。只是后来，实在是大量要求要批量自动生成代码，特别是c++11之前的版本玩什么模板元编程，铺天盖地的要有大量相似的代码。这些代码用其他工具来生成，当然形式会更加漂亮，但是始终还是用原生的预处理来做这种事情会更加的方便，否则每次修改，都要运行一遍外部工具，都麻烦啊！本人是倾向于用预处理来生成代码的。另外，c++11之后，的确原来很多需要宏来生成代码的场合已经不必要了，但是因为c++11的类型推导能力大大加强了之后，发现又有一大波地方可以用宏来生成代码了。并不是说C++中的宏是必不可少之物，但是用了宏，真的可以减少很多很多的重复代码，起码纸面上的代码清爽了很多。&nbsp;&nbsp;&nbsp;&nbsp; <div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 预处理的原生数据类型就只有符号，然后符号只支持##的并接运算，同时，预处理也能识别并接后的结果（否则，并接运算就没意义了），如果是宏函数，就进行调用操作，如果是宏符号，就替换文本，如果什么都不是，就什么都不做，保留符号。但是这样的弱鸡类型，显然远远不能满足离经叛道的码猿需要。经过大量的宏编程的尝试之后，可以很肯定一点，预处理里面只能再模拟出来一种数据类型，那就是正整数，虽然通过补码运算来仿真负数，但是由于预处理里面的符号不能包含减号(-)字符，当然要花大力气捣鼓负整数也是可以的，只是使用上也不方便也不直观，性价比不高，基本上，必须用宏来生成代码的地方，都可以不需要负整数的。<br /><br /><div>&nbsp;&nbsp;&nbsp;&nbsp; 另外，预处理也没有变量类型的概念，不要说强类型，就连弱类型也不是，完全就是无类型。正整数类型的概念全靠码猿人肉编译器来维护，一个循环的宏代码生成一般都是来来回回也不知道调用了多少层宏调用，任何一个地方出错，有时候是几吨密密麻麻的中间失败代码（编译器的预处理缓冲溢出，弃械投降），有时候就完全没有输出，没有任何一丁点的提示，简直是大海捞针的找问题。因此，在用宏循环生成代码时，必须小心翼翼，步步为营，不得不感慨，正儿八经语言里面的类型真是好东西啊。</div><br />其实，数据类型并不重要，重要的是数据上能够支持的运算集合以及这些运算能运用的场合。<br />好了，回到上文，我们用_ZPP_INC_N搞了10个数，通过复制粘贴，可以把N增加到255。实际运用中，完全足够用了。<br />#define _ZPP_INC_JOIN(_A, _B) _ZPP_INC_JOIN_IMP1(_A, _B)<br />#define _ZPP_INC_JOIN_IMP1(_A, _B) _ZPP_INC_JOIN_IMP2(~, _A##_B)<br />#define _ZPP_INC_JOIN_IMP2(p, res) res<br /><br />#define PP_INC(x, ) _ZPP_INC_JOIN(_ZPP_INC_, x)<br />#define _ZPP_INC_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<br />#define _ZPP_INC_1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2<br />#define _ZPP_INC_2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3<br />#define _ZPP_INC_3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4<br />#define _ZPP_INC_4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5<br />#define _ZPP_INC_5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6<br />#define _ZPP_INC_6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7<br />#define _ZPP_INC_7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8<br />#define _ZPP_INC_8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9<br />#define _ZPP_INC_9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10<br />...<br />#define _ZPP_INC_255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 256<br /><br />同样的方式，再如法泡制PP_DEC，从256开始，一直递减到0为止。对于大于256的数，就不支持了，那就都是未定义操作。这样子，通过PP_INC(n)，就得到n+1；而PP_DEC(n)，则是n-1。比如PP_INC(PP_DEC(9))，其结果肯定是9了。很好，这样子，在预处理中就实现了自然数自增1和自减1的运算了。另外，对于大于256的数，比如512传递给PP_INC，就只得到一个_ZPP_INC_512的符号，完全没有任何意义。<br /><br />然后，两个自然数是否相等的判断，也非常重要，必须支持。但是，在此之前，要实现一个宏函数PP_NOT，用来判断入参是否为0。为0的话，则函数返回1，否则，就返回0。也即是：<br />PP_NOT(0) == 1<br />PP_NOT(23) == 0，或者 PP_NOT(var) == 0。<br />记住，预处理提供给我们的原生类型就只有符号和##并接运算，除此之外，别无他物。好像工具太简陋，能完成目的吗？不得不佩服有些码猿的脑洞。以下代码是这样运作的，假设PP_NOT生成以下的调用形式，先不管PP_ARG1，至于符号~，是这样子的，可以看成普通的变量名字，它就是占位符，因为预处理只识别逗号(,)，和小括号，至于其他符号，完全无视，那些是C/C++编译阶段才关心的符号。<br />PP_NOT(0) = PP_ARG1(~, 1, 0)<br />PP_NOT(n) = PP_ARG1(_ZPP_NOT_n, 0)<br />然后，让PP_ARG1取第二个参数（码猿的计数是从0开始的，也即是，0即是1，1即是2），就完成任务了。至于_ZPP_NOT_n是什么鬼，那个只是中间生成的临时符号，可以舍弃。我们只需对_ZPP_NOT_0做特别处理。因此，代码可以这样写了。PP_PROBE()用以生成两个入参<br />#define PP_PROBE() ~, 1<br />#define _ZPP_NOT_0 PP_PROBE()<br />#define PP_NOT(_X, ...) PP_IS(PP_JOIN(_ZPP_NOT_, _X))<br /># define PP_IS(...) PP_ARG1(__VA_ARGS__, 0)<br /><br />这样子之后，显然PP_NOT(n)就可以变成PP_ARG1(_ZPP_NOT_n, 0)的形式了。PP_NOT不是只需一个入参吗？为何后面还要带省略号，纯粹是为了后面各种变态的运用，取悦编译器。已经用宏来写代码了，就不必再遵守什么清规戒律，只要能完成任务就行了。<br /><br />至于PP_ARG1的实现，就很简单了，如下所示，<br />#define PP_ARG0(_0, ...) _0<br />#define PP_ARG1(_0, _1, ...) _1<br />#define PP_ARG2(_0, _1, _2, ...) _2<br /><br />然后通过两次取反的函数，再补上函数PP_BOOL，如果入参&gt;0，就返回1，否则返回0，类似于整型到bool的强制类型转换。<br />#define PP_BOOL(_X, ...) PP_NOT(PP_NOT(_X))<br /><br />有了这些的铺垫之后，要比较两个自然数是否相等，就简单了。其实没什么神秘的，就是针对从0到255，重复256个以下形式的#define语句，<br />#define&nbsp;&nbsp; &nbsp;_ZPP_0_EQUALS_0&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;PP_PROBE()<br />#define&nbsp;&nbsp; &nbsp;_ZPP_1_EQUALS_1&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;PP_PROBE()<br />#define&nbsp;&nbsp; &nbsp;_ZPP_2_EQUALS_2&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;PP_PROBE()<br />...<br />#define PP_EQUALS(x, y) PP_IS(PP_CONCAT4(_ZPP_, x, _EQUALS_, y))<br />PP_EQUALS就是将入参并接成_ZPP_x_EQUALS_y的形式，只要x和y相同，也即是说，它们在上面的表格中，那么，道理就如同PP_NOT的实现那样，最后结果就是1了。其实，预处理中没有判断这种玩意，只有表格，只有并接，只有查表。所谓的图灵完备，说白了，没有玄虚的，就是建表，然后查表。对相等比较取反PP_NOT，自然就得到不相等的判断函数。<br />#define PP_UN_EQUALS(x, y) PP_NOT(PP_IS(PP_CONCAT4(_ZPP_, x, _EQUALS_, y)))<br />再次建表，就可以得到bool运算的函数，或与<br />#define PP_OR(a,b) PP_CONCAT3(_ZPP_OR_, a, b)<br />#define _ZPP_OR_00 0<br />#define _ZPP_OR_01 1<br />#define _ZPP_OR_10 1<br />#define _ZPP_OR_11 1<br /><br />#define PP_AND(a,b) PP_CONCAT3(_ZPP_AND_, a, b)<br />#define _ZPP_AND_00 0<br />#define _ZPP_AND_01 0<br />#define _ZPP_AND_10 0<br />#define _ZPP_AND_11 1<br /><br />再准备一张表格，将字节映射到8个二进制位。<br />#define _ZPP_BINARY_0&nbsp;&nbsp; &nbsp;(0, 0, 0, 0, 0, 0, 0, 0)<br />#define _ZPP_BINARY_1&nbsp;&nbsp; &nbsp;(0, 0, 0, 0, 0, 0, 0, 1)<br />#define _ZPP_BINARY_2&nbsp;&nbsp; &nbsp;(0, 0, 0, 0, 0, 0, 1, 0)<br />#define _ZPP_BINARY_3&nbsp;&nbsp; &nbsp;(0, 0, 0, 0, 0, 0, 1, 1)<br />#define _ZPP_BINARY_4&nbsp;&nbsp; &nbsp;(0, 0, 0, 0, 0, 1, 0, 0)<br />...<br />然后通过模拟计算机组成原理里面的加减乘除的原理，就可以实现四则运算了。对了，整个预处理库的代码都在压缩包上，功能比boost的预处理库强多了，但是代码却少了很多，也容易理解多了，所有代码在vs下面正常运行，其他平台还没有测试。代码包：<a href="/Files/huaxiazhihuo/preprocessor.rar" style="color: #3366ff;">/Files/huaxiazhihuo/preprocessor.rar</a></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215050.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-04 14:21 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/04/215050.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>预处理的图灵完备之引言</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/01/14/214594.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Sat, 14 Jan 2017 07:01:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/01/14/214594.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/214594.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/01/14/214594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/214594.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/214594.html</trackback:ping><description><![CDATA[<div>好久没有光顾cppblog了，现在这里这么冷清了，不免让人有些伤感，可见c++现在多么的不得人心，也可能是c++的大神去了其他的网络平台，好比知乎。不管怎么样，始终对c++还是有些感情，也对cppblog有些感情。<br /><br />我们还是来讨论c++吧，这几年在c++里面玩代码自动生成技术，而预处理是不可避免，也是不可或缺的重要工具。虽然boost pp预处理库在宏的运用上很是完善，但是代码也太多了，而且代码很不好理解，对此，不免让人疑惑，有必要搞得那么复杂，搞那么多代码吗？并且，看了boostpp的使用接口后，感觉写得很不干净，也不好组合。因此，重新做了一套预处理的轮子。以下的代码，假设在msvc2013以上的版本运行，反正很多人用MSVC的，装逼的自当别论，造出来的轮子，倾向于先支持msvc。<br /><br />首先，我们定义一个宏，用来给把入参变成字符串，咦，这个事情也太easy了，但是，在此，感觉，还是有必要废话多解释一下。以下代码惯例都是，所有可用的宏函数都是以PP开头全部大写，而以_ZPP开头的全部都是内部实现，其实还可以做得更难看一点。因为宏函数是全局的，没有作用域的概念，并且只是单纯的文本替换，死的时候，还不知道怎么死，所以，必须谨慎对待。像是windows.h头文件那样，直接用min，max作为宏的名字，虽然用起来很方便，但也不知道制造了多少麻烦，所以，很多时候，包含windows.h时，第一件事情就是undef min和max。<br /><br />以下的代码，可以随便在某个工程下，随便建立一个cpp后缀名的源文件，然后按CTRL+F7编译，不需要F5，就可以看到运行的效果，如果编译通过，就说明宏基本上正确，测试代码越多，准确性就越高。当然，你们也可以通过设置源文件的属性，让msvc生成预处理后的文件，然后用记事本打开那个文件观看。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PP_TEXT(str)&nbsp;_ZPP_TEXT(str)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_TEXT(str)&nbsp;#str</span></div>在c++预处理宏中，操作符#是将后面跟随的表达式加上两个双引号，也就是字符串。PP_TEXT(str)不是直接定义成#str，而是通过调用_ZPP_TEXT(str)，然后在那里才将入参变成字符串，显得有点辗转，有点多此一举，但，其实是为了支持宏的全方位展开，也就是入参str本身也存在宏调用的时候，纯属无奈。比如，如果这样实现<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PP_TEXT(str)&nbsp;#str</span></div>那么，对于下面的情况，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;AAA&nbsp;aaa</span></div>PP_TEXT(AAA)，结果将是"AAA",而不是"aaa"。因为宏操作符直接是将入参变成字符串，没有让入参有一点点回旋的空间，所以只好引入间接层，让入参有机会宏展开。后面，很多宏函数都是这样实现，不得不间接调用，以便让宏全面展开。而msvc的宏展开机制更加奇葩，更加不人性化，其间接调用的形式也更丑陋。这都是没办法的事情。<br />然后，为了调试宏，或者测试宏，当然，很多时候，调试宏，还是要打开预处理的文件来对比分析。我们对 static_assert作一点点包装，因为static_assert需要两个参数，c++11后面的c++版本中，static_assert好像只需要一个入参，那时就不需要这个包装了。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PP_ASSERT(<img src="http://www.cppblog.com/Images/dot.gif" alt="" />)&nbsp;static_assert((__VA_ARGS__),&nbsp;PP_TEXT(__VA_ARGS__));</span></div>PP_ASSERT(...)里面的三个点，是不定参数的宏，而__VA_ARGS__就代表了...所匹配的所有参数，这条语法很重要，要熟练。这里，就不详细解释其用法了，后面会有大把大把的宏函数用到__VA_ARGS__。<br />好了，我们可以开始用PP_ASSERT(...)做测试了。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">PP_ASSERT(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">+</span><span style="color: #000000; ">3</span><span style="color: #000000; ">==</span><span style="color: #000000; ">5</span><span style="color: #000000; ">)</span></div>如果，然后编译这个文件，发现编译通过了，比如<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">PP_ASSERT(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">+</span><span style="color: #000000; ">3</span><span style="color: #000000; ">==</span><span style="color: #000000; ">4</span><span style="color: #000000; ">)</span></div>编译的时候，就会报错误信息，error C2338: 2+3==4<br />好了，测试准备建立起来，就可以开始肆无忌惮的写代码了。一步一步地构建c预处理宏的图灵完备。<br />显然，当务之急，最根本的宏就是将两个宏参数的并接，也即是##运算符，显然好比#运算那样子，必须给里面参数有宏展开的机会，因此要间接调用，下面是其实现<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PP_JOIN(_A,&nbsp;_B)&nbsp;_ZPP_JOIN_I(_A,&nbsp;_B)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_JOIN_I(_A,&nbsp;_B)&nbsp;_ZPP_JOIN_II(~,&nbsp;_A##_B)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_JOIN_II(p,&nbsp;res)&nbsp;res</span></div>竟然不止一层间接，而是两层，又多此一举，是因为发现在做宏递归的时候，一层间接调用还不能让宏充分地展开，所以只好又加间接层，也不明白是何原因，也懒得追究了。现在，接下来，当然是测试PP_JOIN了。各位同学，可以新建立一个测试文件，那个文件include我们的这个宏函数。当然，也可以在同一个文件里面写测试代码，注意分成两段代码，上一段写宏函数，下一段写测试代码，目前来看，都可以的，后面再整理。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">PP_ASSERT(PP_JOIN(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">+</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">))<br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;A&nbsp;20</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;B&nbsp;10</span><span style="color: #000000; "><br />PP_ASSERT(PP_JOIN(A&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;B,&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">30</span><span style="color: #000000; ">))</span></div>有了PP_JOIN，就可以开始做点其他事情了。比如，计数器，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_JOIN(_A,&nbsp;_B)&nbsp;_ZPP_INC_JOIN_IMP1(_A,&nbsp;_B)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_JOIN_IMP1(_A,&nbsp;_B)&nbsp;_ZPP_INC_JOIN_IMP2(~,&nbsp;_A##_B)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_JOIN_IMP2(p,&nbsp;res)&nbsp;res</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PP_INC(x,&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" />)&nbsp;_ZPP_INC_JOIN(_ZPP_INC_,&nbsp;x)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INC_9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10</span><span style="color: #000000; "><br /><img src="http://www.cppblog.com/Images/dot.gif" alt="" /></span></div>这里，我们重新又实现了一遍PP_JOIN，这也是没办法的事情，后面在重重嵌套的时候，会出现PP_JOIN里面又包含PP_JOIN的情况，这样会导致宏停止展开了，所以，只好对于每一个要用到JOIN之处，都用自己版本的JOIN。<br />这是宏函数的实现方式，通过并接，文本替换，一一枚举，才达到这样的效果，也就是说，我们通过JOIN函数，在宏里面构造了一个计数器的数据类型。如果每个宏函数都这样写，岂不是很累。好消息是，只需用这种苦逼方式实现几个最基本的函数，然后通过宏的递归引擎，其他的宏函数就不需这样子一个一个苦逼的并接替换了。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">PP_ASSERT(PP_INC(</span><span style="color: #000000; ">9</span><span style="color: #000000; ">)</span><span style="color: #000000; ">==</span><span style="color: #000000; ">10</span><span style="color: #000000; ">)<br />PP_ASSERT(PP_INC(PP_INC(</span><span style="color: #000000; ">9</span><span style="color: #000000; ">))&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">11</span><span style="color: #000000; ">)</span></div>写测试代码习惯了，写起来就很有意思了，测试通过，也是最激动人心的时刻。<br />接下来，要处理msvc里面宏的恶心行为，然后就结束本引言。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PAIR_SECOND(x,&nbsp;y) y</span><span style="color: #000000; "><br />PP_ASSERT(PAIR_SECOND(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">20</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">20</span><span style="color: #000000; ">)</span></div>这样子，还不错，下面，再define一个宏函数，让其返回一个pair，也就是两个值<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;MAKE_PAIR(x,&nbsp;y)&nbsp;x,&nbsp;y</span></div>然后，这样调用，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">PAIR_SECOND(MAKE_PAIR(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">20</span><span style="color: #000000; ">))</span></div>编译器马上就不高兴了，warning C4003: &#8220;PAIR_SECOND&#8221;宏的实参不足<br />好像是编译器没有先展开MAKE_PAIR(10, 20)，然后再调用PAIR_SECOND，而是直接把MAKE_PAIR(10, 20)整个当成一个函数传给PAIR_SECOND，然后，PAIR_SECOND就提示实参不足，然后，硬要测试，<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">PP_ASSERT(PAIR_SECOND(MAKE_PAIR(</span><span style="color: #000000; ">10</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">20</span><span style="color: #000000; ">))&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">20</span><span style="color: #000000; ">)</span></div>显然，无论如何，编译器势必就龙颜大怒了。对此，我们只好再引入间接层，想办法让MAKE_PAIR(10, 20)先展开，然后再传给PAIR_SECOND。这样，就不能直接用这样的形式了，PAIR_SECOND(MAKE_PAIR(10, 20)) 。只好改成这样，下面的几行代码，很有点惊天地泣鬼神的味道。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_INVOKE_JOIN(_A,&nbsp;_B)&nbsp;_ZPP_IMP_INVOKE_JOIN_I(_A,&nbsp;_B)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_IMP_INVOKE_JOIN_I(_A,&nbsp;_B)&nbsp;_ZPP_IMP_INVOKE_JOIN_II(~,&nbsp;_A##_B)</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;_ZPP_IMP_INVOKE_JOIN_II(p,&nbsp;res)&nbsp;res</span><span style="color: #000000; "><br /><br /></span><span style="color: #0000FF; ">#define</span><span style="color: #000000; ">&nbsp;PP_INVOKE(m,&nbsp;args,&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" />)&nbsp;_ZPP_INVOKE_JOIN(m,&nbsp;args)</span></div>前面几行代码都是PP_INVOKE的JOIN函数实现，可以直接当它们是JOIN函数，关键是PP_INVOKE(m, args, ...)这里，第一个参数m是宏函数，第二个是args，是要传给第一个参数m的参数列表，用括号括起来，至于后面的省略号，是有些时候为了取悦编译器而添加的，也不知道是什么原因，反正这样子就可以了，懒得追究。垃圾宏，垃圾预处理，只要能完成功能就行了，c++中，代码生成代码，重头戏在tmp那里，宏只是小小必要的辅助工具而已。然后，这样调用，<br />PP_ASSERT(PP_INVOKE(PAIR_SECOND, (MAKE_PAIR(10, 20))) == 20)<br />编译通过了，好不容易啊！</div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/214594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-01-14 15:01 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/01/14/214594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>