2

I would like to be able to check whether a certain condition is true or not on an object of some Derived type, which in the local context is only known through its Base type.

The condition to be checked needs specific knowledge of the Derived type, but I'm trying to achieve a level of indirection that allows the checking to be done in a context which only knows about the base class.

One possible way of doing this is by encapsulating the information needed for the check in a functor (or lambda) and dispatch it to the place where the check needs to be performed.

Define the classes

class Base {
//...  
public:
  using Condition = std::function<bool(const Base*)>;
  bool check(const Condition&);
}

class DerivedA : public Base {
//...
public:
  int index() const { return index;}
private:
  int index;
}

class DerivedB : public Base {
//...
public:
  std::string name() const { return name;}
private:
  std::string name;
}

and suppose we have a Derived-aware context, where I can encapsulate the the Derived-specific condition in a lambda function. We can then dispatch the Condition functor to the context where it is going to be needed:

 //context where DerivedA and DerivedB are known

 int idx = 1;

 auto derivedCondition = [idx](const Base* obj) {
    DerivedA* derivedObj = dynamic_cast<const DerivedA*>(obj); 
    if (derivedObj)        
      return (derivedObj->index() == idx);
    return false;
 };

 std:string str = "Peter";

 auto derivedCondition = [str](const Base* obj) {
    DerivedB* derivedObj = dynamic_cast<const DerivedB*>(obj); 
    if (derivedObj)        
      return (derivedObj->name() == str);
    return false;
 };


 // dispatch condition to an external context where it will be used, this context is not aware of Derived
 useConditionElsewhere(derivedCondition);

with

 void useConditionElsewhere(Condition condition) {

    //context where only base class is known
    Base* baseObj;
    //... suppose baseObj is created from a factory class that returns a random subclass

    // baseObj can be a DerivedA, DerivedB or none of them
    bool checked = baseObj->check(condition);
    //..
 }

The above achieves what I need.

My question is: is it possible to do this without casting?

Emerald Weapon
  • 2,392
  • 18
  • 29

3 Answers3

2

Suppose you were to create a ConditionChecker class, with a check function that is overloaded for the various types you need to check:-

class ConditionChecker
{
    public:
    bool checkCondition(DerivedA& toBeChecked)
    {
         //some checking code that knows about DerivedA objects
    }

    bool checkCondition(DerivedB& toBeChecked)
    {
        //some checking code that knows about DerivedB objects
    }
};

If your existing Base class had a virtual function called check that accepts a ConditionChecker object:-

virtual bool check(ConditionChecker& checker) = 0;

then this can be implemented in your derived classes as

bool check(ConditionChecker& checker)
{
    return checker.checkCondition(*this);// calls the correct overload
}
ROX
  • 1,256
  • 8
  • 18
  • But how would you pass an argument that is different from class to class? – Emerald Weapon Apr 28 '16 at 14:41
  • The arguments you require from the derived object will be available in the overloaded version of checkCondition. From your example its not obvious where you'd want to get the values you want to check them against, but they could be passed into the condition checker when its created. If there are lots of types to check and CondtionChecker starts to get messy it can be split into derived types to check each specific type. – ROX Apr 28 '16 at 17:36
  • ROX, I now fully understand your answer. Smeeheey's answer pushed me through the last inch I needed to have the problem on focus. Thanks. – Emerald Weapon Apr 29 '16 at 17:57
  • You're welcome, and thanks to Smeeheey also. It probably didn't help that my example code missed checkCondition in the call (now edited). I nearly mentioned double dispatch myself, I was thinking it's like double dispatch but not quite it. – ROX May 03 '16 at 11:35
1

It seems you have a case of double dispatch here. Essentially your check condition call is dependent on two dynamic types: the derived type of Base and the type of condition itself (although you code the latter as an std::function in your example, which is in many ways equivalent in this case). In C++, which has no native double dispatch, this kind of problem is often solved by using two inheritance hierarchies and two calls. This is precisely the solution given by @ROX . His ConditionChecker can be made a base class with virtual checkConditionCalls to enable various conditions to be checked. Each derived condition class will implement multiple checkCondition functions, one for DerivedA and one for DerivedB (and any others you choose to add) - so they will have both specific knowledge of the condition being checked and the type being derived.

Smeeheey
  • 9,906
  • 23
  • 39
  • I was thinking along the line of double dispatching too, but I just couldn't quite see the problem that way. Thanks for letting me better understand ROX's answer, I think the puzzle is finally starting to make sense. – Emerald Weapon Apr 29 '16 at 17:53
0

It is very hard to understand what do you want. But here is rewritten code using virtual method. I hope it will force you to produce the correct answer.

class Base {
//...  
public:
  virtual bool check(int idx) override { return idx == index();}
}

class Derived : public Base {
//...
public:
  int index() const { return index;}
  virtual bool check(int idx) { return false; }
private:
  int index;
}

void useConditionElsewhere(int idx) {

    //context where only base class is known
    Base* baseObj;
    //...

    bool checked = baseObj->check(idx);
    //..
 }
Dmitriy Zapevalov
  • 1,357
  • 8
  • 13
  • Thanks for your answer, but this does not work. `Base` cannot have a method `bool check(int idx)` with a signature that is specific to work for a certain derived class. – Emerald Weapon Apr 28 '16 at 14:10
  • See edited question, hopefully makes things more clear. – Emerald Weapon Apr 28 '16 at 14:17
  • Now it makes sense! So you want to apply some side logic for each derived type. For such things `dynamic_cast` is the normal way. Another question is how to apply it. For example you can encapsulate checking inside special `Checker` class OR you can make (`typeid` - checker) table OR keep it like you done. – Dmitriy Zapevalov Apr 28 '16 at 14:32
  • Happy to know that! The dynamic casting works, yes, but just out of curiosity, and for the sake of improving myself, I wanted to see if someone has ever come up with some "perfectly seamless" design. – Emerald Weapon Apr 28 '16 at 14:35
  • By the way. My actual application of this consists of an heterogeneous data tree, where each node is a subclass of a base node class which adds its own payload data type. I would like to implement a general and type-generic lookup function form the master root node. – Emerald Weapon Apr 28 '16 at 14:39