On Tuesday, September 16, 2014 3:38:32 AM UTC-4, David Brown wrote:
> This is all related to a more general point - C++ does not support
> designated initialisers that C introduced in C99. (gcc's extensions
> here are just a refinement of that.) If you ever get an answer from the
> standards folk, the usual reasoning is that C++ constructors provide a
> better system than C's designated initialisers, and thus you should use
> constructors.
Compilers are tools. My thinking is this: I should not have to manually
do something the compiler can do for me. :-) When there is no ambiguity
in the initialization type, for example, the compiler should be able to
receive whatever I pass to it, and assemble it for me, at compile time
so I don't have to execute any code at runtime, nor perform the manual
steps of writing code which is executed at runtime.
struct SExample
{
union {
int i;
float f;
};
};
SExample myArray[] = {
{ 10 }, // Initialized into i
{ 29.7f } // Initialized into f
};
If there is any ambiguity about the type, then it could fall back to a
top-down method where it maps into the first one which is close, or
would use an algorithm, such as only directly mapping if the two or
twelve conflicting types available actually occupy the same memory space
within the union (because in such a case it really doesn't matter which
one it maps to because they all map to the same place in memory).
That also brings up another need as I see it... the ability to
explicitly indicate where a union should fall at compile time, and
without having to pad it manually.
struct SExample2
{
union {
double d;
char c;
};
};
I should be able to place these wherever I want them. In fact, for some
low-level purposes this would be desirable.
struct SExample2
{
union {
double d (|4,#,8|);
char c (|top|);
};
};
Something here like:
(|4,#,8|) indicates 4 bytes padding before, the # of bytes for the thing,
and then 8 bytes padding after. So in this case d would be initialized
to consume 20 bytes, with 4 bytes padding before the variable in memory,
then the 8 bytes d occupies, and then 8 bytes of padding after.
(|top|) would indicate that c should go to the top of the memory block
(to the greatest/highest memory address based on the other union members).
The storage for such a union would then be (in bytes):
d: [pad1 4:0,0,0,0][d 8:0,0,0,0,0,0,0,0][pad2 8:0,0,0,0,0,0,0,0]
c: [c 1:0][pad 19:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
In addition, I should be able to manually indicate where I want something
to go to complete override its basic type, such as using an initializer
with a cask after, as in:
SExample myArray[] = {
{ 10 (|#,2,3|) }, // Initialized as 16-bits at offset 3
{ 29.7f } // Initialized into f
};
This would have the effect of increasing the sizeof(SExample) to 5
bytes because the position of the example here would run to offsets
3..4, which are bytes 4 and 5.
> Of course, this argument is nonsense - constructors are more flexible,
> and better for some purposes, but take more effort to write, can't be
> used for compile-time initialisation (perhaps they can now with
> "constexpr"?), and are incompatible with C. Each method has its
> advantages and disadvantages, and people want both.
I don't think anyone would suggest taking away constructors. :-)
They have their place for sure. But, right now, compile time
initialization to non-topmost union members is impossible without
reaching into a compiler extension like gcc's name designation.
That hindrance is unnecessary, and hampers the use of unions, a
perfectly valid tool in the developer's arsenal.
> The C++ standards folk really need to take a look at C11 and update
> their C compatibility to take advantage of the improvements in C since
> the schism. There is not much to do, but a few points would make a big
> difference - designated initialisers is, IMHO, the most important. And
> since gcc is quite happy with designated initialisers in C++, it seems
> there would be no problem adding it to the language.
Designated initializers should only be needed in special cases. But I
agree they should be available.
> Initialising union members automatically by type is another matter. It
> might work okay in simple cases, but it could quickly get complicated
> and ambiguous, especially once classes with constructors got involved.
> Designated initialisers are conceptually simpler, more explicit, and
> more flexible.
The compiler could set limits on things to keep them simple. If it is not
something that can be explicitly resolved at compile time using only basic
translation, then it is required to perform the operation at runtime.
However, even in such a case the compiler could still identify that it
is of such a kind and automatically generate the code so that it is
performed at runtime, even though it appears as though it was done
at compile time. The only issue there would be object code which contains
incomplete data as it has not yet initialized itself as one might
otherwise expect it should be given that the initialization was
performed by the developer at compile time.
I think that's a minor issue though. And I can't imagine a case where
initialization into a union member is so complex that it cannot be
resolved at compile time, or it can't have the code to automatically
populate into the appropriate location be automatically inserted and
performed at runtime.
Can you think of such an example?