Thread overview
Is there a cleaner way of doing this?
Aug 07, 2017
Shachar Shemesh
Aug 07, 2017
Ali Çehreli
Aug 07, 2017
Timon Gehr
Aug 08, 2017
Shachar Shemesh
Aug 08, 2017
Timon Gehr
Aug 07, 2017
Diego
Aug 07, 2017
Dgame
Aug 12, 2017
Mark
Aug 12, 2017
Mark
August 07, 2017
It is often desired to have a struct with an extra parameter. The common way to do this is like so:

struct S(T) {
	T param;

	void initialize(T param) {
		this.param = param;
		// Other stuff
	}
}

The problem is what happens when the param is optional. The common way to do this is to set T to void. This results in the following code:

struct S(T) {
	enum HasParam = !is(T == void);
	static if( HasParam ) {
		T param;
	}

	static if( HasParam ) {
		void initialize(T param) {
			this.param = param;
			// Other stuff
		}
	} else {
		void initialize() {
			// Same other stuff as above!
		}
	}
}

This is both tedious and error prone. Is there a cleaner way of doing this?

Just as an unrealistic fantasy, if the following code was legal, the problem would be resolved on its own:
void func(void p) {
	void param;

	param = p;

	return param;
}


Of course, that code has its own set of problems, and I'm not really suggesting that change.

Shachar
August 07, 2017
On 08/07/2017 01:01 AM, Shachar Shemesh wrote:
> It is often desired to have a struct with an extra parameter. The common
> way to do this is like so:
>
> struct S(T) {
>     T param;
>
>     void initialize(T param) {
>         this.param = param;
>         // Other stuff
>     }
> }
>
> The problem is what happens when the param is optional. The common way
> to do this is to set T to void. This results in the following code:
>
> struct S(T) {
>     enum HasParam = !is(T == void);
>     static if( HasParam ) {
>         T param;
>     }
>
>     static if( HasParam ) {
>         void initialize(T param) {
>             this.param = param;
>             // Other stuff
>         }
>     } else {
>         void initialize() {
>             // Same other stuff as above!
>         }
>     }
> }
>
> This is both tedious and error prone. Is there a cleaner way of doing this?

A mixin template can work. The whole param-related code is in one place and the void specialization obviates the need for static if:

mixin template ParamCode(T) {
    T param;

    void initializeParam(T param) {
        this.param = param;
        initialize();
    }
}

template ParamCode(T : void) {
}

struct S(T) {
    mixin ParamCode!T;

    void initialize() {
        // ...
    }
}

unittest {
    auto a = S!int();
    static assert(a.sizeof == int.sizeof);
    a.initializeParam(42);

    auto b = S!void();
    static assert(b.sizeof == 1);
    b.initialize();
}

void main() {
}

Ali

August 07, 2017
On 07.08.2017 10:01, Shachar Shemesh wrote:
> It is often desired to have a struct with an extra parameter. The common way to do this is like so:
> 
> struct S(T) {
>      T param;
> 
>      void initialize(T param) {
>          this.param = param;
>          // Other stuff
>      }
> }
> 
> The problem is what happens when the param is optional. The common way to do this is to set T to void. This results in the following code:
> 
> struct S(T) {
>      enum HasParam = !is(T == void);
>      static if( HasParam ) {
>          T param;
>      }
> 
>      static if( HasParam ) {
>          void initialize(T param) {
>              this.param = param;
>              // Other stuff
>          }
>      } else {
>          void initialize() {
>              // Same other stuff as above!
>          }
>      }
> }
> 
> This is both tedious and error prone. Is there a cleaner way of doing this?
> ...

struct S(T...) {
    T param;

    void initialize(T param) {
        this.param = param;
        // Other stuff
    }
}

Then, use S!() instead of S!void.

> Just as an unrealistic fantasy, if the following code was legal, the problem would be resolved on its own:
> void func(void p) {
>      void param;
> 
>      param = p;
> 
>      return param;
> }
> 
> 
> Of course, that code has its own set of problems, and I'm not really suggesting that change.
> 
> Shachar

The only reason this code is problematic is that void.sizeof == 1.
August 07, 2017
On Monday, 7 August 2017 at 08:01:26 UTC, Shachar Shemesh wrote:
> It is often desired to have a struct with an extra parameter. The common way to do this is like so:
>
> struct S(T) {
> 	T param;
>
> 	void initialize(T param) {
> 		this.param = param;
> 		// Other stuff
> 	}
> }
>
> The problem is what happens when the param is optional. The common way to do this is to set T to void. This results in the following code:
>
> struct S(T) {
> 	enum HasParam = !is(T == void);
> 	static if( HasParam ) {
> 		T param;
> 	}
>
> 	static if( HasParam ) {
> 		void initialize(T param) {
> 			this.param = param;
> 			// Other stuff
> 		}
> 	} else {
> 		void initialize() {
> 			// Same other stuff as above!
> 		}
> 	}
> }
>
> This is both tedious and error prone. Is there a cleaner way of doing this?
>
> Just as an unrealistic fantasy, if the following code was legal, the problem would be resolved on its own:
> void func(void p) {
> 	void param;
>
> 	param = p;
>
> 	return param;
> }
>
>
> Of course, that code has its own set of problems, and I'm not really suggesting that change.
>
> Shachar

You can use type default initialization property:

struct S(T) {
	T param;

	void initialize(T param = T.init) {
		this.param = param;
		// Other stuff
	}
}

S!int s;
s.initialize(42);  // works
s.initialize();  // also works; s.param == int.init == 0


