宏重复几乎相同的代码

5

我需要根据编译时常量参数编写相同的代码,类似于:

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) - Mircea Ispas
模板和循环可能可以通过编译器进行优化。 - Alex Chamberlain
vecmap 是什么?(为了明确起见)您能否编写一个您打算使用的函数示例,以便我们在反思时有一个起点;因为目前,我担心不是很清楚 :x - Matthieu M.
3个回答

4

除非您有极其严格的性能要求,否则不必担心运行时开销。插入操作无论如何都将在运行时执行,而插入时间肯定会支配更改字符串中字符所需的时间。

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

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* - 即使未声明为const,“text 0”也是const char*。 - Mircea Ispas
1
@Felics:字符串字面量"text 0"的类型是const char [],但你正在用它来初始化一个可修改的char数组。你没有修改原始字面量,所以这里没有UB(未定义行为)。如果您的模式更复杂,可以使用std::string - Andy Prowl
即使您将其声明为非const,也存在从const到非const的转换(2003标准接受,2011标准不接受)。此外,N可以有多个数字。 - Mircea Ispas
1
@Felics:是的,我注意到你已经提出了这个解决方案。我的回答主要是告诉你,你提出的解决方案很有道理,而且你不应该进行过早的优化。除非你有关注性能的原因,否则总是选择最简单的解决方案。 - Andy Prowl
通常我会询问是否可能。每个问题都给了我不同问题的新见解。我已经实现了这个解决方案(虽然不如这里高效,但也是一种胜利!)。 - Mircea Ispas
显示剩余8条评论

0

我相信以下代码应该可以正常工作:

#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)。当然,代码也可以在没有优化的情况下工作(但这只是编译时条件消除)。 - Angew is no longer proud of SO
那我不太明白为什么是 <= n,难道不应该是 == n 吗?(无论如何,我也不确定我是否理解了问题...) - Matthieu M.
@MatthieuM。我理解这个问题的方式(我承认它可能不够清晰)是doTheWork<n>应该设置条目0、1、...n。无论如何,这就是我回答中的代码所做的事情。 - Angew is no longer proud of SO

0

对我来说,重复似乎不是问题,但将 int 转换为字符串并进行后续连接的编译时转换确实是个问题。可以通过以下技术解决重复问题(未经测试):

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接