草庐IT

c++ - 消除 C++ 中的递归模板实例化

coder 2023-11-14 原文

我想定义一个可以在不同地方(在文件范围内)调用的宏,以便创建可以执行某些操作的函数。 (在下面的示例中,函数只是打印一条消息,但当然我的真正意图是做一些其他有用的事情。)挑战在于我想要一些“管理器”功能(在我的示例中,它只是 main() )以某种方式成功地将它们全部调用(以任何顺序),而没有任何代码依赖于宏调用(当然,宏调用本身除外)。我的意思是,一旦文件被写入,另一个程序员将​​能够在不同的地方插入一些新的宏调用或删除一些现有的调用,并且代码仍然可以工作而无需进一步更改。我意识到这可以使用静态对象来完成,但我想探索一种不同的方法。我将使用一些模板技巧和事实__LINE__是单调递增的。

#include <iostream>
using namespace std;

template<int i>
inline void f()
{
   f<i-1>();
}

#define START_REGISTRATION                                \
template<>                                                \
inline void f<__LINE__>() {}  /* stop the recursion */    \
template<> void f<__LINE__>()  /* force semicolon */

#define REGISTER(msg)                                     \
template<>                                                \
inline void f<__LINE__>()                                 \
{                                                         \
   cout << #msg << endl;                                  \
   f<__LINE__ - 1>();                                     \
}                                                         \
template<> void f<__LINE__>()  /* force semicolon */

// Unrelated code ...

START_REGISTRATION;

// Unrelated code ...

REGISTER(message 1);

// Unrelated code ...

REGISTER(message 2);

// Unrelated code ...

REGISTER(message 3);

// Unrelated code ...

// manager function (in this case main() )
int main()
{
   f<__LINE__>();
}

这打印
message 3
message 2
message 1

正如预期的那样。

该解决方案有一些缺点。
  • 无法调用 REGISTER在同一条线上两次。
  • 如果 #line 会中断被玩。
  • 需要在 REGISTER 的所有调用之后放置经理.
  • 由于递归实例化而增加了编译时间。
  • 除非 f 的“虚拟”实例化都内联了,运行时的调用堆栈深度将与 START_REGISTRATION; 之间的行数一样大和 f<__LINE__>();在经理。
  • 代码膨胀:除非 f 的“虚拟”实例化都内联了,实例化的数量同样会很大。
  • 过多的实例化递归深度可能会达到编译器的限制(我的系统默认为 500)。

  • 问题 1-4 我并不介意。通过让每个函数返回一个指向前一个函数的指针,并让管理器使用这些指针来迭代地调用函数而不是让它们相互调用,可以消除问题 5。可以通过创建类似的类模板构造来消除问题 6,该构造能够为 REGISTER 的每次调用进行计算。在前一次调用中实例化了哪个函数,因此只实例化了实际执行某些操作的函数。过多的实例化会从函数模板转移到类模板,但类实例化只会给编译器带来负担;他们不会触发任何代码生成。所以我真正关心的是问题 7,问题是:有没有办法重构事物,以便编译器迭代而不是递归地进行实例化。我也对完全不同的方法持开放态度(除了那些涉及静态对象的方法)。一个简单的解决方案是在管理器之前将所有注册组合在一起(或添加 STOP_REGISTRATION 宏以结束注册块),但这会破坏我的主要目的(在定义它的代码旁边注册内容)。

    编辑:有一些有趣的建议,但恐怕我没有明确我希望实现的目标。我真的对两件事很感兴趣:解决所提出的问题(即,没有静态,每个注册单行,添加/删除注册时没有额外的更改,虽然我忽略了这么说,但只有标准 C++ --- 因此,没有提升)。正如我在下面的评论中所说,我的兴趣本质上更多是理论性的:我希望学习一些新技术。因此,我真的很想专注于重组事物,以便消除(或至少减少)递归或找到满足我上面列出的约束的不同方法。

    编辑 2: MSalter 的解决方案向前迈出了一大步。一开始我以为每次注册都会产生排队的全部成本,但后来我意识到当然一个函数只能被实例化一次,所以在实例化方面我们付出与线性搜索相同的代价,但是递归深度变为对数。如果我解决了这个问题,我会发布一个完整的解决方案,消除问题 5-7。不过,看看它是否可以在恒定的递归深度下完成仍然很好,最好是实例化数量与调用数量成线性关系(a-la the boost solution)。

    编辑 3:这是完整的解决方案。
    #define START_REGISTRATION                                          \
    template<int lo, int hi>                                            \
    struct LastReg {                                                    \
      enum {                                                            \
         LINE_NUM = LastReg<(lo + hi)/2 + 1, hi>::LINE_NUM ?            \
            static_cast<int>(LastReg<(lo + hi)/2 + 1, hi>::LINE_NUM) :  \
            static_cast<int>(LastReg<lo, (lo + hi)/2>::LINE_NUM)        \
      };                                                                \
    };                                                                  \
    template<int l>                                                     \
    struct LastReg<l, l> {                                              \
       enum { LINE_NUM = 0 };                                           \
    };                                                                  \
    template<int l>                                                     \
    struct PrevReg {                                                    \
       enum { LINE_NUM = LastReg<__LINE__ + 1, l - 1>::LINE_NUM };      \
    };                                                                  \
    template<int l> void Register() {}                                  \
    template<int l> void Register()  /* force semicolon */
    
    
    #define REGISTER(msg)                                               \
    template<>                                                          \
    struct LastReg<__LINE__, __LINE__> {                                \
       enum { LINE_NUM = __LINE__ };                                    \
    };                                                                  \
    template<>                                                          \
    void Register<__LINE__>()                                           \
    {                                                                   \
       cout << __LINE__ << ":" << #msg << endl;                         \
       Register<PrevReg<__LINE__>::LINE_NUM>();                         \
    }                                                                   \
    template<> void Register<__LINE__>()  /* force semicolon */
    
    
    #define END_REGISTRATION                                            \
    void RegisterAll()                                                  \
    {                                                                   \
       Register<PrevReg<__LINE__>::LINE_NUM>();                         \
    }                                                                   \
    void RegisterAll()  /* force semicolon */
    
    
    START_REGISTRATION;
    
    REGISTER(message 1);
    
    REGISTER(message 2);
    
    END_REGISTRATION;
    
    
    int main()
    {
       RegisterAll();
    }
    

    最佳答案

    您面临的问题是您正在对 f<i> 进行线性搜索。 ,这会导致过多的实例化。

    解决办法是让f<i>调用 g<i,0> .这反过来调用 g<i,i/2>g<i/2,0> , 拨打 g<i,i/2+i/4> , g<i/2+i/4,i/2> , g<i/2,i/4>g<i/4, 0>异端。你当然会专攻 g<__LINE__, __LINE__>REGISTER() .

    实例化 f<65536>仍然会导致 65536 次模板实例化(您有效地检查了所有先前的 65536 行),但递归深度仅限于 log(65536) 或 16 个级别。这是可行的。

    关于c++ - 消除 C++ 中的递归模板实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6150787/

    有关c++ - 消除 C++ 中的递归模板实例化的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    8. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

      我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

    9. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

      我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

    10. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

      在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

    随机推荐