Proposal to add C99 designated initializers to C++

1 530 vues
Accéder directement au premier message non lu

Daryle Walker

non lue,
8 nov. 2013, 05:48:1508/11/2013
à std-pr...@isocpp.org
[Excuse any mistakes from me doing an all-nighter to get this proposal out here.]

I made a proposal on how to have C99's designated initializers in C++. In C, you can precede an initializer inside a struct/union or array aggregate with the access method ([x] for array elements, ".Whatever" for structs and unions, and can mix & match) to get to a specific subobject to be initialized. I had to create the text nearly from scratch since C and C++ use very different ways to express the same concepts. Plus C++ has to worry about non-dumb classes.

I'd especially like if the crew for Microsoft Visual Studio 2013 took a look to see if I described your practice correctly.

Daryle W.

Roman Perepelitsa

non lue,
8 nov. 2013, 06:27:4008/11/2013
à std-pr...@isocpp.org
FYI: clang supports designated initializers for aggregates when compiling in C++11 mode.

Roman Perepelitsa.


2013/11/8 Daryle Walker <dar...@gmail.com>

--
 
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Daryle Walker

non lue,
10 nov. 2013, 18:58:5110/11/2013
à std-pr...@isocpp.org
OK, so could representatives from both the Visual Studio and Clang teams take a look? (If GCC also does it, or wants to, they can take a look too.)

Daryle W.

Richard Smith

non lue,
10 nov. 2013, 21:42:1410/11/2013
à std-pr...@isocpp.org
The wording isn't quite right in a few ways, but CWG will deal with that if the proposal gets past EWG. I would suggest not allowing the overriding that C allows -- it's not necessary to get the majority of the value from the feature, and it provides very surprising behavior. Just make it ill-formed.

You should also define how the initializers are sequenced. In C they are unsequenced, which doesn't match C++'s more-specified rules for braced initializers. I would suggest making them sequenced in member order (rather than, for instance, in order of appearance), so that designated initialization exactly matches the behavior of constructor initializers.

Thiago Macieira

non lue,
11 nov. 2013, 02:16:1111/11/2013
à std-pr...@isocpp.org
On domingo, 10 de novembro de 2013 18:42:14, Richard Smith wrote:
> You should also define how the initializers are sequenced. In C they are
> unsequenced, which doesn't match C++'s more-specified rules for braced
> initializers. I would suggest making them sequenced in member order (rather
> than, for instance, in order of appearance), so that designated
> initialization exactly matches the behavior of constructor initializers.

Shouldn't compilers emit then a diagnostic if you fail to sequence them in the
right order?

struct S {
int a, b;
};

int i;
int f(int *i) { *i = 42; return 47; }

struct S g()
{
struct S s = { .b = f(i), .a = i };
return s;
}

When compiled as C99, the above returns { .a = 42, .b = 47 }.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
signature.asc

Richard Smith

non lue,
12 nov. 2013, 20:36:0312/11/2013
à std-pr...@isocpp.org
On Sun, Nov 10, 2013 at 11:16 PM, Thiago Macieira <thi...@macieira.org> wrote:
On domingo, 10 de novembro de 2013 18:42:14, Richard Smith wrote:
> You should also define how the initializers are sequenced. In C they are
> unsequenced, which doesn't match C++'s more-specified rules for braced
> initializers. I would suggest making them sequenced in member order (rather
> than, for instance, in order of appearance), so that designated
> initialization exactly matches the behavior of constructor initializers.

Shouldn't compilers emit then a diagnostic if you fail to sequence them in the
right order?

Sure, they can, just as they do for constructor initializers. And, just as with constructor initializers, we need to sequence the subobject initialization in subobject order, in order to get destructors to run in the right order.
 
struct S {
    int a, b;
};

int i;
int f(int *i) { *i = 42; return 47; }

struct S g()
{
    struct S s = { .b = f(i), .a = i };

I assume you meant f(&i) here?

    return s;
}

When compiled as C99, the above returns { .a = 42, .b = 47 }.

Only by chance. C99 6.7.8/23 says: "The order in which any side effects occur among the initialization list expressions is unspecified."

Thiago Macieira

non lue,
13 nov. 2013, 17:53:1113/11/2013
à std-pr...@isocpp.org
On terça-feira, 12 de novembro de 2013 17:36:03, Richard Smith wrote:
> > Shouldn't compilers emit then a diagnostic if you fail to sequence them in
> > the
> > right order?
>
> Sure, they can, just as they do for constructor initializers. And, just as
> with constructor initializers, we need to sequence the subobject
> initialization in subobject order, in order to get destructors to run in
> the right order.

Good.

>
> > struct S {
> >
> > int a, b;
> >
> > };
> >
> > int i;
> > int f(int *i) { *i = 42; return 47; }
> >
> > struct S g()
> > {
> >
> > struct S s = { .b = f(i), .a = i };
>
> I assume you meant f(&i) here?

Yes. I had a reference first, then I changed to a pointer so I could compile in
C mode...

> return s;
>
> > }
> >
> > When compiled as C99, the above returns { .a = 42, .b = 47 }.
>
> Only by chance. C99 6.7.8/23 says: "The order in which any side effects
> occur among the initialization list expressions is unspecified."

