Thread overview
Is there a cleaner way of doing this?
Aug 07
Diego
Aug 07
Dgame
6 days ago
Mark
6 days ago
Mark
August 07
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
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
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
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
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
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
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
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
6 days ago
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.
6 days ago
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) {