digitalmars.dip.ideas - Inline Structs/Named Instances of Anonymous Structs
- Clouudy (56/56) Sep 23 C++ allows you to define named instances of anonymous structs and
- Clouudy (2/5) Sep 24 clear*
- claptrap (4/15) Sep 24 Why not just do this?
- Atila Neves (13/22) Sep 25 I don't think this is a feature that has positive ROI. This is
- Clouudy (15/38) Sep 25 In my opinion, I think that anything that makes things clearer is
- Quirin Schroll (27/83) Sep 25 In D, `inline` isn’t a keyword. Also, consider modifiers such as
- Clouudy (20/40) Sep 25 I've apparently just experienced the Mandela effect because I
- Nick Treleaven (2/13) Sep 26 That is already valid code, `point1` is a nested type.
- Paul Backus (8/18) Sep 25 In D, you can do this with a tuple:
- Steven Schveighoffer (13/31) Sep 25 Yes, but a) it's a terrible way to specify a struct, and b) it's
- Hipreme (5/31) Sep 26 I believe that feature is really useful. And those kind of
- Clouudy (9/12) Sep 27 IMO this should potentially be optional if the feature gets
- Ben Jones (5/8) Sep 26 The "allow struct declarations as template arguments" approach
- Paul Backus (11/28) Sep 27 I agree that it's unfortunate that we have to use a template to
- Clouudy (6/18) Sep 27 TBH I think built-in tuples could be a good way of doing simple
- Paul Backus (17/22) Sep 27 If you have complex nested types that are multiple layers deep,
- Dennis (71/76) Sep 27 I think the example with 'point' is too small to represent a
- Nick Treleaven (15/23) Sep 28 If we wanted to support this, perhaps have an anonymous struct
- Juraj (19/33) Sep 28 I thought, supporting the C syntax would be good enough, example:
- Richard (Rikki) Andrew Cattermole (6/6) Sep 26 A couple of thoughts:
C++ allows you to define named instances of anonymous structs and classes: ```cpp struct Container { struct { int x; int y; } point; void print() const { std::cout << "Point: (" << point.x << ", " << point.y << ")\n"; } }; int main() { Container c; c.point.x = 10; c.point.y = 20; c.print(); } ``` In D, the only way to approximate this is to define a type and then instantiate an instance of it within the struct/class: ```d struct Container { struct Point { int x; int y; } Point point; } ``` This approximation is superfluous; why specify a type that is never meant to be used by users? When nested data structures become larger and larger, this type of declare-and-instantiate pattern becomes an annoyance to write, and hard to read (as the type is separated from the declaration). Named instances of anonymous structs allow for less complexity, less verbosity, and less ambiguity over intent. The information about the variable is right next to the instantiation, and the code clearly communicates that point is simply a nested group within container, not an individual type that can/should be used on its own. Since structs don't end with semicolons in D, a different syntax must be used. I believe that prefixing the struct with the `inline` keyword is the best fit: ```d struct Container { inline struct point { int x; int y; } } ``` This doesn't add any extra keywords, and I believe it makes it clean that the struct is a single instance that is declared in the enclosing scope.
Sep 23
On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:This doesn't add any extra keywords, and I believe it makes it clean that the struct is a single instance that is declared in the enclosing scope.clear*
Sep 24
On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:```d struct Container { inline struct point { int x; int y; } } ``` This doesn't add any extra keywords, and I believe it makes it clean that the struct is a single instance that is declared in the enclosing scope.Why not just do this? int point_x; int point_y;
Sep 24
On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:C++ allows you to define named instances of anonymous structs and classes: ```cpp struct Container { struct { int x; int y; } point; [...]I don't think this is a feature that has positive ROI. This is ugly, but... ```d struct Container { private struct Point { // private means users can't use it directly int x; int y; } Point point; // no newline, and an "extra" `Point` compared to the proposal ``` Alternatives include string/template mixins.
Sep 25
On Thursday, 25 September 2025 at 09:08:34 UTC, Atila Neves wrote:On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:In my opinion, I think that anything that makes things clearer is something that should be at the very least looked into as a proposal. Because ven with the `private struct Point` method you outlined, maintainers can still get confused about whether the `Point` type is meant to be used multiple times. Of course if it's discovered that it's too complex to implement or gets changed along the way then yes, I would agree about the ROI being unjustifiable. But we could at least try to investigate whether that's the case. I can tell you that from my personal experience that I did find myself wishing for this feature on one project, and that Steven also talked about adding a similar feature at DConf before as well. So at least some people think there's a problem, even if my solution isn't the best one.C++ allows you to define named instances of anonymous structs and classes: ```cpp struct Container { struct { int x; int y; } point; [...]I don't think this is a feature that has positive ROI. This is ugly, but... ```d struct Container { private struct Point { // private means users can't use it directly int x; int y; } Point point; // no newline, and an "extra" `Point` compared to the proposal ``` Alternatives include string/template mixins.
Sep 25
On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:C++ allows you to define named instances of anonymous structs and classes: ```cpp struct Container { struct { int x; int y; } point; void print() const { std::cout << "Point: (" << point.x << ", " << point.y << ")\n"; } }; int main() { Container c; c.point.x = 10; c.point.y = 20; c.print(); } ``` In D, the only way to approximate this is to define a type and then instantiate an instance of it within the struct/class: ```d struct Container { struct Point { int x; int y; } Point point; } ``` This approximation is superfluous; why specify a type that is never meant to be used by users? When nested data structures become larger and larger, this type of declare-and-instantiate pattern becomes an annoyance to write, and hard to read (as the type is separated from the declaration). Named instances of anonymous structs allow for less complexity, less verbosity, and less ambiguity over intent. The information about the variable is right next to the instantiation, and the code clearly communicates that point is simply a nested group within container, not an individual type that can/should be used on its own. Since structs don't end with semicolons in D, a different syntax must be used. I believe that prefixing the struct with the `inline` keyword is the best fit: ```d struct Container { inline struct point { int x; int y; } } ``` This doesn't add any extra keywords, and I believe it makes it clean that the struct is a single instance that is declared in the enclosing scope.In D, `inline` isn’t a keyword. Also, consider modifiers such as `private` or `static` that you might want to apply to the newly defined variables, but not the struct itself. How would you do that? The syntax I’d say works better is: ```d struct Container { struct private static Point point1, point2 { int x; int y; } } ``` Essentially, if after `struct` there’s one identifier before the opening brace, that’s a simple struct definition. Alternatively, there could be a variable declaration with attributes and whatnot, followed by the declaration block defining the struct; then, the identifier of the type of those variables is the name of the struct being defined. I think it could be implemented by a lowering in the parser if we wanted it cheap. However, it doesn’t really mix and match. Consider making `Point` a struct template: Where would the template parameters even go? I’m agree with Átila: It’s not great code. It does two different things at once, and the workaround is not only easy, but arguably better code.
Sep 25
On Thursday, 25 September 2025 at 13:21:59 UTC, Quirin Schroll wrote:In D, `inline` isn’t a keyword.I've apparently just experienced the Mandela effect because I could have sworn that it was. But apparently I just got confused with C/C++ after messing with building gcc for too long.The syntax I’d say works better is: ```d struct Container { struct private static Point point1, point2 { int x; int y; } } ``` Essentially, if after `struct` there’s one identifier before the opening brace, that’s a simple struct definition. Alternatively, there could be a variable declaration with attributes and whatnot, followed by the declaration block defining the struct; then, the identifier of the type of those variables is the name of the struct being defined. I think it could be implemented by a lowering in the parser if we wanted it cheap. However, it doesn’t really mix and match. Consider making `Point` a struct template: Where would the template parameters even go?I think that you're heading in the right direction, but I don't necessarily think that you need the type if it's just a singleton. It may be useful to include it optionally for reflection purposes though. I would be satisfied with something similar like this, with the extra modifiers either before or after the `struct` keyword, whatever is easier to implement: ```d struct Container { struct point1 { int x; int y; } } ```
Sep 25
On Thursday, 25 September 2025 at 20:03:27 UTC, Clouudy wrote:I would be satisfied with something similar like this, with the extra modifiers either before or after the `struct` keyword, whatever is easier to implement: ```d struct Container { struct point1 { int x; int y; } } ```That is already valid code, `point1` is a nested type.
Sep 26
On Friday, 26 September 2025 at 17:22:55 UTC, Nick Treleaven wrote:On Thursday, 25 September 2025 at 20:03:27 UTC, Clouudy wrote:Damn you're right. IDK why I keep accidentally doing that.I would be satisfied with something similar like this, with the extra modifiers either before or after the `struct` keyword, whatever is easier to implement: ```d struct Container { struct point1 { int x; int y; } } ```That is already valid code, `point1` is a nested type.
Sep 27
On Saturday, 27 September 2025 at 16:29:38 UTC, Clouudy wrote:Damn you're right. IDK why I keep accidentally doing that.What about this?: ```d struct Container { struct point is { int x; int y; } } ```
Sep 27
On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:C++ allows you to define named instances of anonymous structs and classes: ```cpp struct Container { struct { int x; int y; } point; }; ```In D, you can do this with a tuple: ```d import std.typecons: Tuple; struct Container { Tuple!(int, "x", int, "y") point; } ```
Sep 25
On Thursday, 25 September 2025 at 13:34:15 UTC, Paul Backus wrote:On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:Yes, but a) it's a terrible way to specify a struct, and b) it's a heck of a lot more compile time to process. Note that we have anonymous classes for `new` expressions. I think the idea is sound *and useful*, I'm not sure about the syntax. In fact, the syntax is the most difficult part to figure out. As for just being able to put a struct definition wherever you need a type, this gives a lot more power for modeling than using DSL definitions like `tuple`. I talked about this kind of wishlist feature in my dconf online 2022 talk: https://youtu.be/GFvh6Hbc-3k?t=1730 -SteveC++ allows you to define named instances of anonymous structs and classes: ```cpp struct Container { struct { int x; int y; } point; }; ```In D, you can do this with a tuple: ```d import std.typecons: Tuple; struct Container { Tuple!(int, "x", int, "y") point; } ```
Sep 25
On Thursday, 25 September 2025 at 22:11:50 UTC, Steven Schveighoffer wrote:On Thursday, 25 September 2025 at 13:34:15 UTC, Paul Backus wrote:I believe that feature is really useful. And those kind of unreachable structures could have their typeinfo elided, further making the binary slimmer.On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:Yes, but a) it's a terrible way to specify a struct, and b) it's a heck of a lot more compile time to process. Note that we have anonymous classes for `new` expressions. I think the idea is sound *and useful*, I'm not sure about the syntax. In fact, the syntax is the most difficult part to figure out. As for just being able to put a struct definition wherever you need a type, this gives a lot more power for modeling than using DSL definitions like `tuple`. I talked about this kind of wishlist feature in my dconf online 2022 talk: https://youtu.be/GFvh6Hbc-3k?t=1730 -Steve[...]In D, you can do this with a tuple: ```d import std.typecons: Tuple; struct Container { Tuple!(int, "x", int, "y") point; } ```
Sep 26
On Friday, 26 September 2025 at 13:40:25 UTC, Hipreme wrote:I believe that feature is really useful. And those kind of unreachable structures could have their typeinfo elided, further making the binary slimmer.IMO this should potentially be optional if the feature gets adopted. Even if it'd be annoying to access the TypeInfo (as in `Construct.point.typeinfo`), you may still want it for some use cases. For me, I would need it to be able to serialize these internal structs properly. Maybe it could be included as a compiler switch, or if you want the best of both worlds you could use a pragma or have it automatically check which structs need to have typeinfo.
Sep 27
On Thursday, 25 September 2025 at 22:11:50 UTC, Steven Schveighoffer wrote:I talked about this kind of wishlist feature in my dconf online 2022 talk: https://youtu.be/GFvh6Hbc-3k?t=1730 -SteveThe "allow struct declarations as template arguments" approach seems pretty simple to add. Did you look into it at all? I agree a general syntax would be more challenging
Sep 26
On Thursday, 25 September 2025 at 22:11:50 UTC, Steven Schveighoffer wrote:On Thursday, 25 September 2025 at 13:34:15 UTC, Paul Backus wrote:I agree that it's unfortunate that we have to use a template to create a tuple. The solution to that is to add built-in tuples. Then we'll be able to write, for example, ```d struct Container { (int x, int y) point; } ```In D, you can do this with a tuple: ```d import std.typecons: Tuple; struct Container { Tuple!(int, "x", int, "y") point; } ```Yes, but a) it's a terrible way to specify a struct, and b) it's a heck of a lot more compile time to process. Note that we have anonymous classes for `new` expressions.As for just being able to put a struct definition wherever you need a type, this gives a lot more power for modeling than using DSL definitions like `tuple`.Built-in tuples also solve this problem.
Sep 27
On Saturday, 27 September 2025 at 12:36:34 UTC, Paul Backus wrote:I agree that it's unfortunate that we have to use a template to create a tuple. The solution to that is to add built-in tuples. Then we'll be able to write, for example, ```d struct Container { (int x, int y) point; } ```TBH I think built-in tuples could be a good way of doing simple constructs, but I worry about whether this would work for complex nested types that are layers upon layers deep. Because then it would potentially devolve into LISP-like constructs, which is a massive break from how D reads.As for just being able to put a struct definition wherever you need a type, this gives a lot more power for modeling than using DSL definitions like `tuple`.Built-in tuples also solve this problem.
Sep 27
On Saturday, 27 September 2025 at 23:17:21 UTC, Clouudy wrote:TBH I think built-in tuples could be a good way of doing simple constructs, but I worry about whether this would work for complex nested types that are layers upon layers deep. Because then it would potentially devolve into LISP-like constructs, which is a massive break from how D reads.If you have complex nested types that are multiple layers deep, you should probably be separating those layers into their own named structs anyway rather than defining everything inline. Honestly, even if we had built-in tuples, I would prefer to write your original example like this: ```d struct Point { int x; int y; } struct Container { Point point; } ``` Adding additional levels of nesting in order to save a few lines is rarely a good idea.
Sep 27
On Sunday, 28 September 2025 at 00:25:41 UTC, Paul Backus wrote:If you have complex nested types that are multiple layers deep, you should probably be separating those layers into their own named structs anyway rather than defining everything inline. Honestly, even if we had built-in tuples, I would prefer to write your original example like this:I think the example with 'point' is too small to represent a realistic use case for this feature, so I want to provide a real struct example to make it easier to discuss. This code is copied directly from my code which parses a .json file of a font atlas generated by [msdf-atlas-gen](https://github.com/Chlumsky/msdf-atlas-gen): ```D struct FontJson { Atlas atlas; struct Atlas { double size; // pixels per em int width; int height; Grid grid; struct Grid { int cellWidth; int cellHeight; int rows; int columns; double originY = 0; } } Metrics metrics; struct Metrics { double ascender = 0; double descender = 0; double underlineY = 0; double underlineThickness = 0; } Glyph[] glyphs; struct Glyph { int unicode; double advance = 0; struct PlaneBounds { double left = 0; double top = 0; double right = 0; double bottom = 0; } PlaneBounds planeBounds; struct AtlasBounds { double left = 0; double top = 0; double right = 0; double bottom = 0; } AtlasBounds atlasBounds; } struct Kerning { int unicode1; int unicode2; double advance = 0; } Kerning[] kerning; } ``` I don't think it's too bad currently, but I can see anonymous structs being useful here. I wouldn't replace the structs with tuples though. I also don't really want to flatten the nesting, I like how the nested structs model the json structure very closely, and don't want the structs to be used outside the JSON parsing.
Sep 27
On Sunday, 28 September 2025 at 00:51:19 UTC, Dennis wrote:Metrics metrics; struct Metrics { double ascender = 0; double descender = 0; double underlineY = 0; double underlineThickness = 0; }If we wanted to support this, perhaps have an anonymous struct expression: ```d auto metrics = struct { double ascender = 0; double descender = 0; double underlineY = 0; double underlineThickness = 0; }(); ``` This would be the struct counterpart to a *NewAnonClassExpression*. Though [for consistency](https://dlang.org/spec/class.html#anonymous) perhaps it should be `struct (NamedArgumentList) { DeclDefs }`.
Sep 28
On Sunday, 28 September 2025 at 11:59:42 UTC, Nick Treleaven wrote:If we wanted to support this, perhaps have an anonymous struct expression: ```d auto metrics = struct { double ascender = 0; double descender = 0; double underlineY = 0; double underlineThickness = 0; }(); ``` This would be the struct counterpart to a *NewAnonClassExpression*. Though [for consistency](https://dlang.org/spec/class.html#anonymous) perhaps it should be `struct (NamedArgumentList) { DeclDefs }`.I thought, supporting the C syntax would be good enough, example: ```d struct { int x, y, z; } anons[8], other_var; ``` But if D already has anonymous class expression, than it would be confusing. Anyway, what I find intriguing is something like: ```d MyTemplatedTHing!( struct { int i, j, k; auto somethingUseful => "hmmm"; } )(); ```
Sep 28
A couple of thoughts: 1. The examples I've seen have mostly centered around POD's, these should be solved via the use of tuples. I suggest revisiting this topic after tuples are designed&implemented, to know where they are fail. 2. Passing a struct as a template argument is indeed a valuable abstraction to offer. Just like anonymous classes.
Sep 26