0

I need to implement an interface for a class in C++ where I need to ask for events occured related with two kinds of sets, let's say here persons and actions. I need to ask for person identifier and action identifier in all possible combinations (all of them, specifying only one of the identifiers or specifying both of them)
Option 1)

// Asks for all events occured for every combination
int getNumberofEvents(float param1)
// Asks for all events occured for idPerson AND all possible value of idAction
int getNumberofEvents(int idPerson, float param1)  
// Asks for all events occured in idAction AND all possible value of idPerson
int getNumberofEvents(int idAction, float param1)
// Asks for all events occured in idPerson AND idAction
int getNumberofEvents(int idPerson, int idAction, float param1)

This option is clear to read but I need to implement a different interface for every possible combination, so there would be 8 methods if I include a new identifier (2³).

Option 2)

static const int ALL_PERSONS= 0;
static const int ALL_ACTIONS= 0;
int getNumberofEvents(int idPerson, int idAction, float param1)

For this option there is only one method interface, but I introduce a public magic number to search for 'All possible id'

Regarding to usability and further maintainability, which would be the best option between these two now I have in mind (there can be other better options that I do not include, of course).

Thanks.

chorch
  • 133
  • 1
  • 7
  • I would use bit-masking for such situation which would be second one. I haven't seen anything like the first one for filtering. Flags definitely would be easier to look-up and feel more logical than different functions with different parameters that actually do the same kind of work. – Etherealone Nov 07 '13 at 19:30
  • The method signature, which is based on the name of the method and its parameter types (but not the return type nor the parameter names) must be unique. Your second and third examples of option 1 share the same signature, so won't be allowed by the compiler. /// for option two, I'm assuming you intend to use default argument values -- a problem if the user wants to specify the first and third arguments but let the second be default -- that isn't possible. As long as you require the user to specify the magic value, it will work out. – mah Nov 07 '13 at 19:33
  • have you consider strategy pattern? Another option is policy design with template. – yngccc Nov 07 '13 at 19:39

3 Answers3

2

You could modify option 2 to avoid magic numbers and also avoid the problem of passing a person id for the action parameter or vice versa:

struct Person
{
    explicit Person(int x) : id (x) {}
    int id;
    static Person ALL;
};

Person Person::ALL(0);

struct Action
{
    explicit Action(int x) : id (x) {}
    int id;
    static Action ALL;
};

Action Action::ALL(0);

int getNumberofEvents(const Person& person, const Action& action, float param1);

// ...

int count = getNumberOfEvents(Person(3), Action::ALL, 1.0f);
molbdnilo
  • 55,783
  • 3
  • 31
  • 71
1

You appear to be badly reinventing what C++ has included in the Standard Library: std::count_if

Notice that it allows arbitrarily complex conditions because it accepts a functor object which is able to filter and decide which objects match. With C++11, lambdas make supplying the condition easier than ever.

You can either expose begin() and end() iterators and let the user call std::count_if, or write

int getNumberofEvents(std::function<bool (const descriptor&)> filter, float param1);

and let the user write their query expression in a very natural way:

getNumberOfEvents([](const descriptor& x) { return x.Action == ACTION_RAISE; }, 5.7);
Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
  • These are very interesting solutions that I didn't know. However, here in my problem, the class that implements the interface does not have the actual data, but it has to build a request and send it to a server. – chorch Nov 08 '13 at 08:06
  • @chorch: Ah yes, it's possible to handle that with expression tree libraries (like LINQ in .NET) but that gets complicated quickly. So hardcoding a few query patterns makes sense. – Ben Voigt Nov 08 '13 at 15:33
0

Absolutely go with option 2. The user of your code won't have to look up which method they want to call, they would just be able to use your defined constants in certain cases. It makes the user's life a whole lot easier to only have one function name to work with so that they don't have to be constantly referencing your .h file to figure out which one of the many functions (named the same thing) they want to use.

It'sPete
  • 4,728
  • 5
  • 33
  • 64