August 07, 2017
On Monday, 7 August 2017 at 08:01:26 UTC, Shachar Shemesh wrote:
> It is often desired to have a struct with an extra parameter. The common way to do this is like so:
>
> struct S(T) {
> 	T param;
>
> 	void initialize(T param) {
> 		this.param = param;
> 		// Other stuff
> 	}
> }
>
> The problem is what happens when the param is optional. The common way to do this is to set T to void. This results in the following code:
>
> struct S(T) {
> 	enum HasParam = !is(T == void);
> 	static if( HasParam ) {
> 		T param;
> 	}
>
> 	static if( HasParam ) {
> 		void initialize(T param) {
> 			this.param = param;
> 			// Other stuff
> 		}
> 	} else {
> 		void initialize() {
> 			// Same other stuff as above!
> 		}
> 	}
> }
>
> This is both tedious and error prone. Is there a cleaner way of doing this?
>
> Just as an unrealistic fantasy, if the following code was legal, the problem would be resolved on its own:
> void func(void p) {
> 	void param;
>
> 	param = p;
>
> 	return param;
> }
>
>
> Of course, that code has its own set of problems, and I'm not really suggesting that change.
>
> Shachar

Why don't you use a factory method?

struct S(T)
{
    T p;

    static make(T p)
    {
        S s;
        s.p = p;

        return s;
    }

    static make()
    {
        return S();
    }
}

void main()
{
    auto s1 = S!int.make;
    auto s2 = S!string.make;
}
August 08, 2017
On 07/08/17 12:37, Timon Gehr wrote:

> struct S(T...) {
>      T param;
> 
>      void initialize(T param) {
>          this.param = param;
>          // Other stuff
>      }
> }
> 
> Then, use S!() instead of S!void.
> 

It's an interesting approach. It has the down side that it also accepts S!(int, string, int[23], double), and I'm still not sure what I think about this option (i.e. - whether I want to allow it).

If not, then things start to look quite misleading to the user, and I'd rather have the ugly static-ifs than do that.

Shachar
August 08, 2017
On 08.08.2017 08:06, Shachar Shemesh wrote:
> On 07/08/17 12:37, Timon Gehr wrote:
> 
>> struct S(T...) {
>>      T param;
>>
>>      void initialize(T param) {
>>          this.param = param;
>>          // Other stuff
>>      }
>> }
>>
>> Then, use S!() instead of S!void.
>>
> 
> It's an interesting approach. It has the down side that it also accepts S!(int, string, int[23], double), and I'm still not sure what I think about this option (i.e. - whether I want to allow it).
> 
> If not, then things start to look quite misleading to the user, and I'd rather have the ugly static-ifs than do that.
> 
> Shachar

I don't see why not, but you can just add a template constraint:

struct S(T...) if(T.length<=1) { ... }


You can also hide the approach as an implementation detail:

struct S(T){
    static if(is(T==void)){
        private alias X = AliasSeq!();
    }else{
        private alias X = AliasSeq!T;
    }
    X param;
    void initialize(X param){
        this.param=param;
        // ...
    }
}
August 08, 2017
On 08/08/2017 05:03 AM, Timon Gehr wrote:
> On 08.08.2017 08:06, Shachar Shemesh wrote:
>> On 07/08/17 12:37, Timon Gehr wrote:
>>
>>> struct S(T...) {
>>>      T param;
>>>
>>>      void initialize(T param) {
>>>          this.param = param;
>>>          // Other stuff
>>>      }
>>> }
>>>
>>> Then, use S!() instead of S!void.
>>>
>>
>> It's an interesting approach. It has the down side that it also accepts S!(int, string, int[23], double), and I'm still not sure what I think about this option (i.e. - whether I want to allow it).
>>
>> If not, then things start to look quite misleading to the user, and I'd rather have the ugly static-ifs than do that.
>>
>> Shachar
> 
> I don't see why not, but you can just add a template constraint:
> 
> struct S(T...) if(T.length<=1) { ... }
> 
> 
> You can also hide the approach as an implementation detail:
> 
> struct S(T){
>      static if(is(T==void)){
>          private alias X = AliasSeq!();
>      }else{
>          private alias X = AliasSeq!T;
>      }
>      X param;
>      void initialize(X param){
>          this.param=param;
>          // ...
>      }
> }

Very good creative use of the language, kudos Timon. -- Andrei
August 12, 2017
On Monday, 7 August 2017 at 08:01:26 UTC, Shachar Shemesh wrote:
> The problem is what happens when the param is optional. The common way to do this is to set T to void. This results in the following code:
>
> struct S(T) {
> 	enum HasParam = !is(T == void);
> 	static if( HasParam ) {
> 		T param;
> 	}
>
> 	static if( HasParam ) {
> 		void initialize(T param) {
> 			this.param = param;
> 			// Other stuff
> 		}
> 	} else {
> 		void initialize() {
> 			// Same other stuff as above!
> 		}
> 	}
> }
>
> This is both tedious and error prone. Is there a cleaner way of doing this?
> 
> Shachar

I was going to suggest using Algebraic/Variant, as in:

    void initialize(Algebraic!(int,void)) {
        static if(hasParam)
            this.param = param;
        // Other stuff
    }

but unfortunately Algebraic seems very cumbersome to use. You can't call initialize with an int (or "parameterlessly"); you have to use it like this:

    x.initialize(Algebraic!(int,void)(my_integer)); //

which is bad in many ways, e.g. you'll have to import std.variant whenver you want to call initialize.

Ideally, I would have liked to write something like this:

    void initialize( static if(hasParam) { mixin("T param"); } ) {
        static if(hasParam) {
            this.param = param;
        }
        // Other stuff
    }

but of course this is currently not possible.
August 12, 2017
On Saturday, 12 August 2017 at 15:02:34 UTC, Mark wrote:
> I was going to suggest using Algebraic/Variant, as in:
>
>     void initialize(Algebraic!(int,void)) {

This should read:
      void initialize(Algebraic!(int,void) param) {