From the OpenGL Wiki page on "Data Type (GLSL)", I found a pretty broad statement about initializer lists.
...Therefore, GLSL takes a feature from C++11 and allows the
initialization of any type by using an initializer list. An
initializer list is a list of initializers (which themselves could be
initializer lists) that are used to initialize a variable. Initializer
lists are bounded by curly braces ("{" and "}").
Initializer lists do not require specifying the type name; the
compiler will deduce it correctly. And this deduction extends down the
hierarchy of objects in aggregates (structs/arrays). For example, the
above code could be initialized with the following initializer list:
Data dataArray[3] = { {...}, ... }
I have tried a number of variations on initializer lists, and I'm not convinced.
# version 330
vec4 by_return(void) { return {1.0}; } // unexpected '{', expecting "::"
vec4 by_decl(void) { vec4 x = {1}; return x; } // too little data in initialization
Okay, initializer lists are not equivalent to constructors of the deduced type, e.g. vec4(1.0)...
vec4 by_decl(void) { vec4 x = {1,0,0,0}; return x; }
// OpenGL does not allow C style initializers
...but it's moot. Just in case "any type" was more broad than intended, I made sure to try a variant where a struct should be deduced in an outer initializer list, and another with an explicit constructor but arguments given as initializer lists:
struct Dual { vec4 u, v; }
Dual struct_outer(void) { return {vec4(1,0,0,0), vec4(0,0,0,0)}; } // unexpected '{', expecting "::"
Dual struct_inner(void) { return Dual({1,0,0,0}, {0,0,0,0}); } // unexpected '{', expecting "::"
Contrast that result with the next few lines of the Wiki:
The compiler automatically deduces that the second element of each
Data member is an vec2. This is based on the definition of Data.
Finally, arrays should be no different from structs, so this should come as no surprise:
vec4[2] as_array(void) { return {{1,0,0,0},{0,0,0,0}}; } // unexpected '{', expecting "::"
vec4[2] as_array2(void) { vec4 x[2] = {{1,0,0,0},{0,0,0,0}}; return x; }
// OpenGL does not allow C style initializers
vec4[2] as_array3(void) { return {vec4(1), vec4(1)}; } // unexpected '{', expecting "::"
vec4[2] as_array4(void) { return vec4[2]{vec4(1), vec4(1)}; } // unexpected '{', expecting "::"
vec4[2] as_array5(void) { vec4[2] x; x[0] = {1,0,0,0}; x[1] = {0,0,0,0}; return x; } // unexpected '{', expecting "::"
From everything I've seen so far, it looks the compiler is capable of parsing a "C style initializer", but only in assignment, and for the sole purpose of rejecting it more explicitly.
Initializer lists are a later addition to OpenGL. The Wiki notes off to the side that this feature is "core since version 4.2". I made sure to include my #version 330 statement because I was so sure that initializer lists were an earlier addition than GL 3.3, and I didn't even question that until the moment I was about to submit the question. Lesson learned, I think.
Related
While reading an article, I came across the following function:
SolidColor::SolidColor(unsigned width, Pixel color)
: _width(width),
_color(color) {}
__attribute__((section(".ramcode")))
Rasterizer::RasterInfo SolidColor::rasterize(unsigned, Pixel *target) {
*target = _color;
return {
.offset = 0,
.length = 1,
.stretch_cycles = (_width - 1) * 4,
.repeat_lines = 1000,
};
}
What is the author doing with the return statement? I haven't seen anything like that before, and I do not know how to search for it... Is it valid for plain C too?
Edit:
link to the original article
This isn't valid C++.
It's (sort of) using a couple features from C known as "compound literals" and "designated initializers", which a few C++ compilers support as an extension. The "sort of" comes from that fact that to be a legitimate C compound literal, it should have syntax that looks like a cast, so you'd have something like:
return (RasterInfo) {
.offset = 0,
.length = 1,
.stretch_cycles = (_width - 1) * 4,
.repeat_lines = 1000,
};
Regardless of the difference in syntax, however, it's basically creating a temporary struct with members initialized as specified in the block, so this is roughly equivalent to:
// A possible definition of RasterInfo
// (but the real one might have more members or different order).
struct RasterInfo {
int offset;
int length;
int stretch_cycles;
int repeat_lines;
};
RasterInfo rasterize(unsigned, Pixel *target) {
*target = color;
RasterInfo r { 0, 1, (_width-1)*4, 1000};
return r;
}
The big difference (as you can see) is that designated initializers allow you to use member names to specify what initializer goes to what member, rather than depending solely on the order/position.
It is a C99 compound literal. This feature is specific to C99, but gcc and clang choose to implement it in C++ as well(as extension).
6.26 Compound Literals
ISO C99 supports compound literals. A compound literal looks like a
cast containing an initializer. Its value is an object of the type
specified in the cast, containing the elements specified in the
initializer; it is an lvalue. As an extension, GCC supports compound
literals in C90 mode and in C++, though the semantics are somewhat
different in C++.
Consider the following declaration:
#include <array>
struct X
{
//std::array<bool,3> arr={false,false,false};
bool brr[3]={false,false,false};
};
As is, it compiles normally by g++ 5.2. But if I uncomment the std::array, I get an error:
test.cpp:5:46: error: array must be initialized with a brace-enclosed initializer
std::array<bool,3> arr={false,false,false};
^
test.cpp:5:46: error: too many initializers for ‘std::array<bool, 3u>’
OTOH, this declaration works without problems inside main(). Also, the following initialization does work inside struct X:
std::array<bool,3> arr={{false,false,false}};
Why can't I use the simple initialization with single braces in struct definition?
This looks like a gcc bug see: Bug 65815 - brace elision doesn't work in NSDMI. The report says:
On Page 975 of "The C++ Programming Language", 4th edition, Bjarne
Stroustrup says:
"An array can be initialized by an initializer list: array a1 =
{ 1, 2, 3 };"
and Clang (V 3.5) accepts it. However, G++ 4.9.2 thinks this is an
error:
"error: array must be initialized with a brace-enclosed initializer
const std::array<double, 3> _ar0val = {1.0, -1.0, 1.0};"
The issue was narrowed down to the following test case:
struct array {
int data [2];
};
struct X {
array a = { 1, 2 };
};
It looks like the fix is in the head revision, the OPs code works in that revision, see it live.
As noted in the bug report using an inner set of braces is a possible work-around:
std::array<bool,3> arr={ {false,false,false} };
^ ^
While reading an article, I came across the following function:
SolidColor::SolidColor(unsigned width, Pixel color)
: _width(width),
_color(color) {}
__attribute__((section(".ramcode")))
Rasterizer::RasterInfo SolidColor::rasterize(unsigned, Pixel *target) {
*target = _color;
return {
.offset = 0,
.length = 1,
.stretch_cycles = (_width - 1) * 4,
.repeat_lines = 1000,
};
}
What is the author doing with the return statement? I haven't seen anything like that before, and I do not know how to search for it... Is it valid for plain C too?
Edit:
link to the original article
This isn't valid C++.
It's (sort of) using a couple features from C known as "compound literals" and "designated initializers", which a few C++ compilers support as an extension. The "sort of" comes from that fact that to be a legitimate C compound literal, it should have syntax that looks like a cast, so you'd have something like:
return (RasterInfo) {
.offset = 0,
.length = 1,
.stretch_cycles = (_width - 1) * 4,
.repeat_lines = 1000,
};
Regardless of the difference in syntax, however, it's basically creating a temporary struct with members initialized as specified in the block, so this is roughly equivalent to:
// A possible definition of RasterInfo
// (but the real one might have more members or different order).
struct RasterInfo {
int offset;
int length;
int stretch_cycles;
int repeat_lines;
};
RasterInfo rasterize(unsigned, Pixel *target) {
*target = color;
RasterInfo r { 0, 1, (_width-1)*4, 1000};
return r;
}
The big difference (as you can see) is that designated initializers allow you to use member names to specify what initializer goes to what member, rather than depending solely on the order/position.
It is a C99 compound literal. This feature is specific to C99, but gcc and clang choose to implement it in C++ as well(as extension).
6.26 Compound Literals
ISO C99 supports compound literals. A compound literal looks like a
cast containing an initializer. Its value is an object of the type
specified in the cast, containing the elements specified in the
initializer; it is an lvalue. As an extension, GCC supports compound
literals in C90 mode and in C++, though the semantics are somewhat
different in C++.
I have a simple couple lines of code
std::regex_iterator<std::string::const_iterator>
regit (attributesStart, _curIter, _attributeRegex),
regend(std::regex_iterator<std::string::const_iterator>);
while (regit != regend)
{
// [...]
}
The compiler complains about the while line, saying
Invalid operands to binary expression ('std::regex_iterator' and 'std::regex_iterator (*)(std::regex_iterator)')
Any idea why this is?
int main()
{
int i = 0, f(int);
f(i);
}
int f(int) {return 0;}
Is valid code. There is a block-scope function declaration of f inside main, in the same init-declarator-list as i, which is a usual int.
The same happens in your case, just in a more complicated manner. Here the function declared is regend and has a parameter of type std::regex_iterator<..> - you could have also deduced this by inspecting the error message. The problem wouldn't be solved by using std::regex_iterator<std::string::const_iterator>(); Then the parameter would be a pointer-to-function instead, but regend not a variable. Use uniform initialization (i.e. { and }) or double braces to avoid this.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why can't variables defined in a conditional be constructed with arguments?
Consider this simple example:
/*1*/ int main() {
/*2*/ for (int i(7); i;){break;}
/*3*/ if (int i(7)) {}
/*4*/ }
Why line-2 compiles just fine, whilst line-3 gives the error? This is little strange to me why if-statement is in this aspect treated worse than for-loop?
If this is compiler specific - I tested with gcc-4.5.1:
prog.cpp: In function 'int main()':
prog.cpp:3:7: error: expected primary-expression before 'int'
prog.cpp:3:7: error: expected ')' before 'int'
I was inspired by this question
[UPDATE]
I know this compiles just fine:
/*1*/ int main() {
/*2*/ for (int i = 7; i;){break;}
/*3*/ if (int i = 7) {}
/*4*/ }
[UPDATE2]
It seems to be purely academic question - but this could be extremely important for such types as std::unique_ptr<> which cannot be copied:
#include <memory>
int main() {
if (std::unique_ptr<int> i = new (std::nothrow) int(7)) {
}
if (std::unique_ptr<int> i(new (std::nothrow) int(7))) {
}
}
Neither of these two kinds are allowed. Not sure about new C++11 syntax {}?
The C++ standard doesn't provide a rationale but I would suspect that using the constructor notation could cause some inconsistencies. For example, since function declarations aren't allowed in the if statement, the most vexing parse would actually mean what was intended. For example:
int f(); // function declaration (simple form or the most vexing parse)
if (int f()) { // illegal in C++ but, when allowed, would be a zero-initialized int
}
In C++ 2011 you can use brace-initialization:
if (int x{f()}) {
...
}
Unfortunately, brace-initialization doesn't always mean the same thing as using constructor notation (I think it's called direct-initialization).
With respect to the update: you can use one of these:
if (std::unique_ptr<int> p = std::unique_ptr<int>(new int(1))) { ... }
if (auto p = std::unique_ptr<int>(new int(2))) { ... }
if (std::unique_ptr<int>{new int(3)}) { ... }
It seems there are plenty of options :-)
In terms of syntax, the reason is simple.
The first element of the for statement are statements. Statements can create variables as well as initialize them. The if conditional is a condition. The spec does define that a condition can create a variable, but only by = initialization.
Be glad the spec even allows you to create variables in an if at all. It has to have special grammar to even allow that ("condition" being different from "expression"), and that was probably something inherited from C.
That being said, C++11 does define a condition as being able to initialize a declaration with a braced-init-list (spec-speak for "initializer list"). So it should be legal. But since VS2012 doesn't support initializer lists yet...