宏无法正确扩展

发布于 2025-02-13 23:23:36 字数 878 浏览 3 评论 0原文

I have these macros defined:

#define AL_ASSERT_NO_MSG(x) if (!(x)) { assertDialog(__LINE__, __FILE__); __debugbreak(); }
#define AL_ASSERT_MSG(x, msg) if (!(x)) { assertDialog(msg, __LINE__, __FILE__); __debugbreak(); }

#define GET_MACRO(_1, _2, NAME, ...) NAME
#define AL_ASSERT(...) GET_MACRO(__VA_ARGS__, AL_ASSERT_MSG, AL_ASSERT_NO_MSG)(__VA_ARGS__)

I wanted to have it dispatched to the AL_ASSERT_NO_MSG macro if I only pass one argument and AL_ASSERT_MSG if I pass two arguments.

但是,当我使用这样的宏时:al_assert(false,“ test”)它扩展到if(!(false,“ test”))){assertDialog(23,“ C: \\ dev \\ c ++ \\ sortingalgorithms \\ algorithmvisualizer \\ src \\ window.cpp”); __debugbreak(); };,并且不起作用。

更多信息:我正在使用MSVC使用Visual Studio。这不是基于解决方案的项目,而是一个CMAKE项目作为文件夹打开。

我做错了什么?任何帮助都将受到赞赏。

I have these macros defined:

#define AL_ASSERT_NO_MSG(x) if (!(x)) { assertDialog(__LINE__, __FILE__); __debugbreak(); }
#define AL_ASSERT_MSG(x, msg) if (!(x)) { assertDialog(msg, __LINE__, __FILE__); __debugbreak(); }

#define GET_MACRO(_1, _2, NAME, ...) NAME
#define AL_ASSERT(...) GET_MACRO(__VA_ARGS__, AL_ASSERT_MSG, AL_ASSERT_NO_MSG)(__VA_ARGS__)

I wanted to have it dispatched to the AL_ASSERT_NO_MSG macro if I only pass one argument and AL_ASSERT_MSG if I pass two arguments.

However, when I use the macro like this: AL_ASSERT(false, "Test") it expanded to if (!(false, "Test")) { assertDialog(23, "C:\\Dev\\c++\\SortingAlgorithms\\AlgorithmVisualizer\\src\\Window.cpp"); __debugbreak(); }; and it does not work.

More information: I am using Visual Studio with MSVC. This is not a solution-based project but a CMake project opened as a folder.

What did I do wrong? Any help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

神爱温柔 2025-02-20 23:23:36

由于使用__行____文件__,因此您只需要宏。所有其他逻辑都可以在通常的函数中完成:

// Required arguments for args:
// message [optional]
// __LINE__
// __FILE__
template<typename C, typename... Args>
constexpr void al_assert(C&& condition, Args&&... args) {
   static_assert(sizeof...(args) >= 2);
   static_assert(sizeof...(args) <= 3);
   if (!std::forward<C>(condition)) {
       assertDialog(std::forward<Args>(args)...);
       __debugbreak();
   }
}

// assuming al_assert is placed in global namespace scope
// but better use a private namespace for al_assert
#define AL_ASSERT(...) (::al_assert(__VA_ARGS__, __LINE__, __FILE__))

// or
// #define AL_ASSERT(...) do { ::al_assert(__VA_ARGS__, __LINE__, __FILE__); } while(0)

使用C ++ 20,根本不需要宏:

#include<source_location>

//...

template<typename C, typename... Args>
struct al_assert {
    constexpr al_assert(C&& condition, Args&&... args,
                        std::source_location location = std::source_location::current()) {
       static_assert(sizeof...(args) <= 1);
       if (!std::forward<C>(condition)) {
           assertDialog(std::forward<Args>(args)..., location.line(), location.file_name());
           __debugbreak();
       }
    }
};

template<typename C, typename... Args>
al_assert(C&&, Args&&...) -> al_assert<C, Args...>;

可以直接使用宏而不是将宏用作al_assert(/*.....*/)< /代码>。 std :: source_location在参数包工作之后的CTAD技巧是

(模板参数和转发引用涵盖了潜在的边缘情况,以使其与宏观行为尽可能近。根据您的用例,您可能会对EG进行>条件简单地简单地感到满意bool而不是。)

You only need the macro because of the use of __LINE__ and __FILE__. All the other logic can be done in a usual function:

// Required arguments for args:
// message [optional]
// __LINE__
// __FILE__
template<typename C, typename... Args>
constexpr void al_assert(C&& condition, Args&&... args) {
   static_assert(sizeof...(args) >= 2);
   static_assert(sizeof...(args) <= 3);
   if (!std::forward<C>(condition)) {
       assertDialog(std::forward<Args>(args)...);
       __debugbreak();
   }
}

// assuming al_assert is placed in global namespace scope
// but better use a private namespace for al_assert
#define AL_ASSERT(...) (::al_assert(__VA_ARGS__, __LINE__, __FILE__))

// or
// #define AL_ASSERT(...) do { ::al_assert(__VA_ARGS__, __LINE__, __FILE__); } while(0)

With C++20 the macro is not required at all:

#include<source_location>

//...

template<typename C, typename... Args>
struct al_assert {
    constexpr al_assert(C&& condition, Args&&... args,
                        std::source_location location = std::source_location::current()) {
       static_assert(sizeof...(args) <= 1);
       if (!std::forward<C>(condition)) {
           assertDialog(std::forward<Args>(args)..., location.line(), location.file_name());
           __debugbreak();
       }
    }
};

