Getting the default value of a class member field

WebFreak001 d.forum at webfreak.org
Fri Dec 2 09:39:24 UTC 2022


On Friday, 2 December 2022 at 04:14:37 UTC, kinke wrote:
> On Friday, 2 December 2022 at 00:24:44 UTC, WebFreak001 wrote:
>> I want to use the static initializers (when used with an UDA) 
>> as default values inside my SQL database.
>>
>> See 
>> https://github.com/rorm-orm/dorm/blob/a86c7856e71bbc18cd50a7a6f701c325a4746518/source/dorm/declarative/conversion.d#L959
>>
>> With my current design it's not really possible to move it out 
>> of compile time to runtime because the type description I 
>> create there gets serialized and output for use in another 
>> program (the migrator). Right now it's simply taking the 
>> compile time struct I generate and just dumping it without 
>> modification into a JSON serializer.
>>
>> [...]
>
> Okay, so what's blocking CTFE construction of these models? 
> AFAICT, you have a templated base constructor in `Model`, which 
> runs an optional `@constructValue!(() => Clock.currTime + 
> 4.hours)` lambda UDA for all fields of the derived type. Can't 
> you replace all of that with a default ctor in the derived type?
>
> ```
> class MyModel : Model {
>     int x = 123;        // statically initialized
>     SysTime validUntil; // dynamically initialized in ctor
>
>     this() {
>         validUntil = Clock.currTime + 4.hours;
>     }
> }
> ```
>
> Such an instance should be CTFE-constructible, and the valid 
> instance would feature the expected value for the `validUntil` 
> field. If you need to know about such dynamically generated 
> fields (as e.g. here in this time-critical example), an option 
> would be a `@dynamicallyInitialized` UDA. Then if you 
> additionally need to be able to re-run these current 
> `@constructValue` lambdas for an already constructed instance, 
> you could probably go with creating a fresh new instance and 
> copying over the fresh new field values.

constructValue is entirely different than this default value. 
It's not being put into the database, it's just for the library 
to send it when it's missing. (so other apps accessing the 
database can't use the same info) - It's also still an open 
question if it even gives any value because it isn't part of the 
DB.

To support constructValues I iterate over all DB fields and run 
their constructors. I implemented listing the fields with a 
ListFields!T template. However now when I want to generate the DB 
field information I also use this same template to list all 
columns to generate attributes, such as what default value to put 
into SQL. Problem here is that that tries to call the 
constructor, which wants to iterate over the fields, while the 
fields are still being iterated. (or something similar to this)

Basically in the end the compiler complained about forward 
reference / the size of the fields not being known when I put in 
a field of a template type that would try to use the same 
ListFields template on the class I put that value in.

Right now I hack around this by adding an `int cacheHack` 
template parameter to ListFields, which simply does nothing. 
However this fixes that the compiler thinks the template isn't 
usable and everything seems to work with this.

Anyway this is all completely different from the default value 
thing, because I already found workarounds and changed some 
internals a bit to support things like cyclic data structures.

I would still like a way to access the initializer from class 
fields, and it would be especially cool would be to know if they 
are explicitly set. Right now I have this weird and heavy 
`@defaultValue(...)` annotation that's basically the same as `= 
...;`, that I just needed to add to make it possible to use 
T.init as default value in the DB as well, but not force it. My 
code uses `@defaultFromInit` to make it use the initializer, but 
it would be great if I didn't need this at all. (although because 
of my cyclic template issues it might break again and be unusable 
for me)


More information about the Digitalmars-d-learn mailing list