模板类的编译时计数器

2024-01-20

想象一下,您有很多带有很多不同模板参数的类。每个类都有一个方法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()编译时所有实例化类的 s。所以,理论上应该可以生成这样一个列表(aconst std::array<p, S> L和一些S) 作为编译时常量列表。但如何呢? (也欢迎 C++0x 解决方案)。


为什么我需要这个?

在只有 256 kB(用于代码和数据)的架构上,我需要为传入的类 id 生成对象。现有的序列化框架或上面的运行时解决方案过于庞大。如果没有模板,编译时解决方案会很容易,但我想保留模板提供的所有优势。


Manually

您可以做的最简单的事情就是手动滚动代码,我不认为模板可以为您带来太多好处,所以我将使用普通类,其中A, B...代表您类型的特定实例。这允许在编译时初始化类型,但代价是每当将新类型添加到系统时必须记住更新查找表:

typedef void (*function_t)();
function_t func[] = {
    &A::f,
    &B::f,
    &C::f
};

从维护的角度来看,我会推荐这个。系统自动化将使代码将来更难理解和维护。

Macros

最简单、最自动化的宏生成系统可能会生成更少的代码,它只使用宏。由于第一种方法将大量使用宏,因此我将自动生成函数,就像您在上一个问题中所做的那样。如果您(希望)放弃了通过宏生成完整代码的路径,则可以删除该部分代码。

为了避免在不同上下文中重新输入类型名称,您可以使用任何上下文所需的所有数据定义一个宏,然后使用其他宏来过滤每个特定上下文中要使用的内容(以及如何使用):

// This is the actual list of all types, the id and the code that you were
// generating in the other question for the static function:
#define FOREACH_TYPE( macro ) \
    macro( A, 0, { std::cout << "A"; } ) \
    macro( B, 1, { std::cout << "B"; } ) \
    macro( C, 2, { std::cout << "C"; } )

// Now we use that recursive macro to:
// Create an enum and calculate the number of types used
#define ENUM_ITEM( type, id, code ) \
    e_##type,
enum AllTypes {
    FOREACH_TYPE( ENUM_ITEM )
    AllTypes_count
};
#undef ENUM_ITEM

// Now we can create an array of function pointers
typedef void (*function_t)();
function_t func[ AllTypes_count ];

// We can create all classes:
#define CREATE_TYPE( type, the_id, code ) \
struct type {\
   static const int id = the_id; \
   static void func() code\
};
FOREACH_TYPE( CREATE_TYPE )
#undef CREATE_TYPE

// And create a function that will 
#define REGISTER_TYPE( type, id, code ) \
    func[ i++ ] = &type::func;

void perform_registration() {
   int i = 0;
   FOREACH_TYPE( REGISTER_TYPE );
};
#undef REGISTER_TYPE

// And now we can test it
int main() {
   perform_registration();
   for ( int i = 0; i < AllTypes_count; ++i ) {
      func[ i ]();
   }
}

另一方面,这是维护的噩梦,非常脆弱且难以调试。添加新类型很简单,只需在其中添加新行即可FOREACH_TYPE宏,你就完成了......一旦出现问题,祝你好运......

模板和元编程

另一方面,使用模板您可以接近,但无法到达类型的单点定义。您可以通过不同的方式自动执行某些操作,但至少您需要定义类型本身并将它们添加到类型列表中以获得其余功能。

使用 C++0x 代码简化实际 type_list 的定义,您可以从定义类型开始,然后创建type_list。如果您想避免使用 C++0x,请查看 Loki 库,但对于 C++0x,类型列表非常简单:

template <typename ... Args> type_list {}; // generic type list
typedef type_list< A, B, C, D > types;     // our concrete list of types A, B, C and D
                                           // this is the only source of duplication:
                                           // types must be defined and added to the
                                           // type_list manually [*]

现在我们需要使用一些元编程来操作类型列表,例如我们可以计算列表中元素的数量:

template <typename List> struct size;     // declare
template <typename T, typename ... Args>  // general case (recursion)
struct size< type_list<T,Args...> > {
   static const int value = 1 + size< type_list<Args...>::value;
};
template <>                               // stop condition for the recursion
struct size< type_list<> > {
   static const int value = 0;
};

