Thread overview | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
August 07, 2017 Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shachar Shemesh | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shachar Shemesh | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shachar Shemesh | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shachar Shemesh | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shachar Shemesh | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shachar Shemesh | 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 Re: Is there a cleaner way of doing this? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mark | 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) { |
Copyright © 1999-2021 by the D Language Foundation