【问题标题】:Is it possible to use C++ macros to generate blocks of code?是否可以使用 C++ 宏来生成代码块?
【发布时间】:2021-08-13 18:24:19
【问题描述】:

我有一个 C++ 程序,其中某种模式的代码块不断重复,我想知道是否可以使用 C++ MACROS 作为预处理器来自动生成此代码。更准确地说,我的代码块看起来像这样:

for(std::size_t i=0;i< lc_.size(); i++)
{
  std::string str;
  state::MsData lc = data->lc(i);
  convert<data::ClassForLC>(lc.data(), str);
  lc_[i] = data::ClassForLC(str);
}

然后我可能会有另一个看起来像这样的块:

for(std::size_t i=0;i< mmop_.size(); i++)
{
  std::string str;
  state::MvData mmop = data->mmop(i);
  convert<data::ClassForMMOP>(mmop.data(), str);
  mmop_[i] = data::ClassForMMOP(str);
}

如您所见,一般模式是这样的:

  for(std::size_t i=0;i< X_.size(); i++)
    {
      std::string str;
      Y X = data->X(i);
      convert<Z>(X.data(), str);
      X_[i] = Z(str);
    }

我想知道是否可以定义一个宏 REPLACE(X,Y,Z) 将上面代码中的 X,Y,Z 替换为我作为参数传递的任何文本?注意:我使用 C++11。 谢谢

【问题讨论】:

  • 这能回答你的问题吗? Multi line preprocessor macros
  • 有可能 - 但不要这样做!这是维护的痛苦! for(std::size_t i=0;i&lt; lc_.size(); i++) 的长度在循环中也不会改变,所以不要每次都抓取它 - 取决于集合 .size() 可能是一项昂贵的操作。
  • 只是补充一下@John3136 所说的,调试变成了一场巨大的噩梦。相反,请考虑使用模板函数或传入一个函数来执行非泛型部分。
  • 可以像这样概括代码,因此只需编写一次。函数模板是一种常见的解决方案。也可以使用宏编写代码,但您会冒着使用宏带来的所有痛苦的风险。
  • 将重复代码放入函数或子程序的古老艺术发生了什么?与其创建宏,不如尝试在函数中定义代码,放入头文件并用“内联”标记。

标签: c++ c++11


【解决方案1】:

不要那样做。

template<class Z, class X, class F>
void do_stuff( X&& x, F&& f ) {
  for(std::size_t i=0;i< x.size(); i++)
  {
    std::string str;
    auto tmp = f(i);
    convert<Z>(tmp.data(), str);
    x[i] = Z(str);
  }
}

写一个模板。我们可以这样使用它:

do_stuff<data::ClassForMMOP>(
  mmop_,
  [&](std::size_t i){ return data->mmop(i); }
);

使用宏是可能的,但这是个坏主意。

这是一个坏主意,原因有很多:

  1. 您得到的错误将基于文本替换。

  2. 调试通常是不可能的。

  3. 在基于宏的生成中可能会出现许多难以发现的细微错误。

使用模板,您可以获得 C++ 编译器和调试器完全理解的内容,以及一堆“早期”的正确性检查。模板产生的错误很难,但比宏错误要容易得多。

但要在宏中实际做到这一点:

#define MACRO(X,Y,Z) \
  X x = Y(); \
  Z(Y)

在换行符之前使用 \ 以使 C++ 预处理器(嗯,不止于此)而言“所有内容都在同一行”。

实际上,您正在生成一段极长的 C++ 代码。由于 C++ 不依赖换行符,因此宏可以工作。

【讨论】:

  • 在不使用简单宏的所有原因中,错误报告确实不是一个原因。首先,大多数现代编译器本身就是预处理器,它们通常会准确地报告宏中的错误(使用诸如“扩展宏时...”之类的文本),其次,没有什么能比模板化代码生成的错误更好。关于调试和细微错误的观点是站得住脚的。
  • @SergeyA 我发现简单的模板错误消息很容易。模板只允许更复杂的结构。根据我的经验,在一堆递归宏中做任何复杂的事情会使理解错误消息变得更加困难。真的很痛苦。 (痛苦的来源:基于复杂链式宏的反射系统中的错误。)也许事情已经变得更好了,不知道。
  • 我想说,对于简单的东西,现在宏和模板错误都非常可读。对于任何不那么简单的事情,它们都很难。
  • decltype(f(i)) tmp = f(i); -> auto tmp = f(i);
  • @PaulSanders 啊,我忘了他们早在c++11 中就使用过auto。我已经习惯了“废话,C++11,我要删除auto”。叹息。
【解决方案2】:

您可以使用反斜杠 (\) 将宏定义扩展另一行。 例如,如果我有:

#define MACRO1(x) std::cout << "macro1: " << x << std::endl;

我可以改成:

#define MACRO1(x) std::cout << "macro1: " \
<< x << std::endl;

您还需要确保不要在定义的最后一行使用反斜杠,并且在放置反斜杠之前始终留一个空格。

编辑:

您也可以正常使用模板,例如:

#define my_template<int T> std::cout << T << std::endl;

但我建议您改为这样做:

#define my_template(T) std::cout << T << std::endl;

因为:

  1. 您收到的错误将基于文本替换。
  2. 通常无法进行调试。
  3. 在基于宏的生成中可能会出现很多小错误,这些错误很难发现。

【讨论】:

    【解决方案3】:

    试试:

    #define MY_MACRO(X, Y, Z)                   \
        for(std::size_t i=0;i< X#_.size(); i++) \
        {                                       \
          std::string str;                      \
          Y X = data->X(i);                     \
          convert<Z>(X.data(), str);            \
          X#_[i] = Z(str);                      \
        }
    

    但我建议改用模板。

    【讨论】:

    • 非常感谢。这如何使用模板来实现?
    • 提醒:宏没有类型检查;模板更安全。
    • 例如,只修改了lc_,而data似乎是唯一的输入参数,因此接受data作为参数的方法就绰绰有余了(如果lc_和@ 987654326@继承同一个模板类,我们可以实现lc_.convert(data)也许)。
    • 如果您建议使用模板,请给 OP 模板代码。
    猜你喜欢
    • 2015-12-22
    • 1970-01-01
    • 2020-09-26
    • 1970-01-01
    • 1970-01-01
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    相关资源
    最近更新 更多