【问题标题】:Macro to repeat almost the same code重复几乎相同代码的宏
【发布时间】:2023-03-27 12:27:01
【问题描述】:

我必须根据编译时间常数参数编写相同的代码,例如:

map["text 0"] = vec[0];
map["text 1"] = vec[1];
...
map["text n"] = vec[n];

问题是我在编写代码时不知道n,我将它作为模板参数接收。显而易见的解决方案是使用一个循环并在循环内生成"text k" 并使用vec[k],但是当它应该在编译时完成时,这会产生运行时开销。另一种解决方案是将函数专门用于不同的N 值,但这样我将不得不多次手动编写相同的代码,并且没有理由将其设为模板。

我知道有一些智能宏可以重复类似的事情 N 次(例如 BOOST_PP_REPEAT 宏系列),但我找不到针对我的特定问题的解决方案。

你有解决这个问题的办法吗?

【问题讨论】:

  • @jrok 是的,但不是所有功能(VS2010 和最新的 Xcode)
  • 模板和循环可能会被编译器优化。
  • 什么是vec,什么是map? (为了清楚起见)您能否写一个您打算使用的函数的示例,以便我们在反思中有一个起点;因为目前,恐怕还不是很清楚:x

标签: c++ c-preprocessor


【解决方案1】:

除非您有非常、非常严格的性能限制,否则没有理由担心运行时开销。无论如何,插入都将在运行时执行,而插入时间肯定会支配更改字符串中的字符所需的时间。

此外,宏很难调试和维护:尽可能避免使用它们。在这里,我建议展开一个简单的循环:

std::string s = "text 0";
std::map<std::string, int> m;
for (int i = 0; i < N; i++)
{
    m[s] = vec[i];
    s[5] = '1' + i; // This is going to be the run-time overhead...
}

如果您的数字增长到高于9,在C++11 中您可以使用to_string() 函数将整数转换为字符串:

std::string const s = "text ";
std::map<std::string, int> m;
for (int i = 0; i < N; i++)
{
    m[s + std::to_string(i)] = vec[i];
}

如果性能被证明是一个问题,那么您可以尝试基于宏的更核心的方法。但是,如果您的测量结果不会显示显着的开销,则更喜欢简洁明了并展开一个简单的循环。

【讨论】:

  • 这将触发未定义的行为,我不能修改 const char* - “text 0” 是 const char*,即使它没有声明为 const。
  • @Felics:字符串文字"text 0" 的类型为const char [],但这就是您用来初始化char 数组的方法,它是可修改的。您没有修改原始文字,因此这里没有 UB。如果你的模式比较复杂,可以使用std::string
  • 即使声明为非常量,也存在从常量到非常量的转换(2003 标准接受,2011 标准不接受)。 N 也可以有多个数字。
  • @Felics:用非常量的一个副本初始化一个常量是完全有效的。例如。 int a = 5; const int b = a;.
  • @Felics:是的,我注意到你已经提出了这个作为解决方案。我的回答主要是告诉你,你提出的方案是有道理的,不要过早优化。除非您有理由关心性能,否则请始终选择最简单的解决方案。
【解决方案2】:

我相信以下应该可行:

#include <boost/preprocessor.hpp> 
//... or just the required sub-headers

// Will generate code for 0, 1, ... (N_END - 1)
#define N_END 10

#define ONE_ASSIGNMENT(maZ, maIter, maData) \
    if (maIter <= n) map["text " BOOST_PP_STRINGIZE(maIter)] = vec[maIter];

BOOST_PP_REPEAT(N_END, ONE_ASSIGNMENT, %%) //this generates the code

#undef ONE_ASSIGNMENT
#undef N_END

请注意,if() 将文字与模板参数 (n) 进行比较,因此任何值得称道的优化器都会从中生成无分支代码。

我使用%% 作为“这个值从未使用过”。它被传递到 maData 参数中,所以如果你有一些有用的东西可以实际传递(例如 "text "),你可以这样做。

【讨论】:

  • @MatthieuM。是的。没关系。我什至依靠这个来使优化器将if() 变成只是主体或nop,因为它知道文字(maIter)和编译时常量(n)健康)状况。当然,代码在没有优化的情况下也可以工作(但它实际上只是编译时条件消除)。
  • 那我不太明白为什么&lt;= n,不应该是== n吗? (我不确定我是否理解这个问题......)
  • @MatthieuM。我理解这个问题的方式(我承认它可能更清楚)是doTheWork&lt;n&gt; 应该设置条目 0、1、... n。无论如何,这就是我答案中的代码所做的。
【解决方案3】:

重复对我来说似乎不是问题,但是 int 到 string 的编译时转换和随后的连接会。重复问题可以通过以下技术解决(未经测试):

template<k,l> struct fill_vector {
    static void doIt (... & vec) {
        vec [INT_TO_TEXT (k)] = k;
        fill_vector<k+1,l-1>::doIt (vec);
    }
};

template<k> struct fill_vector<k,0> {
    static void doIt (... & vec) {
        vec [INT_TO_TEXT (k)] = k;
    }
};

//...

fill_vector<0,n>::doIt (vec);

也许有人知道如何实现INT_TO_TEXT

【讨论】:

    猜你喜欢
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 2012-02-18
    • 2017-03-15
    相关资源
    最近更新 更多