Syntax Error with Q_ASSERT
-
Hi everyone! Using the following...
Q_ASSERT(std::is_same<int, int>::value);
... I get
syntax error: ')'
, but with extra parentheses ...Q_ASSERT((std::is_same<int, int>::value));
... it compiles and works. Looks like a bug in the Q_ASSERT macro to me, or am I missing something?
Platform is Qt 5.7.0, Visual C++ Compiler 14
-
Hi
It's like it sees it as 2 params. ?!?
It's not Q_ASSERT fault ( it seems) as any macro suffer from this.#define MACRO(cond) (true)
MainWindow::~MainWindow()
{
MACRO( std::is_same<int, int>::value );gives me
error: macro "MACRO" passed 2 arguments, but takes just 1
MACRO( std::is_same<int, int>::value );
^(5.7, mingw)
So it seems its just macro and templates and a stupid preprocessor
http://stackoverflow.com/questions/9416635/using-commas-inside-a-macro-without-parenthesis-how-can-i-mix-and-match-with-a
http://stackoverflow.com/questions/8942912/how-to-pass-multi-argument-templates-to-macros -
Hi
It's like it sees it as 2 params. ?!?
It's not Q_ASSERT fault ( it seems) as any macro suffer from this.#define MACRO(cond) (true)
MainWindow::~MainWindow()
{
MACRO( std::is_same<int, int>::value );gives me
error: macro "MACRO" passed 2 arguments, but takes just 1
MACRO( std::is_same<int, int>::value );
^(5.7, mingw)
So it seems its just macro and templates and a stupid preprocessor
http://stackoverflow.com/questions/9416635/using-commas-inside-a-macro-without-parenthesis-how-can-i-mix-and-match-with-a
http://stackoverflow.com/questions/8942912/how-to-pass-multi-argument-templates-to-macros@mrjj Thank you! That's really pretty stupid.
-
@mrjj Thank you! That's really pretty stupid.
@Wieland
Yeah, i know templates are pure compiler stage
but one should think preprocess at least accepts valid
syntaxes :)
Luckily the () fix is not super ugly. -
@mrjj Thank you! That's really pretty stupid.
@mrjj said in Syntax Error with Q_ASSERT:
one should think preprocess at least accepts valid
syntaxesWhy should anyone think that?
@Wieland said in Syntax Error with Q_ASSERT:
That's really pretty stupid.
The preprocessor is basically a copy-paste-made-easy, it's a very, very simple program. It does string replacements only, it cares not for any syntax or any language for that matter. You can run the preprocessor independently of the compiler (whether it's C, C++, Java, FORTRAN or w/e), and actually some fortran code (used with gfortran) makes use of the gcc's preprocessor.
-
@mrjj said in Syntax Error with Q_ASSERT:
one should think preprocess at least accepts valid
syntaxesWhy should anyone think that?
@Wieland said in Syntax Error with Q_ASSERT:
That's really pretty stupid.
The preprocessor is basically a copy-paste-made-easy, it's a very, very simple program. It does string replacements only, it cares not for any syntax or any language for that matter. You can run the preprocessor independently of the compiler (whether it's C, C++, Java, FORTRAN or w/e), and actually some fortran code (used with gfortran) makes use of the gcc's preprocessor.
@kshegunov The preprocessor is part of the C++ language specification and I would expect that, besides all the other smart things it also can do, it is able to handle such situations in a sane way. Anyways, I got used to C++ coming up with nasty surprises.
Edit: Next time maybe better Write in Go ;-)
-
@kshegunov The preprocessor is part of the C++ language specification and I would expect that, besides all the other smart things it also can do, it is able to handle such situations in a sane way. Anyways, I got used to C++ coming up with nasty surprises.
Edit: Next time maybe better Write in Go ;-)
@Wieland said in Syntax Error with Q_ASSERT:
The preprocessor is part of the C++ language specification
It is? I've never known that.
Anyways, I got used to C++ coming up with nasty surprises.
Eh, yeah. More syntax means more pitfalls. But tell that to the standards committee ... as you said, just write in Go! ;)
-
The preprocessor only understands
(
and,
in this case (as it's a pre processor i.e. no symbols, namespaces or templates exist at this point), so this is basically parsed asQ_ASSERT(STUFF, OTHER_STUFF);
When you put the extra parentheses it becomesQ_ASSERT(STUFF_IN_PARENTHESES)
.Here's a cute gotcha for the extra parentheses trick:
Because the general rules for type deduction in c++ were pretty boring, c++11 brought such wonders asdecltype
to make it more fun:int foo; bool nudge_nudge_wink_wink = std::is_same<decltype(foo), decltype((foo))>::value; //gives "false"... obviously :P
This marvel is sponsored by the fact that
foo
is an lvalue and(foo)
is an expression ;)So if in your macro you happen to try to deduce the type of the expression passed to it you might have a joyful debugging session.
This is one such super-simplified pattern commonly found in macro based property systems:#define FOO(bar, bazz) decltype(bar) hello = bazz; int foo; FOO(foo, 42); //works fine FOO((foo), 42); //error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
@kshegunov
C++ standard includes C standard by reference. One of the talks at this year's CppCon mentioned that there was a cleanup effort in C++17 made to remove some of the more obscure or irrelevant C headers. -
The preprocessor only understands
(
and,
in this case (as it's a pre processor i.e. no symbols, namespaces or templates exist at this point), so this is basically parsed asQ_ASSERT(STUFF, OTHER_STUFF);
When you put the extra parentheses it becomesQ_ASSERT(STUFF_IN_PARENTHESES)
.Here's a cute gotcha for the extra parentheses trick:
Because the general rules for type deduction in c++ were pretty boring, c++11 brought such wonders asdecltype
to make it more fun:int foo; bool nudge_nudge_wink_wink = std::is_same<decltype(foo), decltype((foo))>::value; //gives "false"... obviously :P
This marvel is sponsored by the fact that
foo
is an lvalue and(foo)
is an expression ;)So if in your macro you happen to try to deduce the type of the expression passed to it you might have a joyful debugging session.
This is one such super-simplified pattern commonly found in macro based property systems:#define FOO(bar, bazz) decltype(bar) hello = bazz; int foo; FOO(foo, 42); //works fine FOO((foo), 42); //error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
@kshegunov
C++ standard includes C standard by reference. One of the talks at this year's CppCon mentioned that there was a cleanup effort in C++17 made to remove some of the more obscure or irrelevant C headers.@Chris-Kawa Thank you for this! Every day a new surprise. Or two.
-
The preprocessor only understands
(
and,
in this case (as it's a pre processor i.e. no symbols, namespaces or templates exist at this point), so this is basically parsed asQ_ASSERT(STUFF, OTHER_STUFF);
When you put the extra parentheses it becomesQ_ASSERT(STUFF_IN_PARENTHESES)
.Here's a cute gotcha for the extra parentheses trick:
Because the general rules for type deduction in c++ were pretty boring, c++11 brought such wonders asdecltype
to make it more fun:int foo; bool nudge_nudge_wink_wink = std::is_same<decltype(foo), decltype((foo))>::value; //gives "false"... obviously :P
This marvel is sponsored by the fact that
foo
is an lvalue and(foo)
is an expression ;)So if in your macro you happen to try to deduce the type of the expression passed to it you might have a joyful debugging session.
This is one such super-simplified pattern commonly found in macro based property systems:#define FOO(bar, bazz) decltype(bar) hello = bazz; int foo; FOO(foo, 42); //works fine FOO((foo), 42); //error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
@kshegunov
C++ standard includes C standard by reference. One of the talks at this year's CppCon mentioned that there was a cleanup effort in C++17 made to remove some of the more obscure or irrelevant C headers.@Chris-Kawa
Yes, I suppose so, I just never thought about it. I don't often think about what the standard does or doesn't include, but I always thought the preprocessor is just a "common non-standardized extension", the language doesn't require it to function. And seeing that code, which I'm happy to say I understand not one iota of, I must reiterate my despise for C++11. :) -
-
C++11 always evokes this feeling in me:
and by the way I'm an atheist ... :] -
I just came up with something that looks like a solution for this to me. What do guys think?
#if !defined(MY_ASSERT) # ifndef QT_NO_DEBUG # define MY_ASSERT_FIRST_ARGUMENT(A, ...) A # define MY_ASSERT(...) ((!MY_ASSERT_FIRST_ARGUMENT(__VA_ARGS__)) ? qt_assert(#__VA_ARGS__,__FILE__,__LINE__) : qt_noop()) # else # define MY_ASSERT(...) qt_noop() # endif #endif
-
I just came up with something that looks like a solution for this to me. What do guys think?
#if !defined(MY_ASSERT) # ifndef QT_NO_DEBUG # define MY_ASSERT_FIRST_ARGUMENT(A, ...) A # define MY_ASSERT(...) ((!MY_ASSERT_FIRST_ARGUMENT(__VA_ARGS__)) ? qt_assert(#__VA_ARGS__,__FILE__,__LINE__) : qt_noop()) # else # define MY_ASSERT(...) qt_noop() # endif #endif
@Wieland
where is the docs? ;)
Looks cool. its cryptic enough that it might actually work :) -
It's almost the same as the current implementation of Q_ASSERT, just with a variadic macro. So it's C++11 only.
-
@mrjj good one :)
@Wieland Could you explain how it is suppose to work? From what I can decrypt it just checks the first argument passed, so for your original exampleMY_ASSERT(std::is_same<int, int>::value);
it would expand to something like((!std::is_same<int) ? ...
which doesn't make much sense? Or am I missing something?Btw. I get compiler errors for this:
with gcc:in definition of macro MY_ASSERT_FIRST_ARGUMENT wrong number of template arguments (1, should be 2)
with clang:error: expected > MY_ASSERT(std::is_same<int, int>::value);
it compiles in VS2015 U3 although their macro expansion is broken to bits so I wouldn't trust it does what it should. -
@mrjj good one :)
@Wieland Could you explain how it is suppose to work? From what I can decrypt it just checks the first argument passed, so for your original exampleMY_ASSERT(std::is_same<int, int>::value);
it would expand to something like((!std::is_same<int) ? ...
which doesn't make much sense? Or am I missing something?Btw. I get compiler errors for this:
with gcc:in definition of macro MY_ASSERT_FIRST_ARGUMENT wrong number of template arguments (1, should be 2)
with clang:error: expected > MY_ASSERT(std::is_same<int, int>::value);
it compiles in VS2015 U3 although their macro expansion is broken to bits so I wouldn't trust it does what it should.@Chris-Kawa Damn, I only tested it with MSVC and it works there :-(
-
@mrjj good one :)
@Wieland Could you explain how it is suppose to work? From what I can decrypt it just checks the first argument passed, so for your original exampleMY_ASSERT(std::is_same<int, int>::value);
it would expand to something like((!std::is_same<int) ? ...
which doesn't make much sense? Or am I missing something?Btw. I get compiler errors for this:
with gcc:in definition of macro MY_ASSERT_FIRST_ARGUMENT wrong number of template arguments (1, should be 2)
with clang:error: expected > MY_ASSERT(std::is_same<int, int>::value);
it compiles in VS2015 U3 although their macro expansion is broken to bits so I wouldn't trust it does what it should.@Chris-Kawa My
ideahope was that the preprocessor would be smarter when I confront it with a variadic macro. -
@Wieland I don't think it works. It just compiles ;)
Well you could use something simpler:define MY_ASSERT(...) (!(__VA_ARGS__) ? qt_assert(#__VA_ARGS__,__FILE__,__LINE__) : qt_noop())
and that should be ok, but it has the same drawback I described earlier - it changes the expression it tests by adding extra () around it. Admittedly it's not a big deal and it should work as expected most of the time.