www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Default struct constructors if a struct member is a union

reply solidstate1991 <laszloszeremi outlook.com> writes:
```d
union U {
     int i32;
     long i64;
     float f32;
     double f64;
}

struct S {
     TypeEnum type;
     U data;
}

S foo0 = S(TypeEnum.Integer32, S(20));  //Ugly, but works
S foo1 = S(TypeEnum.Integer64, S(20L)); //Error: cannot 
implicitly convert expression
//ditto for the rest of the members
```

My question is can I initialize structs like these in one line 
without relying on a second line? My usecase scenario doesn't 
really allow constructors for the struct, since it's a binding to 
an external library via C API.
Jun 29
next sibling parent Basile B. <b2.temp gmx.com> writes:
On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:
 [...]
 My question is can I initialize structs like these in one line 
 without relying on a second line? My usecase scenario doesn't 
 really allow constructors for the struct, since it's a binding 
 to an external library via C API.
Apprently that works with an anonymous union: ```d enum TypeEnum { Integer32, Integer64, Float32, Float64, } struct S { TypeEnum type; union { int i32; long i64; float f32; double f64; } } S foo0 = S(TypeEnum.Integer32, 20); S foo1 = S(TypeEnum.Integer64, 20L); ```
Jun 29
prev sibling next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:
 S foo0 = S(TypeEnum.Integer32, S(20));  //Ugly, but works
 S foo1 = S(TypeEnum.Integer64, S(20L)); //Error: cannot
Did you mean `U(20)`? The 20 applies to the first field of the union, i32. `U(20L)` also works and (I think) it does the same because of VRP.
 My question is can I initialize structs like these in one line 
 without relying on a second line? My usecase scenario doesn't 
 really allow constructors for the struct, since it's a binding 
 to an external library via C API.
You can use a field initializer: ```d enum TypeEnum { Integer32, Integer64 } union U { int i32; long i64; float f32; double f64; } struct S { TypeEnum type; U data; } S foo0 = S(TypeEnum.Integer32, U(20)); // init i32 S foo1 = S(TypeEnum.Integer64, U(f32: 0.5F)); // struct initializer syntax S foo2 = {TypeEnum.Integer32, {20}}; S foo3 = {TypeEnum.Integer64, {f32: 0.5F}}; // init f32 void main() { assert(foo0.data.i32 == 20); assert(foo3.data.f32 == 0.5F); } ```
Jun 30
parent Basile B. <b2.temp gmx.com> writes:
On Sunday, 30 June 2024 at 11:23:45 UTC, Nick Treleaven wrote:
 On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:
 S foo0 = S(TypeEnum.Integer32, S(20));  //Ugly, but works
 S foo1 = S(TypeEnum.Integer64, S(20L)); //Error: cannot
Did you mean `U(20)`? The 20 applies to the first field of the union, i32. `U(20L)` also works and (I think) it does the same because of VRP.
 My question is can I initialize structs like these in one line 
 without relying on a second line? My usecase scenario doesn't 
 really allow constructors for the struct, since it's a binding 
 to an external library via C API.
You can use a field initializer: ```d enum TypeEnum { Integer32, Integer64 } union U { int i32; long i64; float f32; double f64; } struct S { TypeEnum type; U data; } S foo0 = S(TypeEnum.Integer32, U(20)); // init i32 S foo1 = S(TypeEnum.Integer64, U(f32: 0.5F)); // struct initializer syntax S foo2 = {TypeEnum.Integer32, {20}}; S foo3 = {TypeEnum.Integer64, {f32: 0.5F}}; // init f32 void main() { assert(foo0.data.i32 == 20); assert(foo3.data.f32 == 0.5F); } ```
That's another solution, that being said, the real problem OP encountered is the classic one of "implict construction", which is not a D thing.
Jun 30
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:
 ```d
 union U {
     int i32;
     long i64;
     float f32;
     double f64;
 }

 struct S {
     TypeEnum type;
     U data;
 }

 S foo0 = S(TypeEnum.Integer32, S(20));  //Ugly, but works
 S foo1 = S(TypeEnum.Integer64, S(20L)); //Error: cannot 
 implicitly convert expression
 //ditto for the rest of the members
 ```

 My question is can I initialize structs like these in one line 
 without relying on a second line? My usecase scenario doesn't 
 really allow constructors for the struct, since it's a binding 
 to an external library via C API.
Have you tried named parameter construction? ```d S foo2 = S(TypeEnum.Float32, U(f32: 20.0)); ``` -Steve
Jul 02
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:
 My usecase scenario doesn't really allow constructors for the 
 struct, since it's a binding to an external library via C API.
BTW, this is not true. A constructor does not change the struct layout or anything about it from the C side. You can safely add the struct constructor (or any other struct member functions) and the struct itself should be C compatible. -Steve
Jul 02
prev sibling parent cc <cc nevernet.com> writes:
On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:
 My question is can I initialize structs like these in one line 
 without relying on a second line?
```d template ValueOfUDAType(alias T, alias UDA) { static foreach (uidx, U; __traits(getAttributes, T)) static if (is(typeof(U) == UDA)) enum UDA ValueOfUDAType = U; } struct S { enum TypeEnum { Integer32, Integer64, Float32, Float64, } union U { (TypeEnum.Integer32) int i32; (TypeEnum.Integer64) long i64; (TypeEnum.Float32) float f32; (TypeEnum.Float64) double f64; } TypeEnum type; U data; this(T)(T t) { opAssign(t); } void opAssign(S rhs) { static foreach (idx, field; S.tupleof) { this.tupleof[idx] = rhs.tupleof[idx]; } } void opAssign(T)(T t) { static foreach (idx, field; data.tupleof) static if (is(T == typeof(field))) { type = ValueOfUDAType!(field, TypeEnum); data.tupleof[idx] = t; static assert(!is(typeof(FOUND)), "Assigning type "~fullyQualifiedName!T~" matches multiple candidates "~fullyQualifiedName!U); enum bool FOUND = true; } static assert(is(typeof(FOUND)), "Cannot assign type "~fullyQualifiedName!T~" to "~fullyQualifiedName!U); } T get(T)() { static foreach (idx, field; data.tupleof) static if (is(T == typeof(field))) { enforce(type == ValueOfUDAType!(field, TypeEnum), "Type mismatch requesting "~fullyQualifiedName!T); return data.tupleof[idx]; } } bool is_a(T)() { static foreach (idx, field; data.tupleof) static if (is(T == typeof(field))) { return (type == ValueOfUDAType!(field, TypeEnum)); } } } S s = 3.14f; assert(s.type == S.TypeEnum.Float32); s = 9.17; assert(s.type == S.TypeEnum.Float64); if (s.is_a!double) { writeln(s.get!double); } try { int n = s.get!int; } catch (Exception e) { writeln("Not an int"); } s = 20; assert(s.type == S.TypeEnum.Integer32); s = 40L; assert(s.type == S.TypeEnum.Integer64); ``` Or just use SumType.
Jul 06