C++ 让宏将迭代参数传递到通过宏变量提供的代码体中?

C++ 让宏将迭代参数传递到通过宏变量提供的代码体中?,c++,macros,C++,Macros,本质上,我想在更改单个变量的值时重复一行代码,就像基本for循环一样 现在我在看这个: #define UNROLL2(body) n = 0; body n++; body int n; UNROLL2(std::cout << "hello " << n << "\n";) #定义展开2(主体)n=0;body n++;身体 int n; 展开2(std::cout在您的问题中,您展示了一个执行控制台I/O的示例,我评论说控制台I/O的开销远远大于循环

本质上,我想在更改单个变量的值时重复一行代码,就像基本for循环一样

现在我在看这个:

#define UNROLL2(body) n = 0; body n++; body 
int n;
UNROLL2(std::cout << "hello " << n << "\n";)
#定义展开2(主体)n=0;body n++;身体
int n;

展开2(std::cout在您的问题中,您展示了一个执行控制台I/O的示例,我评论说控制台I/O的开销远远大于循环构造(条件分支)的开销,因此展开这类内容没有什么意义。在这种情况下,智能优化器可能不会展开,因为代码大小的增加不会带来速度上的好处。但是,根据您的后续评论,这似乎只是一个小小的一次性示例,我不应该过多关注关于细节

事实上,我完全理解您所说的MSVC不展开循环。即使启用了优化,它也不会展开循环,除非您使用配置文件引导的优化。一些琐碎的事情如下:

void Leaf();

void MyFunction()
{
    for (int i = 0; i < 2; ++i)  { Leaf(); }
}
即使在
/O2
,这也太可怜了

我不久前发现了这一点,并查看它是否已被报告为缺陷。不幸的是,Microsoft最近对Connect中的所有旧bug进行了大规模清除,因此您无法在档案中追溯到很远的地方,但我确实发现了这一点。这一点由于与Intrinsic相关而被关闭,这要么是一个误解,要么是一个错误因此,基于上面显示的代码,我仍在等待有意义的响应。就优化而言,这对我来说似乎是一个很容易实现的结果,所有竞争对手的编译器都会这样做,所以这对微软的编译器来说是非常尴尬的

所以,是的,如果你不能切换编译器,PGO也帮不了你(或者你也不能使用它),我完全理解您为什么想要进行某种类型的手动展开。但我并不真正理解您为什么厌恶模板。使用模板的原因并不是轻视宏,而是因为它们提供了更干净、更强大的语法,同时也同样保证在编译时对它们进行计算/扩展。

您可以选择以下内容:

template <int N>
struct Unroller
{
   template <typename T>
   void operator()(T& t)
   {
      t();
      Unroller<N-1>()(t);
   }
};

template <>
struct Unroller<0>
{
   template <typename T>
   void operator()(T&)
   { }
};
因此,借助递归模板扩展的魔力,您可以:

void MyFunction()
{
   MyOperation op;
   Unroller<16>()(op);
}
当然,这是一个简单的例子,但它向您展示了优化器甚至可以在这里进行尾部调用优化。因为模板魔术与函子一起工作,正如我前面所说的,您可以使逻辑按需要展开,添加成员变量,等等。由于模板是展开的,所以所有的逻辑都会展开在编译时递归地重新编译

从字面上说,我能找到的唯一缺点是,所有模板扩展都会使目标文件膨胀一点。在这种情况下,使用
Unroller
,我在目标文件中发出17种不同的函数定义。但是,除了对编译时间的轻微影响外,这没什么大不了的,因为它们不会包含在最终的二进制输出。如果编译器的优化器足够聪明,能够独立完成这项工作,显然会更好,但在那之前,这是一个可行的解决方案,可以牵手并强制它生成所需的代码,而且我认为它比基于宏的方法干净得多。

这可以使用(重复使用macro BOOST_PP_),但请记住,您可以这样做并不意味着您应该这样做

#include <iostream>
#include <boost/preprocessor/repetition/repeat.hpp>

#define DECL(z, n, text) std::cout << "n = " << n << std::endl;

int main()
{
  int n = 0;
  BOOST_PP_REPEAT(5, DECL, "");
}
#包括
#包括

#定义DECL(z,n,文本)std::cout为什么不为
循环使用
?它只多了一行,而且比宏更容易混淆。@raket1111我想在程序的性能关键部分使用它,因为循环并不总是展开,即使在编译时已知边界,也没有办法强制MSVC这样做,让您对其进行概要分析和proven for循环或模板展开是导致显著减速的原因?我也可能有偏见,但我不会在“性能关键”上使用MSVC程序。至于宏循环,出现了。如果
for
循环没有展开,那是因为展开它并不是一个性能胜利。在这种情况下很可能不会,因为控制台I/O的开销远远大于一个简单条件分支的开销。在你有一个好的结果之前,不要猜测你的优化器ason这样做。你看到通过将调用
std::cout
8次的东西展开,性能提高了30%?我称之为BS。如果你说的是展开一种不同类型的循环,那么可能是这样;当你不进行PGO构建时,MSVC在展开循环时不会太激进。此外,我发布的是一条评论,而不是答案。
void MyFunction()
{
   MyOperation op;
   Unroller<16>()(op);
}
sub  rsp, 40
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
call Leaf
add  rsp, 40
jmp  Leaf
#include <iostream>
#include <boost/preprocessor/repetition/repeat.hpp>

#define DECL(z, n, text) std::cout << "n = " << n << std::endl;

int main()
{
  int n = 0;
  BOOST_PP_REPEAT(5, DECL, "");
}