0

I'm not sure how to name this problem, so I'm going to try to explain as good as I can.

I want to be able to switch strategies depending on the types of two different objects. To make this work, I am thinking of flagging the objects with an enum type, and having a 'registry' (arrayish) of these strategies. Ideally, the correct strategy would be accessed with some simple operation like a bitwise operator between the two types.

This pseudocode may make what I'm trying to explain easier to understand:

enum Type { A, B, C }

struct Object {
  Type type;
}

class ActionRunner {
  vector<Strategy> strategies;

  void registerStrategy(type1, type2, strategy) {
    strategies[type1 operator type2] = strategy;
  }

  void runStrategyFor(type1, type2) {
    strategies[type1 operator type2].execute();
  }
}

This would be easy to solve using a map, but I'd like to use an array or vector because a map seems like an overkill for a problem like this and using an array is probably much faster.

So the problem is I don't know what operator I might be able to use to select the 'position' of the right strategy. I've been thinking of a few combinations but it seems all of them end up causing collisions with the different combinations at some point.

Does anyone have any clues/advice on what I may be able to use for this?

PS: I know premature optimization is bad, but I'm just trying to figure out if this problem can be solved in a simple way.

------- EDIT ------------------------------------------------

In light of the answers, I've been giving the problem some extra thought and I've come to the conclusion what I intended with this question isn't possible the way I'd like it. I'm going to try to re-state the problem I'm trying to solve now using this question.

I'd like to have a class structure in which there's objects of certain type 'BaseClass' and a 'processor' object that takes two objects derived from 'BaseClass' and runs the right strategy for those. Something like this:

class Processor {
  void run (DerivedA a, DerivedB b);
}

class BaseClass {}
class DerivedA: public BaseClass {}
class DerivedB: public BaseClass {}

BaseClass a = new DerivedA;
BaseClass b = new DerivedB;

processor.run(a, b)

According to what I understand, this would not work as I'd expect if what is passed as parameters to 'run' are references, which is what I'd rather do. Is there any way to do this without way-too-complicated code? (tripple dispatch!?)

I have in mind something like the double dispatch combined with an slave (processor) object that I think would work, but that seems awfully complex and probably a pain to maintain and extend.

Thanks!

uorbe001
  • 187
  • 3
  • 10

2 Answers2

0

The second sentence of your question rang a bell for me:

I want to be able to switch strategies depending on the types of two different objects.

This sounds like you want to perform a double dispatch. See the question (in particular, the answers to the question ;-)) at Double dispatch/multimethods in C++ for how to implement this in C++.

Community
  • 1
  • 1
Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • I think in this question the *type* isn't the standard C++ type. It's rather a **value** describing an object. This is how it looks from the posted code – SomeWittyUsername Dec 21 '12 at 10:59
  • @icepack: Possibly, maybe that's also a hint: instead of having an object with a 'type' field taking one of three vaules things may be easier if there were just three C++ classes (in which case the double dispatch implementation in the question I linked to becomes relevant). – Frerich Raabe Dec 21 '12 at 11:18
  • I was actually thinking of language types at first, but I started considering using values to define the types to explore the possibility of using a simple operator to access the 'strategy registry'. This is probably not the best design alternative, but I'm just exploring the options at this stage. – uorbe001 Dec 21 '12 at 22:47
  • @FrerichRaabe You may be pointing me onto the right direction there, I have yet to fully look at the double dispatch, but it does look like the double dispatch with a visitor pattern may be the best option for me in this case. This problem would become irrelevant for me if I end up changing the design with your suggestion, but as I said, I'm just considering possible designs yet ;) – uorbe001 Dec 21 '12 at 22:50
0

That's a classic example for using map instead of array. Array is actually a private case of map with key defined as an integer. In your case the key is a tuple so a simple array won't do and you'll end up with collisions (even if you're lucky for your specific input, your code will be extremely non-robust).

You can have an intermediate solution, between simple array and map: 2D array, with your 2 types serving as indices to rows and columns..

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
  • After giving it some extra thought I came to the same conclusion as you did. The only issue with the map is that it is potentially slower than an array. The 2D array is a good option I have considered, but it would mean having to allocate more memory than necessary in most cases (at least the way I have considered). The one I'm considering here is probably not the best design option though. – uorbe001 Dec 21 '12 at 22:43
  • @uorbe001 there are plenty of ways to minimize memory usage. E.g., you can implement your own operator with bunch of `if..else` statements for each of your combinations that will return an index in 1D array - it will work but it's ugly and will result in performance penalty. The bottom line is that no matter what you do you'll pay either in space or in time (or a combination of both) – SomeWittyUsername Dec 22 '12 at 07:43
  • Yeah, I was trying to find a way of 'plugging-in' strategies from the client code, but it doesn't look like there's a way of doing this efficiently (from a space and time perspective). I think the best option for my problem may be to have some 'core' strategies built-in, and maybe allow client code to register new strategies despite the time/space cost. Ideally I'd like to have all the strategies 'plugged-in' to increase binary compatibility, but the downsides make me think it's probably a bad idea to try that. – uorbe001 Dec 22 '12 at 13:35