How can I restrict a function argument to a some specific enums? Preferably check it at compile-time (though I doubt this is possible).
What I have are 2 enums (KeyCode
for keyboard keys and Mouse.Button
for mouse buttons) and they are treated in the code in exactly the same way. I could simply overload the function and copy-paste the contents, but well, I'd like to avoid the nightmares.
Simplified version of what I currently have (inside a class)
enum E1 { Zero, One, Two }
enum E2 { Three, Four, Five }
// Overloads so users can only use this with enums only of type E1 or E2
public void DoEnumStuff(E1 e) {
DoEnumStuffTemplate(e);
}
public void DoEnumStuff(E2 e) {
DoEnumStuffTemplate(e);
}
// private function so users cannot access this generic one
private void DoEnumStuffTemplate<T>(T e) where T : struct, IConvertible {
// check type for safety
if (!typeof(T).IsEnum || typeof(T).Name != "E1" || typeof(T).Name != "E2")
throw new ArgumentException();
// do lots of stuff
DoSomething(e); //<- overloaded function, accepts only E1 and E2 =ERROR
// do lots of other stuff
}
For completeness sake:
DoSomething
behaves completely different depending on which type is givenDoSomething
is called a lot in the function- I cannot change the Enums
- I do not want to change
DoSomething
I think I need to be able to tell the compiler that the generic T
is surely either E1
or E2
, but I have no clue as of how to do this.
Edit: the situation
Lots of good suggestions, but nothing that encompasses all I want. I'll add here the code I currently have to shed some more light on the problem hopefully.
I'm making a minesweeper clone to try out Unity 2D. I've created an Action
class based on the thor::ActionMap
class from the library Thor in C++ used with SFML. It simply allows for neat code such as (in C++)
ActionMap actionMap<string>;
actionMap["Fire"] = Action(Keyboard::LeftControl) || Action(Mouse::Left);
// stuff
while (game.IsRunning()) {
if (actionMap["Fire"].IsActive()) //true if left control or left mouse button is held
// FIRE
// probably more stuff
}
Where ActionMap
is simply a dictionary of a key (here a string
) and an Action
. As you can see, the Action
accepts both keyboard and mouse buttons which are 2 different enum
s. Thus the equivalent of the DoSomething(e)
from the example code.
I'm now creating a method that can change the controls consistently. It uses the enum
EControls
as key instead of a string
. Here KeyCode
contains all keyboard keys and Mouse.Button
all the mouse buttons. I need to differentiate between pressing and releasing of a button here, which is why both EControls.TilePressed
and EControls.TileReleased
will have the same key and need to be treated differently than for example EControls.GameEscape
. This code is again in C#.
private ActionMap _controls = new ActionMap<EControls>();
// Set controls for a keyboard key
public void SetControl(EControls control, KeyCode key) {
switch (control) {
// If either TilePressed or Released was given, set them both to the same key
case EControls.TilePressed:
case EControls.TileReleased:
//Here Action(...) is DoSomething(...) from the example code
_controls[EControls.TilePressed] = new Action(key, Action.EActionType.PressOnce);
_controls[EControls.TileReleased] = new Action(key, Action.EActionType.ReleaseOnce);
break;
case EControls.TileFlagPressed:
case EControls.TileFlagReleased:
_controls[EControls.TileFlagPressed] = new Action(key, Action.EActionType.PressOnce);
_controls[EControls.TileFlagReleased] = new Action(key, Action.EActionType.ReleaseOnce);
break;
case EControls.GameEscape:
_controls[EControls.GameEscape] = new Action(key, Action.EActionType.ReleaseOnce);
break;
default:
throw new ArgumentOutOfRangeException("control");
}
}
// Set controls for a mouse button
public void SetControl(EControls control, Mouse.Button button) {
// copy-pasted code :(
case EControls.TilePressed:
case EControls.TileReleased:
_controls[EControls.TilePressed] = new Action(button, Action.EActionType.PressOnce);
_controls[EControls.TileReleased] = new Action(button, Action.EActionType.ReleaseOnce);
break;
case EControls.TileFlagPressed:
case EControls.TileFlagReleased:
_controls[EControls.TileFlagPressed] = new Action(button, Action.EActionType.PressOnce);
_controls[EControls.TileFlagReleased] = new Action(button, Action.EActionType.ReleaseOnce);
break;
case EControls.GameEscape:
_controls[EControls.GameEscape] = new Action(button, Action.EActionType.ReleaseOnce);
break;
default:
throw new ArgumentOutOfRangeException("control");
}
}
As you can see, in almost every line of code new Action(...)
is present and code such as if (typeof(T).GetType() == typeof(E1))
is essentially the same as copy-pasting the contents of the function. Which is something I'd like to avoid (copy-pasting would even be safer on compile-time). But as it stands, it does not seem to be possible.
Since in a bigger game you'll probably regularly add some new controls, it will be quite an annoyance.
Sorry for the wall of text :s