template<typename C, typename... Args>
al_assert(C&&, Args&&...) -> al_assert<C, Args...>;

This can be used directly instead of using the macro as al_assert(/*...*/). The CTAD trick to make std::source_location after the parameter pack work is due to this answer.

(The template parameters and forwarding references are there to cover potential edge cases to make it as close as possible to the macro behavior. Depending on your use case, you may be happy with e.g. making the condition simply a bool instead.)

左耳近心 2025-02-20 23:23:36

MSVC似乎不会将__ va_args __扩展到多个参数(请参阅此相关问题: msvc not /a>)

这似乎有效:

#define GET_MACRO(_1, _2, NAME, ...) NAME
#define XGET_MACRO(args) GET_MACRO args
#define AL_ASSERT(...) XGET_MACRO((__VA_ARGS__, AL_ASSERT_MSG, AL_ASSERT_NO_MSG))(__VA_ARGS__)

如果您针对C ++ 20(并且具有Visual Studio 2019版本16.5),则可以使用__ va_opt __

#define AL_ASSERT(x, ...) if (!(x)) { assertDialog(__VA_OPT__( (__VA_ARGS__) , ) __LINE__, __FILE__); __debugbreak(); }

或者您可以使用某些功能来完成:

#define AL_ASSERT(x, ...) (([](bool b, const auto*... msg) { \
    static_assert(sizeof...(msg) <= 1, "AL_ASSERT passed too many arguments"); \
    if (!b) {                                     \
        if constexpr (sizeof...(msg) == 1) {      \
          const char* m{msg...};                  \
          assertDialog(msg, __LINE__, __FILE__);  \
        } else {                                  \
          assertDialog(__LINE__, __FILE__);       \
        }                                         \
        __debugbreak();
})(x, __VA_ARGS__))

使用版本16.10,您可以在没有 std :: source_location

void AL_ASSERT(bool x, const std::source_location location = std::source_location::current()) {
    if (!x) {
        assertDialog(location.line(), location.file_name());
        __debugbreak();
    }
}

void AL_ASSERT(bool x, const char* msg, const std::source_location location = std::source_location::current()) {
    if (!x) {
        assertDialog(msg, location.line(), location.file_name());
        __debugbreak();
    }
}

It seems that MSVC does not expand __VA_ARGS__ into multiple arguments (see this related question: MSVC doesn't expand __VA_ARGS__ correctly)

This seems to work:

#define GET_MACRO(_1, _2, NAME, ...) NAME
#define XGET_MACRO(args) GET_MACRO args
#define AL_ASSERT(...) XGET_MACRO((__VA_ARGS__, AL_ASSERT_MSG, AL_ASSERT_NO_MSG))(__VA_ARGS__)

And if you are targeting C++20 (and have Visual Studio 2019 version 16.5), you can use __VA_OPT__:

#define AL_ASSERT(x, ...) if (!(x)) { assertDialog(__VA_OPT__( (__VA_ARGS__) , ) __LINE__, __FILE__); __debugbreak(); }

Or you can do it with some functions:

#define AL_ASSERT(x, ...) (([](bool b, const auto*... msg) { \
    static_assert(sizeof...(msg) <= 1, "AL_ASSERT passed too many arguments"); \
    if (!b) {                                     \
        if constexpr (sizeof...(msg) == 1) {      \
          const char* m{msg...};                  \
          assertDialog(msg, __LINE__, __FILE__);  \
        } else {                                  \
          assertDialog(__LINE__, __FILE__);       \
        }                                         \
        __debugbreak();
})(x, __VA_ARGS__))

And with version 16.10 you can do it without a macro with std::source_location:

void AL_ASSERT(bool x, const std::source_location location = std::source_location::current()) {
    if (!x) {
        assertDialog(location.line(), location.file_name());
        __debugbreak();
    }
}

void AL_ASSERT(bool x, const char* msg, const std::source_location location = std::source_location::current()) {
    if (!x) {
        assertDialog(msg, location.line(), location.file_name());
        __debugbreak();
    }
}
看透却不说透 2025-02-20 23:23:36

尝试使用预处理器将表达式与消息分开并不是最好的主意,因为两者都可以包含逗号,从而使其总体上不可能。

但是您可以做到这一点:

void foo(int line, bool cond, std::string msg_or_file, const char *file_or_null = nullptr);

#define ASSERT(...) foo(__LINE__, __VA_ARGS__, __FILE__)

然后,如果您确实提供了Mesasge,则foo(line,cond,“消息”,“ file”)将被调用,否则foo(line,cond cond cond ,“文件”,nullptr)

通过检查最后一个参数是否为零,您可以查看第三个parmae​​ter是消息还是文件名。

...或者您可以超载该功能。

Trying to separate the expression from the message using preprocessor is not the best idea, since both can contain commas, making it impossible in general.

But you can do this:

void foo(int line, bool cond, std::string msg_or_file, const char *file_or_null = nullptr);

#define ASSERT(...) foo(__LINE__, __VA_ARGS__, __FILE__)

Then, if you do provide a mesasge, foo(line, cond, "message", "file") will be called, and otherwise foo(line, cond, "file", nullptr) will be called.

By checking if the last parameter is zero, you see whether the third parmaeter is a message or a filename.

... Or you can just overload the function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文