www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Inline Structs/Named Instances of Anonymous Structs

reply Clouudy <Swergers123 gmail.com> writes:
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
next sibling parent Clouudy <Swergers123 gmail.com> writes:
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
prev sibling next sibling parent claptrap <clap trap.com> writes:
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
prev sibling next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
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
parent Clouudy <Swergers123 gmail.com> writes:
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:
 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.
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.
Sep 25
prev sibling next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
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
parent reply Clouudy <Swergers123 gmail.com> writes:
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
parent reply Nick Treleaven <nick geany.org> writes:
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
parent reply Clouudy <Swergers123 gmail.com> writes:
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:
 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.
Damn you're right. IDK why I keep accidentally doing that.
Sep 27
parent Clouudy <Swergers123 gmail.com> writes:
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
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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:
 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; } ```
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
Sep 25
next sibling parent reply Hipreme <msnmancini hotmail.com> writes:
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:
 On Wednesday, 24 September 2025 at 02:58:53 UTC, Clouudy wrote:
 [...]
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. 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
I believe that feature is really useful. And those kind of unreachable structures could have their typeinfo elided, further making the binary slimmer.
Sep 26
parent Clouudy <Swergers123 gmail.com> writes:
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
prev sibling next sibling parent Ben Jones <fake fake.fake> writes:
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

 -Steve
The "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
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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.
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; } ```
 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
parent reply Clouudy <Swergers123 gmail.com> writes:
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;
 }
 ```

 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.
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.
Sep 27
parent reply Paul Backus <snarwin gmail.com> writes:
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
parent reply Dennis <dkorpel gmail.com> writes:
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
parent reply Nick Treleaven <nick geany.org> writes:
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
parent Juraj <junk vec4.xyz> writes:
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
prev sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
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