拥有类型列表的大小是我们问题的第一步,因为它允许我们定义一个函数数组:

typedef void (*function_t)();                 // signature of each function pointer
struct registry {
   static const int size = ::size< types >::value;
   static const function_t table[ size ];
};
function_t registry::table[ registry::size ]; // define the array of pointers

现在我们想要注册该数组中每个特定类型的静态函数,为此我们创建一个辅助函数(封装为类型中的静态函数以允许部分特化)。请注意,这个具体部分设计为在初始化期间运行:它不会是编译时间,但成本应该微不足道(我会更担心所有模板的二进制大小):

template <typename T, int N>                         // declaration
struct register_types_impl;
template <typename T, typename ... Args, int N>      // general recursion case
struct register_types_impl< type_list<T,Args...>, N> {
   static int apply() {
      registry::table[ N ] = &T::f;                  // register function pointer
      return register_types_impl< type_list<Args...>, N+1 >;
   }
};
template <int N>                                     // stop condition
struct register_types_impl< type_list<>, int N> {
   static int apply() { return N; }
};
// and a nicer interface:
int register_types() {
   register_types_impl< types, 0 >();
}

现在我们需要一个 id 函数将我们的类型映射到函数指针,在我们的例子中是类型列表中类型的位置

template <typename T, typename List, int N>      // same old, same old... declaration
struct id_impl;
template <typename T, typename U, typename ... Args, int N>
struct id_impl< T, type_list<U, Args...>, N > {  // general recursion
   static const int value = id_impl< T, type_list<Args...>, N+1 >;
};
template <typename T, typename ... Args, int N>  // stop condition 1: type found
struct id_impl< T, type_list<T, Args...>, N> {  
   static const int value = N;
};
template <typename T, int N>                     // stop condition 2: type not found
struct id_impl< T, type_list<>, N> {
   static const int value = -1;
}
// and a cleaner interface
template <typename T, typename List>
struct id {
   static const int value = id_impl<T, List, 0>::value;
};

现在您只需要在运行时触发注册,然后再执行任何其他代码:

int main() {
   register_types(); // this will build the lookup table
}

[*] 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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

模板类的编译时计数器 的相关文章

