Thread overview
Why doesn't `opAssign` work for my templated struct?
Nov 08
monkyyy
Nov 09
kdevel
November 08

I am working on a library for making types representing units of measurement. Base units are derived from a struct called BaseUnit. I want to make it so that BaseUnit-derived types can have their values assigned from float constants, but not from each-other or from float variables.

I just want to know why the compiler doesn't recognize the opAssign function I have.

struct BaseUnit(TThis) {
    float value;

    this(T)(T v) if(isNumeric!T) {
        value = v;
    }

    void opAssign(T)(const T v) if(isNumeric!T) {
        value = v;
    }
}

enum Milligrams : BaseUnit!Milligrams;
enum Millimoles : BaseUnit!Millimoles;

unittest {
	Milligrams mass = Milligrams(20f);
	assert(mass==Milligrams(20f));
	mass = 40f;
        assert(mass.value==40f);
}

That line in the unit test assigning 40f to mass is giving me an error:

Error: cannot implicitly convert expression `40.0F` of type `float` to `Milligrams`

However, if I were to change BaseUnit into a struct (not a struct template), and make mass of BaseUnit rather than Milligrams, then the opAssign function works as intended.

What's going on?

November 08

On Friday, 8 November 2024 at 21:55:53 UTC, Liam McGillivray wrote:

>

if(isNumeric!T)

Error: cannot implicitly convert expression `40.0F` of type `float` to `Milligrams`

I dont believe phoboes type detection templates are well designed; given some sort of type pattern matching issue you should write your own with the pattern

void dummyNumbericFunction(T)(T t){
  auto foo=t+t;
  ...
}
enum isNumbertic(T)=__traits(compiles,dummyNumbericFunction!T);
November 09

On Friday, 8 November 2024 at 21:55:53 UTC, Liam McGillivray wrote:

>

enum Milligrams : BaseUnit!Milligrams;
enum Millimoles : BaseUnit!Millimoles;

These are forward declarations of enumeration types (aka opaque enums). These types do not inherit from the BaseUnit struct. Declaring the base type says that the member values of the enumeration must implicitly convert to the base type.

https://dlang.org/spec/enum.html#named_enums

>

Milligrams mass = Milligrams(20f);

I'm not sure why that compiles.

>

mass = 40f;

That line in the unit test assigning 40f to mass is giving me an error:

Error: cannot implicitly convert expression `40.0F` of type `float` to `Milligrams`

Because an enumeration type does not inherit methods of its base type, there is no opAssign method for enum types.

>

However, if I were to change BaseUnit into a struct (not a struct template), and make mass of BaseUnit rather than Milligrams, then the opAssign function works as intended.

What's going on?

Then mass of type BaseUnit does have an opAssign member.

November 09

On Friday, 8 November 2024 at 21:55:53 UTC, Liam McGillivray wrote:

>

I am working on a library for making types representing units of measurement. Base units [...]

>
[...]
enum Milligrams : BaseUnit!Milligrams;
enum Millimoles : BaseUnit!Millimoles;
[...]

Shouldn't (base) units be named in singular form?

>
[...]
	Milligrams mass = Milligrams(20f);
[...]

I would have expected something like

   Mass!milligram mass = Mass!milligram(20);

The variable mass represents a quantity measured in baseunits (milligrams). It does not represent the base unit. I'm afraid that your design will allow the assignment of amounts of substance to variables of "type" Milligrams.

November 09

On Saturday, 9 November 2024 at 12:33:44 UTC, Nick Treleaven wrote:

>

On Friday, 8 November 2024 at 21:55:53 UTC, Liam McGillivray wrote:

>

Milligrams mass = Milligrams(20f);

I'm not sure why that compiles.

Filed: https://issues.dlang.org/show_bug.cgi?id=24850