struct T{ double x};
In C, it creates no problem.
But in C++, it gives the following compilation error:
expected ';' at end of member declaration.
From C11, "Structure and union specifiers, syntax" (6.7.2.1/1):
struct-declaration:
specifier-qualifier-list struct-declarator-listopt ;
Each element of a struct ends in a semicolon. Your claim that there is "no problem" is not based on what the C specification says. If your compiler accepts such code, it is not a conforming C compiler, or you are not using it correctly. (Some compilers have a configurable level of standards conformance.)
The GCC parser for C grammar is implemented as follows:
/* If no semicolon follows, either we have a parse error or
are at the end of the struct or union and should
pedwarn. */
if (c_parser_next_token_is (parser, CPP_SEMICOLON))
c_parser_consume_token (parser);
else
{
if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
pedwarn (c_parser_peek_token (parser)->location, 0,
"no semicolon at end of struct or union");
else if (parser->error
|| !c_parser_next_token_starts_declspecs (parser))
{
c_parser_error (parser, "expected %<;%>");
c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
break;
}
/* If we come here, we have already emitted an error
for an expected `;', identifier or `(', and we also
recovered already. Go on with the next field. */
}
It calls function pedwarn on a missing semicolon.
The definition of pedwarn can be found here. It reads:
pedwarn is for code that is accepted by GCC but it should be rejected or diagnosed according to the current standard, or it conflicts with the standard (either the default or the one selected by -std=). It can also diagnose compile-time undefined behavior (but not runtime UB). pedwarns become errors with -pedantic-errors.
Why output of struct T{ double x}; different in C and C++?
The example struct definition is ill-formed in both C and C++.
C and C++ are different languages, they use different parsers (or whatever component of the compiler detects this error). The output is different because different decisions were made by people when they implemented the parser of the C compiler, than were made when the C++ parser was implemented.
Latter decided to issue an error, the former issues merely a warning, and successfully compiles despite the bug. Another C compiler can refuse to compile as well, and a C++ compiler can accept the program (as long as it produces a warning).
Generally, C language likes semicolon much more than Pascal (Delphi). In your case, C accepts struct T{ double x};, but C++ already requires struct T{ double x;};
Related
I'm working with libsystemd-dev (a C library) in my C++ application.
I get a gcc/clang pedantic warning
compound literals are a C99-specific feature
with this code:
#include <systemd/sd-bus.h>
void foo()
{
sd_bus_error err = SD_BUS_ERROR_NULL; // compound literals are a C99-specific feature
...
}
Looking at the <systemd/sd-bus.h> header file, I see:
typedef struct {
const char *name;
const char *message;
int _need_free;
} sd_bus_error;
#define SD_BUS_ERROR_MAKE_CONST(name, message) ((const sd_bus_error) {(name), (message), 0})
#define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE_CONST(NULL, NULL)
This means I can solve the warning with:
#include <systemd/sd-bus.h>
void foo()
{
sd_bus_error err = {nullptr, nullptr, 0};
...
}
But is that a good idea? If the library changes, my code would need to change too so I feel that it's volatile. Is there really any problem with this warning? Is there a better way to get around it?
There is always the method of just using compiler flags to disable the warning, but I was wondering if there could be an encouraged method in-code to address this.
Omnifarious already hinted at one approach - use extensions inside a wrapper function. The slightly more robust method is to use an extern "C" wrapper function, in its own Translation Unit. Compile that whole Translation Unit as C11, without extensions.
This is generally more robust. Mixing code at source level requires advanced compiler support, whereas linking C and C++ is fairly straightforward.
I'm attempting to build source files of an open source C++ library written by someone else. This is being done on Windows with Cygwin's mingw-w64 compiler. The only compiler option I'm attaching is -std=gnu++11 since the library depends on some C++11 features.
Here are some examples of code in their library that appears to be causing issues:
CPScalar & Abs()
{
m_dValue = std::abs(m_dValue);
return *this;
}
//...
template<typename Unit>
bool SEScalarQuantity<Unit>::Set(const SEScalarQuantity<Unit>& s)
{
if (m_readOnly)
throw CommonDataModelException("Scalar is marked read-only");
if (!s.IsValid())
return false;
m_value = s.m_value;
m_isnan = (std::isnan(m_value)) ? true : false;
m_isinf = (std::isinf(m_value)) ? true : false;
m_unit = s.m_unit;
return true;
}
I get compiler errors on the std:: qualified functions above. The compiler error on the m_dValue = std::abs(m_dValue); line is
error: call of overloaded 'abs(double&)' is ambiguous
Which made me think it could be related to the question of whether std::abs(0u) is ill-formed as well as this answer to a similar SO question.
m_isnan = (std::isnan(m_value)) ? true : false; and the following line gives me
error: expected unqualified-id before '(' token
There are countless other uses of std:: that the compiler doesn't complain about. If I remove all of the std:: qualifiers in the statements that are giving me errors, the code compiles beautifully.
Thing is, this open source project is (presumably) being built by others without modification, so what am I missing here?
Add #include <cmath> to the file being compiled. The problem is that there are a couple of overloads of std::abs for integer types that are declared in the header <cstdlib> and the compiler is complaining that it doesn't know which of those to use. What's needed, though, is std::abs(double), and that's declared in <cmath>.
The reason that this code works with some compilers and not others is probably that there is a declaration of std::abs(double) coming in from some header other than <cmath>. That's allowed, but not required.
I recently got a C++ program for my school test.
#include<iostream.h>
#define convert(p,q) p+2*q
void main()
{
int a,b,result;
cin>>a>>b;
result=convert(a,b);
cout<<result;
}
This works correctly.
When I put p+2*q in {}, it gives me the error :
"Expression syntax in function main()"
Now when I declare result just before output, like this:
int result=convert(a,b);
It works. Why and why not?
Avoid using macros if you have trouble understanding what the compiler complains about. This is what your code looks like when the macro is expanded, when you put the extra {}
result={a+2*b}
And this is what it looks like with the declaration on the same line
int result={a+2*b}
Before the C++11 standard, the former is a syntax error. Since C++11, it is copy-list-initialization of a temporary (see the syntax labeled (10) ).
The latter is aggregate initialization.
My question is related to Prasoon's question about non POD types and value initialization.
I tried the following code on online compilers like Ideone and Codepad but the executables gave runtime error on both the sites.
#include <iostream>
#include <cassert>
struct Struct {
std::string String;
int Int;
bool k;
};
struct InStruct:Struct
{
InStruct():Struct(){}
};
int main()
{
InStruct i;
assert ( i.Int == 0);
std::cout << "Hello";
}
Ideone Output here
Codepad Output here
Does that mean neither of them support C++03 value initialization feature?
Does that mean neither of them support C++03 value initialization feature?
Yes.
Prior to version 4.4, GCC did not completely support value initialization (the Boost GCC compatibility header explains this and has links to the relevant GCC defect reports; see line 77).
If your code needs to be portable, you should be very careful relying on value initialization; GCC did not support it fully until recently and Visual C++ does not fully support it even in its latest version, Visual C++ 2010.
The declaration
InStruct i;
does not invoke value initialization
$8.5.3/10 - "An object whose
initializer is an empty set of
parentheses, i.e., (), shall be
value-initialized."
If you want to value-initialize, you would require an expression something like
assert(InStruct().Int == 0);
Try it now! - Ideone supports GCC-4.5.1
Why does this not compile on VC 2005?
bool isTrue(bool, bool) { return true; }
void foo();
#define DO_IF(condition, ...) if (condition) foo(__VA_ARGS__);
void run()
{
DO_IF(isTrue(true, true)); // error C2143: syntax error : missing ')' before 'constant'
}
Running this through the preprocessor alone outputs:
bool isTrue(bool, bool) { return true; }
void foo();
void run()
{
if (isTrue(true true)) foo();;
}
Notice the missing comma in the penultimate line.
Last Edit:
LOL!
bool isTrue(bool, bool) { return true; }
void foo();
#define DO_IF(condition, ...) if (condition) { foo(__VA_ARGS__); }
void run()
{
DO_IF(isTrue(true ,, true)); // ROTFL - This Compiles :)
}
Macros with indefinite numbers of arguments don't exist in the 1990 C standard or the current C++ standard. I think they were introduced in the 1999 C standard, and implementations were rather slow to adopt the changes from that standard. They will be in the forthcoming C++ standard (which I think is likely to come out next year).
I haven't bothered to track C99 compliance in Visual Studio, mostly because the only things I use C for anymore require extreme portability, and I can't get that with C99 yet. However, it's quite likely that VS 2005 lacked parts of C99 that VS2008 had.
Alternately, it could be that you were compiling the program as C++. Check your compiler properties.
Run your code through CPP (C preprocessor) to see what substitutions CPP does for your macro.
You could do it either by invoking cpp or providing -E parameter to compiler (if you use gcc, of course).
Various preprocessor implementations parse the commas greedily, treating them as separators for macro arguments. Thus, CPP thinks that you're asking "DO_IF" to do a substitution with two parameters, "isTrue(true" and "true)".
Your code compiles just fine in VS2008, if i change DO_IF to RETURN_IF. However, this should not change anything relevant for your error.
Edit: Still compiles without errors, even after your changes.
I think that should work, except, shouldn't it be...
RETURN_IF(isTrue(b, !b));
and
RETURN_IF(isTrue(b, b));