【问题标题】:How can I make a macro handle different types of arguments?如何让宏处理不同类型的参数?
【发布时间】:2019-06-18 02:14:31
【问题描述】:

如何编写一个宏来根据其参数的类型执行不同的操作?

我有一个宏需要处理可以具有两种类型之一的参数。

#include <typeinfo>

enum class Coolness { kUndefined, kUncool, kCool };
enum class Tallness { kUndefined, kShort, kTall };

void MakePerson (Coolness coolness, Tallness tallness) {}

// Provide a way to make a person by only providing Coolness or Tallness.
#define MAKE_PERSON(x)                         \
({                                             \
  if (typeid(x) == typeid(Coolness)) {         \
      MakePerson(((x)), Tallness::kUndefined); \
  } else {                                     \
      MakePerson(Coolness::kUndefined, (x));   \
  }                                            \
})

int main()
{
  MAKE_PERSON(Coolness::kUncool);
  MAKE_PERSON(Tallness::kTall);
}

(我们可以在这里使用默认参数,但在实际代码中我们实际上必须使用宏。)

编译器对 main 中的两个调用都抛出错误:

main.cpp: In function ‘int main()’:
main.cpp:23:43: error: cannot convert ‘Coolness’ to ‘Tallness’ for argument ‘2’ to ‘void MakePerson(Coolness, Tallness)’
       MakePerson(Coolness::kUndefined, (x)); \
                                           ^
main.cpp:29:3: note: in expansion of macro ‘MAKE_PERSON’
   MAKE_PERSON(Coolness::kUncool);
   ^~~~~~~~~~~
main.cpp:21:45: error: cannot convert ‘Tallness’ to ‘Coolness’ for argument ‘1’ to ‘void MakePerson(Coolness, Tallness)’
       MakePerson(((x)), Tallness::kUndefined); \
                                             ^
main.cpp:30:3: note: in expansion of macro ‘MAKE_PERSON’
   MAKE_PERSON(Tallness::kTall);
   ^~~~~~~~~~~

(在https://www.onlinegdb.com/online_c++_compiler完成)

我们不能像this question那样使用__builtin_types_compatible_p,因为我们的编译器没有。

如何编写一个宏来根据其参数的类型执行不同的操作?

【问题讨论】:

  • 这就是 C++ 模板的用途。这是 C++,而不是带有 shudder 宏的史前 C...
  • 不幸的是,我认为我需要在实际问题中使用宏 - 我正在开发需要访问 __LINE____FILE__ 的日志基础设施。这只是我能想到的最小的例子。
  • 由于__LINE__ __FILE__ 总是相同的类型,在任何地方,都不清楚这有什么关系。
  • @SamVarshavchik,我想说的是我正在努力启用 LOG(...) 调用,我想我需要在这里使用一个宏,这样我才能得到这条线# 和调用发生位置的文件(否则我只会得到实现日志校准的行# 和文件)。但我想你是说宏的实现应该使用 C++ 模板,而不是说不应该使用宏,永远?

标签: c++


【解决方案1】:

使用简单的函数重载,不要试图让宏变得更聪明:

enum class Coolness { kUndefined, kUncool, kCool };
enum class Tallness { kUndefined, kShort, kTall };

void MakePerson (Coolness coolness, Tallness tallness)
{
    ...
}

inline void MakePerson (Coolness coolness)
{
    MakePerson(coolness, Tallness::kUndefined);
}

inline void MakePerson (Tallness tallness)
{
    MakePerson(Coolness::kUndefined, tallness);
}

#define MAKE_PERSON(x) \
{ \
    // use __FILE__ and __LINE__ as needed... \
    MakePerson(x); \
}

int main()
{
    MAKE_PERSON(Coolness::kUncool);
    MAKE_PERSON(Tallness::kTall);
}

Live Demo

【讨论】:

  • 谢谢,这显然是解决我给出的例子的方法。我认为它说明了我需要如何修改我的实际代码,以便我们可以更接近这个实现......
【解决方案2】:

欢迎提出其他建议,但我们最终所做的是使用 static_cast 告诉编译器参数是什么类型:

#include <typeinfo>

enum class Coolness { kUndefined, kUncool, kCool };
enum class Tallness { kUndefined, kShort, kTall };

void MakePerson (Coolness coolness, Tallness tallness) {}

// Provide a way to make a person by only providing Coolness or Tallness.
// Static cast is used because the compiler fails to typecheck the 
// branches correctly without it.
#define MAKE_PERSON(x)                                              \
({                                                                  \
  if (typeid(x) == typeid(Coolness)) {                              \
      MakePerson(static_cast<Coolness>((x)), Tallness::kUndefined); \
  } else {                                                          \
      MakePerson(Coolness::kUndefined, static_cast<Tallness>((x))); \
  }                                                                 \
})

int main()
{
  MAKE_PERSON(Coolness::kUncool);
  MAKE_PERSON(Tallness::kTall);
}
...Program finished with exit code 0 

【讨论】:

  • 这太可怕了。此外,当您刚刚检查类型为Coolness 时,还不清楚为什么要执行static_cast&lt;Coolness&gt;
  • 如果您的问题是if 的错误分支没有类型检查,请考虑constexpr if,而不是用无效的强制转换草率地隐藏类型错误。
  • @SilvioMayolo 谢谢,我不知道constexpr if。不幸的是,我不能使用它,因为我仅限于 C++11。
  • @M.M 添加了一条评论来解释我为什么要进行 static_cast,谢谢(不是说这样做会更好,但希望它有助于解释原因)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-14
  • 2021-04-04
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 2022-01-02
相关资源
最近更新 更多