Thread overview
Default struct constructors if a struct member is a union
Jun 30
Basile B.
Jul 01
Basile B.
Jul 07
cc
June 29
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.

June 30

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:

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);
June 30

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:

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);
}
July 01

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:

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.

July 02

On Saturday, 29 June 2024 at 23:33:41 UTC, solidstate1991 wrote:

>
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?

S foo2 = S(TypeEnum.Float32, U(f32: 20.0));

-Steve

July 02

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

July 07

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?

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.