Re: Getting the default value of a class member field

2022-12-02 Thread WebFreak001 via Digitalmars-d-learn

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)


Re: Getting the default value of a class member field

2022-12-01 Thread kinke via Digitalmars-d-learn
You could potentially skip running the `@constructValue` lambdas 
in the ctor via `if (__ctfe)`, if skipping this extra init is 
acceptable for CTFE instances.


Re: Getting the default value of a class member field

2022-12-01 Thread kinke via Digitalmars-d-learn

On Friday, 2 December 2022 at 04:14:37 UTC, kinke wrote:

[...]
Such an instance should be CTFE-constructible, and the valid 
instance would feature the expected value for the `validUntil` 
field. [...]


Oh well, that time-sensitive example clearly isn't CTFE-able. :D 
- If that's the primary use case for these `@constructValue` 
UDAs, then yeah, this makes things more complicated.





Re: Getting the default value of a class member field

2022-12-01 Thread kinke via Digitalmars-d-learn

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.


Re: Getting the default value of a class member field

2022-12-01 Thread WebFreak001 via Digitalmars-d-learn

On Thursday, 1 December 2022 at 23:02:31 UTC, kinke wrote:

On Thursday, 1 December 2022 at 08:09:05 UTC, WebFreak001 wrote:
[...]

AFAIK, there is no way. Unlike a struct's init symbol, a class' 
one doesn't necessarily represent a valid instance state - it's 
just the raw payload before invoking a ctor (which constructs a 
valid instance), and the state 'dead' memory is reset to after 
finalizing an object instance (to prevent dead pointers keeping 
other GC refs alive).


If the ctor worked at CTFE, one could use:
```d
int get() {
scope x = new X;
return x.x;
}
enum bla = get();
```
to get the `x` value of a *valid* instance, which might be 
different than the static initializer (if modified in the ctor).


I guess the main question is why do you require the static 
initializers of these fields at compile-time. 
`__traits(initSymbol)` was added to aid in manual blitting at 
runtime.


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.


I might be abusing classes a little bit here, but they provide 
the easiest way to do a variety of things:
- `is(T : Model)` and especially type specialization like `void 
foo(T : Model)(T x)` is much easier to write and use
- it basically allows me to inject methods into my type (using 
template parameter `this This` I can even get my real type)
- it's the most easy and pretty to type for the user. A struct 
with `mixin Model` would be quite verbose imo and doesn't allow 
Model to define custom fields easily, because they would break 
the implicitly generated constructor


Re: Getting the default value of a class member field

2022-12-01 Thread kinke via Digitalmars-d-learn

On Thursday, 1 December 2022 at 08:09:05 UTC, WebFreak001 wrote:

I've got this class definition:

```d
class X
{
this()
{
assert(false);
}

int x = 3;
}
```

due to internal reasons the constructor would fail at compile 
time, so I put in an assert(false) here, and I can't add or 
change any methods in X.


How do I get `3` if I have `X` and field name `"x"` at compile 
time?


For structs `X.init.x` / `__traits(getMember, X.init, "x")` 
would work, however for classes it complains about null 
dereference.


I saw there is __traits(initSymbol), however that one doesn't 
work at compile time.


AFAIK, there is no way. Unlike a struct's init symbol, a class' 
one doesn't necessarily represent a valid instance state - it's 
just the raw payload before invoking a ctor (which constructs a 
valid instance), and the state 'dead' memory is reset to after 
finalizing an object instance (to prevent dead pointers keeping 
other GC refs alive).


If the ctor worked at CTFE, one could use:
```d
int get() {
scope x = new X;
return x.x;
}
enum bla = get();
```
to get the `x` value of a *valid* instance, which might be 
different than the static initializer (if modified in the ctor).


I guess the main question is why do you require the static 
initializers of these fields at compile-time. 
`__traits(initSymbol)` was added to aid in manual blitting at 
runtime.


Getting the default value of a class member field

2022-12-01 Thread WebFreak001 via Digitalmars-d-learn

I've got this class definition:

```d
class X
{
this()
{
assert(false);
}

int x = 3;
}
```

due to internal reasons the constructor would fail at compile 
time, so I put in an assert(false) here, and I can't add or 
change any methods in X.


How do I get `3` if I have `X` and field name `"x"` at compile 
time?


For structs `X.init.x` / `__traits(getMember, X.init, "x")` would 
work, however for classes it complains about null dereference.


I saw there is __traits(initSymbol), however that one doesn't 
work at compile time.