Ah, ok. So my code actually has undefined behaviour. For C++, though, we do
have an order and we also have to initialise sub-objects in the correct order.

Daryle Walker

non lue,
14 nov. 2013, 05:57:1014/11/2013
à std-pr...@isocpp.org
I uploaded a new version (2013-Nov-14). I completely redid the new paragraphs. I went beyond paragraph 4 of section 8.5.1 and covered the entire section. Am I justified in removing the sections I suggested? Should the ones I didn't mention be altered? What about 8.5.1/8? Is 8.5.1/I took much of a ripoff of 8.5.4/4?

I added changes to sections 8.5.4 and 14.5.3, but I don't know if those are good enough. And what about 8.5.1/8?

I need more examples, especially ones that replace that deleted ones. (Make sure to mention which new section/paragraph they go with.)

Daryle Walker

non lue,
17 nov. 2013, 12:11:0217/11/2013
à std-pr...@isocpp.org
I uploaded a third version (2013-Nov-17). I shortened at lot of the prose about which sub-objects can be targeted and how to find the target object for a given initializer into two huge paragraphs, and they're more integrated. I got rid of some of my more clunky terms, plus "brace elision." I hope you can understand the text that replaces the brace elision idea.

Matthew Fioravante

non lue,
24 avr. 2015, 22:25:5524/04/2015
à std-pr...@isocpp.org
Did this proposal ever get anywhere? Not only does it improve C compatibility but the feature also has a nice property that it can be automatically used to design functions with named arguments:

#include <experimental/string_view>
#include <vector>


using StringView = std::experimental::string_view;


struct splitParams {
 
char delim = ',';
 
char quote = '"';
};


std
::vector<StringView> split(StringView s, splitParams p);


auto foo() {
 
return split("a,b,c", { .delim = ',', .quote = '"'});
}

There is a slight hassle that you have to specify the parameters in member order or suffer a compiler warning, but it can make a clean procedural interface (or a constructor with a lot of params such as a gui widget). Many other languages use functions with many parameters because they have named function arguments. One good example is python's argparse library. Functions with many parameters are not a feasble design idiom in C++ because we lack named function parameters.

We could suggest implementations silence the warning when the constructor arguments are trivially destructible and the init/destroy order actually doesn't matter or doesn't exist.

struct Params {
 
int x = 0;
 
int y = 0;
 
string_view s;
 
array_view<Widget> widgets; //Could use array_view also
};
void f(const Params& p);

f
({.s = "hello", .x = 1}); //Ok

David Krauss

non lue,
24 avr. 2015, 22:54:2624/04/2015
à std-pr...@isocpp.org

On 2015–04–25, at 10:25 AM, Matthew Fioravante <fmatth...@gmail.com> wrote:

Did this proposal ever get anywhere?

The word “designated” doesn’t appear in the indexes of papers from 2013 or 2014, so I guess not.

By the way, as a further extension, it would be nice to have direct-initialization of aggregate members using designated initializer syntax.

struct s {
    explicit s( int ) {}
};

struct c {
    s m;
};

Currently, initializing c requires naming the type and accessing the copy/move constructor:

c q { s( 5 ) };

If s is not movable, then aggregate initialization is impossible. The obvious semantics of direct-initialization of a designated initializer are optimal:

c q { .m( 5 ) };

This is not yet supported by Clang, though.

David Krauss

non lue,
24 avr. 2015, 23:05:1224/04/2015
à std-pr...@isocpp.org

On 2015–04–25, at 10:54 AM, David Krauss <pot...@gmail.com> wrote:

By the way, as a further extension, it would be nice to have direct-initialization of aggregate members using designated initializer syntax.

Oh, this is actually already in the linked proposal.

Protip: When proposing something not already familiar to the committee (or even in any case, just as a courtesy), summarize all intended changes in English. Don’t introduce new concepts in standardese.

David Krauss

non lue,
25 avr. 2015, 03:39:3525/04/2015
à std-pr...@isocpp.org
(Replying to an old post.)

On 2013–11–13, at 9:36 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

And, just as with constructor initializers, we need to sequence the subobject initialization in subobject order, in order to get destructors to run in the right order.

There’s still a decision to be made, whether side effects of aggregate initializers are sequenced in subobject order or in order of appearance, per [dcl.init.list] §8.5.4/4.

Currently it’s unspecified (as far as I know) that the initialization of an aggregate subobject is part of the full-expression of an aggregate initializer, or otherwise subject to the sequencing rule of §8.5.4/4. On one hand, it’s usually assumed to be the case, and §8.5.1/13 comes pretty close to specifying it. On the other, code with such an assumption is brittle. It will only work with aggregate initialization and not with constructors, which don’t begin execution until all parameters are initialized, after the entire list is evaluated. And the entire object doesn’t begin to exist until all its subobjects are created, so you can’t evaluate an access expression within its own initializer in the first place.

Different order between evaluation and initialization is easy to model with copy-initialization, because you can generate the temporary objects in list order and then use them in subobject order. It might be impossible with direct-initialization, though.

Wouldn’t it be reasonable to require that designated initializers using direct-initialization appear in order? Essentially, require the ordering warning to be implemented, and upgrade it to an error upon direct-initialization.
Répondre à tous
Répondre à l'auteur
Transférer
0 nouveau message