模板类的编译时计数器
想象一下,您有很多带有很多不同模板参数的类。每个类都有一个方法static void f()
。您希望将所有这些函数指针收集在列表 L 中。
运行时解决方案很简单:
typedef void (*p)();
std::vector<p> L;
int reg (p x) { static int i = 0; L.push_back(x); return i++; } // also returns an unique id
template <typename T> struct regt { static int id; };
template <typename T> int regt<T>::id = reg (T::f);
template < typename ... T > struct class1 : regt< class1<T...> > { static void f(); };
template < typename ... T > struct class2 : regt< class2<T...> > { static void f(); };
// etc.
编译器在编译时知道所有实例化类的所有 f()
。因此,理论上应该可以生成这样一个列表(一个 const std::array
L 和一些 S
)作为编译时常量列表。但如何呢? (也欢迎 C++0x 解决方案)。
为什么我需要这个?
在只有 256 kB(用于代码和数据)的架构上,我需要为传入的类 id 生成对象。现有的序列化框架或上面的运行时解决方案过于庞大。如果没有模板,编译时解决方案会很容易,但我想保留模板提供的所有优势。
Imagine that you have a lot of classes with a lot of different template parameters. Every class has a method static void f()
. You want to collect all these function pointers in a list L.
A run-time solution would be easy:
typedef void (*p)();
std::vector<p> L;
int reg (p x) { static int i = 0; L.push_back(x); return i++; } // also returns an unique id
template <typename T> struct regt { static int id; };
template <typename T> int regt<T>::id = reg (T::f);
template < typename ... T > struct class1 : regt< class1<T...> > { static void f(); };
template < typename ... T > struct class2 : regt< class2<T...> > { static void f(); };
// etc.
The compiler knows all f()
s of all instantiated classes at compile-time. So, theoretically it should be possible to generate such a list (a const std::array<p, S> L
with some S
) as a compile-time constant list. But how? (C++0x solutions are welcome, too).
Why do I need this?
On an architecture with only 256 kB (for code and data), I need to generate objects for incoming ids of classes. Existing serialization frameworks or the run-time solution above are unnecessarily big. Without templates a compile-time solution would be easy, but I want to keep all the advantages templates offer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
手动
您可以做的最简单的事情就是手动滚动代码,我不认为模板可以为您带来太多好处,所以我将使用普通类,其中
A
、B
...代表类型的特定实例。这允许在编译时初始化类型,但代价是每当将新类型添加到系统时必须记住更新查找表:从维护的角度来看,我建议这样做。系统自动化将使代码将来更难理解和维护。
宏
最简单、最自动化的宏生成系统可能会生成更少的代码,它只使用宏。由于第一种方法将大量使用宏,因此我将自动生成函数,就像您在上一个问题中所做的那样。如果您(希望)放弃了通过宏生成完整代码的路径,则可以删除该部分代码。
为了避免在不同上下文中重新输入类型名称,您可以使用任何上下文所需的所有数据定义一个宏,然后使用其他宏来过滤每个特定上下文中要使用的内容(以及如何使用)
:另一方面,这是一场维护噩梦,非常脆弱且难以调试。添加新类型很简单,只需向
FOREACH_TYPE
宏添加一个新行即可完成...一旦失败,祝您好运...模板和元编程
另一方面,使用模板您可以接近,但无法到达类型的单点定义。您可以通过不同的方式自动执行某些操作,但至少您需要定义类型本身并将它们添加到类型列表中以获得其余功能。
使用 C++0x 代码简化实际 type_list 的定义,您可以从定义类型开始,然后创建
type_list
。如果你想避免使用 C++0x,那么看看 Loki 库,但是使用 C++0x 类型列表就足够简单了:现在我们需要使用一些元编程来操作类型列表,我们可以示例计算列表中的元素数量:
拥有类型列表的大小是我们问题的第一步,因为它允许我们定义函数数组:
现在我们要注册其中每个特定类型的静态函数数组,为此我们创建一个辅助函数(封装为类型中的静态函数以允许部分特化)。请注意,这个具体部分设计为在初始化期间运行:它不会是编译时间,但成本应该微不足道(我会更担心所有模板的二进制大小):
现在我们需要一个映射的 id 函数我们的类型到函数指针,在我们的例子中是类型在类型列表中的位置
现在你只需要在运行时触发注册,在任何其他代码之前:
[*] 好吧..某种程度上,您可以使用宏技巧来重用类型,因为宏的使用是有限的,所以维护/调试不会那么困难。
Manually
The simplest thing that you can do is just roll the code manually, I don't think that there is much that can be used to your advantage from the templates, so I will use plain classes, where
A
,B
... stand for particular instantiations of your types. That allows for compile time initialization of the types, at the cost of having to remember to update the lookup table whenever a new type is added to the system:I would recommend this, from a maintenance point of view. Automating the system will make the code much harder to understand and maintain in the future.
Macros
The simple most automated one, which will probably generate less code is a macro generation system is just using macros. Since this first approach will use extensive use of macros, I will generate the functions automatically, as you did in the previous question. You can remove that part of code if you have (hopefully) given up the path of full code generation through macros.
To avoid having to retype the names of the types in different contexts you can define a macro with all the data you need for any context, and then use other macros to filter what is to be used (and how) in each particular context:
This is, on the other hand a maintenance nightmare, quite fragile and hard to debug. Adding new types is trivial, just add a new line to the
FOREACH_TYPE
macro and you are done... and the best of lucks once something fails...Templates and metaprogramming
On the other hand, using templates you can get close but you cannot get to the single point of definition for the types. You can automate some of the operations in different ways, but at the very least you will need to define the types themselves and add them to a typelist to get the rest of the functionality.
Simplifying the definition of the actual type_list with C++0x code you can start by defining the types and then creating the
type_list
. If you want to avoid using C++0x, then take a look at the Loki library, but with C++0x a type list is simple enough:Now we need to use some metaprogramming to operate on the type list, we can for example count the number of elements in the list:
Having the size of the type list is a first step in our problem, as it allows us to define an array of functions:
Now we want to register the static functions from each particular type in that array, and for that we create an auxiliar function (encapsulated as a static function in a type to allow for partial specializations). Note that this concrete part is designed to be run during initialization: it will NOT be compile time, but the cost should be trivial (I would be more worried on the binary size with all the templates):
Now we need an id function that maps our types to the function pointer, which in our case is the position of the type in the type list
Now you just need to trigger the registration at runtime, before any other code:
[*] Well... sort of, you can use a macro trick to reuse the types, as the use of macros is limited, it will not be that hard to maintain/debug.
这就是你的错误。编译器对其他编译单元中的模板实例化一无所知。现在应该很明显为什么实例化的数量不是可以用作模板参数的常量整数表达式(如果
std::array
是专门化的呢?前面的问题停止了!)There's your mistake. The compiler knows nothing about template instantiations in other compilation units. It should now be pretty obvious why the number of instantiations isn't a constant integral expression that could be used as a template argument (and what if
std::array
was specialized? Halting Problem ahead!)