www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is there a cleaner way of doing this?

reply Shachar Shemesh <shachar weka.io> writes:
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
Aug 07
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Aug 07
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Aug 07
parent reply Shachar Shemesh <shachar weka.io> writes:
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
Aug 07
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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; // ... } }
Aug 08
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Aug 08
prev sibling next sibling parent Diego <diego example.com> writes:
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
Aug 07
prev sibling next sibling parent Dgame <r.schuett.1987 gmail.com> writes:
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; }
Aug 07
prev sibling parent reply Mark <smarksc gmail.com> writes:
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.
Aug 12
parent Mark <smarksc gmail.com> writes:
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) {
Aug 12