www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What's a good approach to DRY with the block code of a case-statement?

reply Jack <jckj33 gmail.com> writes:
I have a block of code that the only thing that change is the 
type passed in one of the template functions called so I'd like 
to make a DRY for this. But I'm not just replacing by a function 
due to control-flow, for example, there are if-statements where 
one just break and the other return 0. I think I could do 
something with mixin() that would kinda mimic C's macro but I 
still find it messy. Any alternatives?

```d
static int doSomething()
{
	switch(val)
	{
		case VAL_FOO:
			auto obj = getObject!MyType(someData); // this is the type
												   // that changes
			if(obj.shouldExit()) break;
		
			auto m = Message(...);
			if(obj.doSomethingElse(m)) return 0;
		break;

	   // ...
	
	   default:
	}

	return doSomethingY();
}
```

Maybe my least resort, if I went to replace by a function, I 
could do something like this:

```d
enum OP { BREAK, RETURN }
pragma(inline, true):
OP foo(T)()
{		
	auto obj = getObject!T(someData); // this is the type
										   // that changes
	if(obj.shouldExit()) return OP.BREAK;

	auto m = Message(...);
	if(obj.doSomethingElse(m)) return OP.RETURN;

	return OP.BREAK;
}
```

then:

```d
static int doSomething()
{
	switch(val)
	{
		case VAL_FOO:
			auto r = foo!MyType();
			if(r == OP.BREAK) break;
			if(r == OP.RETURN0) return 0;
		break;

	   // ...
	
	   default:
	}

	return doSomethingY();
}
```

I still find this not much elegant. If anyone knows a better way 
to do this, help are very welcome
Apr 26 2021
next sibling parent ag0aep6g <anonymous example.com> writes:
On Monday, 26 April 2021 at 20:39:55 UTC, Jack wrote:
 I have a block of code that the only thing that change is the 
 type passed in one of the template functions called so I'd like 
 to make a DRY for this. But I'm not just replacing by a 
 function due to control-flow, for example, there are 
 if-statements where one just break and the other return 0. I 
 think I could do something with mixin() that would kinda mimic 
 C's macro but I still find it messy. Any alternatives?

 ```d
 static int doSomething()
 {
 	switch(val)
 	{
 		case VAL_FOO:
 			auto obj = getObject!MyType(someData); // this is the type
 												   // that changes
 			if(obj.shouldExit()) break;
 		
 			auto m = Message(...);
 			if(obj.doSomethingElse(m)) return 0;
 		break;

 	   // ...
 	
 	   default:
 	}

 	return doSomethingY();
 }
 ```
I think you're looking for this D idiom: ```d sw: switch (rt_value) { static foreach (ct_value; ct_values) { case ct_value: some_template!ct_value; break sw; } } ``` Applied to your code it might look like this: ```d import std.meta: AliasSeq; alias stuff = AliasSeq!(VAL_FOO, MyType, VAL_BAR, MyOtherType, /* ... */); sw: switch (val) { static foreach (i; 0 .. stuff.length / 2) { case stuff[i]: auto obj = getObject!(stuff[i + 1])(someData); if(obj.shouldExit()) break sw; auto m = Message(...); if(obj.doSomethingElse(m)) return 0; break sw; } } ```
Apr 26 2021
prev sibling parent z <z z.com> writes:
I'd recommend you to use templates with alias parameters but you 
mentioned that you need to retain function context(for gotos, 
continue, break, etc...)
One thing you could do is mix the ugly mixin solution with the 
good one:
```D
//inside the function, so that you can access locals
pragma(inline, true)
string getDRYstr(T, parameters...)() {
     static assert(__ctfe);
     string mixedinstr;
     mixedinstr ~= /*code*/;
     static if(/**/){mixedinstr ~= /*code*/;}
     //...
     mixedinstr ~= "break;";
     return mixedinstr
}
```
There is no doubt that it is an ugly solution but the time saved 
not copy-pasting code could be worth it.
Apr 28 2021