随机推荐

  • 连接两个 Pandas 数据框

    请给出两个数据框 DF1 A B a1 b1 a2 b2 a3 b3 DF2 C1 C2 C3 0 0 1 我想做以下 DF1 DF2 产生以下结果 A B C1 C2 C3 a1 b1 0 0 1 a2 b2 0 0 1 a3 b3 0
  • 打开Refine - 将另一个文件添加到现有项目中

    我已将 CSV 文件导入到 OR Open Refine 由于我的 CSV 文件包含超过 200 000 条记录 我决定创建单独的文件 因为上传大文件在我的计算机中无法工作 需要很长时间 甚至不确定它是否真正导入 我能够从单个文件 大 中创
  • 相当于 JavaFX8 的 JGraph?

    我想将一个使用旧版本 JGraph 的旧 swing 工具移植到 JavaFX8 然而 由于 JGraph 是一个基于 Swing 的库 因此也考虑替换它 那么 是否有类似于 JGraph 的东西 但与 JavaFX8 一起使用 那么 是否
  • 将类添加到特定类名上的 .hover 功能 - jQuery

    因此 在我的整个文档中 我希望每次用户将鼠标悬停在具有特定类名称的元素上时 我都希望添加一个类 我的 CSS 看起来像这样 hotspot hover hotspothover border 4px solid fff box shadow
  • 查找并重命名所有文件扩展名不正确的图片

    我正在寻找一种方法来自动重命名所有文件扩展名错误的图像 到目前为止 我至少找到了如何获取所有这些文件的列表 find media folder name jpg exec file grep PNG GIF gt foobar txt fi
  • 在scala中玩框架表单验证

    scala 中 play 框架表单验证的工作 跟随我的 Signup 对象 它在 mapping missing 对象表单中方法映射的参数 遵循此方法与 如果你想 将其视为部分应用函数 case class UserRegistration
  • getAllCellInfo 在 android 4.2.1 中返回 null

    我的 Android 版本是 4 2 1 我正在尝试使用TelephonyManager getAllCellInfo 方法 在我的清单文件中我有ACCESS COARSE UPDATES ACCESS COARSE LOCATION AC
  • Jquery Ajax 中的函数作为参数

    是否可以将函数放入 Jquery Ajax 的参数中 如下所示 dataType 和 data 作为函数给出 如果返回类型为 JSON 则 dataType 返回 JSON 值 如果 isJson 为 false 则返回文本 dataVal
  • 裸机 RISC-V CPU - 处理器如何知道从哪个地址开始获取指令?

    我正在设计自己的 RISC V CPU 并且已经能够实现一些指令代码 我已经安装了 RV32I 版本的 GCC 编译器 所以我现在有了汇编器riscv32 unknown elf as可用的 我正在尝试仅用一条指令来汇编一个程序 simpl
  • 尝试理解异步操作子类

    我正在尝试开始使用Operation在一个副项目中 而不是在我的网络代码中散布基于闭包的回调 以帮助消除嵌套调用 所以我在读一些关于这个主题的书 然后我发现this https gist github com calebd 93fa3473
  • 在每个应用程序的事件循环迭代上执行槽

    如何在应用程序事件循环的每次迭代中调用我的槽 我知道的唯一方法是使用 QTimer 并且在每次超时 每毫秒 信号时我可以调用我的插槽 但我不喜欢这个选项 它看起来像是解决方法 有什么建议如何更正确地做到这一点 来自 Qt 4 7 QCore
  • META“过期”标签

    因此 使用 FF 中的 pagespeed 我能够找到一些关于如何减少页面加载时间的区域 其中提到的部分使用 META 过期 标签 它列出了我的所有 CSS 和 JS 文件以及 IMG 文件 现在 如果我只是将此标记合并到我网站的主页 in
  • Java - “\n”是什么意思? [复制]

    这个问题在这里已经有答案了 我用 Java 创建了一个二维数组 我正在寻找一种在控制台上打印它的方法 以便我可以确认我正在制作的东西是正确的 我在网上找到了一些为我执行此任务的代码 但我对代码的特定部分的含义有疑问 int n 10 int
  • 在 ClearCase 中手动撤消交付

    我目前在 ClearCase 中从子流到父级的传输被提升 如果我尝试撤消交付 它会告诉我不能 因为 集成活动已签入 或 签入版本 如果我尝试恢复交付 它会说尝试签出或合并元素时遇到错误 但没有具体告诉我是哪一个 所以我正在寻找一种方法 手动
  • Ping google 关于分页站点地图 django

    I have sitemap xml有 150k 行 我正在使用分页 所以有sitemap xml p 1 sitemap xml p 2 etc 我应该如何使用 Django 向 Google 告知这些页面 或者谷歌会发现sitemap
  • 删除常规数组的元素

    我有一个 Foo 对象数组 如何删除数组的第二个元素 我需要类似的东西RemoveAt 但对于常规数组 如果您不想使用列表 var foos new List
  • 在Python中使用正则表达式解析PDF文件

    我正在尝试使用 Python 的 re 模块解析 PDF 文件中的一些对象元素 我的目标是使用正则表达式解析每个 PDF 对象 PDF 对象示例如下 1 0 obj lt lt Type Catalog Pages 2 0 R gt gt
  • Java char 数组转int

    是否可以转换char 包含数字的数组 一个int 是否char 包含组成数字数字的 unicode 字符 在这种情况下 只需从char 并使用 Integer parseInt char digits 1 2 3 int number In
  • 如何在android中使用volley下载视频文件?

    我想使用 volley 库下载视频 我正在使用 volley 库来处理应用程序中的所有网络调用 首先 我们需要创建一个扩展 Volley Request 类的自定义类 要下载文件数据 我们可以创建自定义字节数组请求 该字节数组可以转换为输入
  • 模板类的编译时计数器

    想象一下 您有很多带有很多不同模板参数的类 每个类都有一个方法static void f 你想将所有这些函数指针收集到一个列表 L 中 运行时解决方案很简单 typedef void p std vector p L int reg p x