0

I have two classes deriving from the same base class. on compile time it is known which one gets created based on a macro define. I have another class that is a user and calls member functions (different ones for each class). It looks like this:

class User() {

    void useClass( Base* p ) {
#ifdef useA
        p->aFun();
#else 
        p->bFun()
#endif
    }

class Base() {}
class A : public Base {
    void aFun();
}
class B : public Base {
    void bFun();
}
class C {
    C() {
#ifdef useA
        p = new A();
#else 
        p = new B();
#endif
    }
    Base* p; 
    User m_user;
    void doStuffWithUser() {
        user.useClass( p );
    }
}

I would like to reduce the amount of macros, so I am wondering if there is a better way to do this. In particular, the #ifdef in the User class doesn't look very nice to me. Is there a way to reproduce it without using the macro? Ideally without runtime checks to determine what type p is.

EDIT: The two derived classes have different members that need to be called and despite the inheritance, this cant be changed.

Teivaz
  • 5,462
  • 4
  • 37
  • 75
chrise
  • 4,039
  • 3
  • 39
  • 74
  • 1
    If your usage is limited to what you describe in your code, then virtual functions do exactly what you need. – Kinan Al Sarmini May 26 '17 at 10:46
  • 1
    You can probably provide a [CRTP](https://stackoverflow.com/questions/4173254/what-is-the-curiously-recurring-template-pattern-crtp) to get rid of the `#ifdef`'s. – πάντα ῥεῖ May 26 '17 at 10:47
  • looks like virtual functions. maybe tagdispatch and template stuff as well – jonas_toth May 26 '17 at 10:49
  • I edited the question to make clear that the two classes have different interfaces. this cant be changed. so virtual functions wont work here – chrise May 26 '17 at 10:50
  • @chrise In that case you can declare 2 different useClass functions (one for A and one for B) combined with dynamic dispatch (e.g. visitor pattern) to invoke the correct function. – Kinan Al Sarmini May 26 '17 at 10:56
  • but if I pass a Base* to the function in both cases, I cant override, can I? – chrise May 26 '17 at 11:02
  • @chrise that's where the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) comes to play. It's one possible solution, it can probably be done at compile time with template voodoo. – Kinan Al Sarmini May 26 '17 at 11:04
  • To be more accurate, what you need is [double dispatch](https://en.wikipedia.org/wiki/Double_dispatch#Double_dispatch_in_C.2B.2B), a visitor is one way to implement it in C++. – Kinan Al Sarmini May 26 '17 at 11:06
  • 1
    Currently, your code doesn't compile. – Jarod42 May 26 '17 at 11:26

7 Answers7

1

A solution is the visitor pattern.

The idea is to have two classes : the visitor and the visited.

The visitor is used to call a function depending on the real type of the object. The visited is the object of your class hierarchy.

In your example, you could do:

class User() {

    void useClass( Base* p ) {
         p->visit(visitor);
    }


class Base() {
    virtual void visit(Visitor) = 0;
}
class A : public Base {
    void aFun();
    virtual void visit(Visitor v) override {
        v.visit(this);
    }
}
class B : public Base {
    void bFun();
    virtual void visit(Visitor v) override {
        v.visit(this);
    }
}
class Visitor {
    void visit(B* b) {
        b->bFun();
    }
    void visit(A* a) {
        a->aFun();
    }
}

By having this double dispatch with the visit function, you ensure that you call the function depending on the real type.

I don't think there is a compile time solution to your issue because in useClass (as it is now), there is no way (at compile time) to know the real type of p. If you want to have a compile time solution you need to do more changes. For example making useClass a template or overloading it, which mean you can't call useClass with a Base* any more ...

nefas
  • 1,120
  • 7
  • 16
1

The fact that A and B share a common base class is irrelevant since they have different interfaces that you are using.

I would make C a template and store a pointer to the derived class instead of the base class:

template<typename T>
class CT {
public:
    CT() {
        p = std::make_unique<T>();
    }
    std::unique_ptr<T> p;
    User m_user;
    void doStuffWithUser() {
        user.useClass(p);
    }
};

Then you can simply overload useClass() to accept either A or B:

class User {
public:
    void useClass(A* p) {
        p->aFun();
    }
    void useClass(B* p) {
        p->bFun();
    }
};

Now you just have one compile time switch:

#ifdef useA
using C = CT<A>;
#else
using C = CT<B>;
#endif
Ryand
  • 436
  • 4
  • 16
0

You can rename aFun and bFun to Fun and make it virtual(also add it in Base class) and in useClass, use Fun method, compiler will figure out which method to use. This will eliminate first macro.

For the second maybe you should use rewrite it in some other way, so you wouldnt use macros at all. I don't think you can reproduce this behavior without macros. Maybe you should have some flag that you give to constructor, 1 to create object A or 0 to create object B and get this flag from user at the runtime.

EDIT So maybe you can create function Fun that in class A calls aFun and in class B calls bFun.

Izaya
  • 409
  • 3
  • 6
0

You can create a template for User class and specialize it for class A and class B:

template<typename T>
class User
{
    void useClass( Base* p );
}

template<>
class User<A>
{
    void useClass( Base* p ) {p->aFun();}
};

template<>
class User<B>
{
    void useClass( Base* p ) {p->bFun();}
};

Now in class C:

template<typename T>
class C {
    C() {
       p = new T();
    }
    Base* p; 
    User<T> m_user;
    void doStuffWithUser() {
        m_user.useClass( p );
    }
}

As a final note, just avoid using new operator. Try std::unique_ptr or std::shared_prt

PS. I have not tested this code

Amadeus
  • 10,199
  • 3
  • 25
  • 31
0

if you do not want to change any interface you can use single #ifdef

class Base {};

class A : public Base {
public:
   void aFun(){}
};

class B : public Base {
public:
  void bFun(){}
};

#ifdef useA
  typedef A impl_type;
  auto correct_func = &impl_type::aFun;
#else
  typedef B impl_type;
  auto correct_func = &impl_type::bFun;
#endif

class User {
public:
  void useClass( Base* p ) {
    auto pointer = (static_cast<impl_type*>(p));
    (pointer->*correct_func)();
   }
};

class C {
  C() {
    p = new impl_type();
  }
  Base* p;
  User m_user;
  void doStuffWithUser() {
    m_user.useClass( p );
  }
};
Andrew Kashpur
  • 736
  • 5
  • 13
-1

Probably you could name both functions with the same name in A and B and make it virtual, so useClass will call only needed function.

Like

class User() {
    void useClass( Base* p ) {
        p->fun();
    }
};    
class Base() {
    virtual void fun() = 0;
};
class A : public Base {
    void fun();
};
class B : public Base {
    void fun();
};

Also you can use some kind of constexpr function (if you are using c++11 standard or newer) to determine what type p is.

Edit: After seeing comment, i think that you're probably can left yours aFun(), bFun(), and just add some fun() func which will be derived and call type-specific function. Also, it may be helpful to try and create some adapter classes with same interfaces(as in gof patterns).

Edit2: I mean that there could be some function like

constexpr Base* chooseType(int a){
    if(a == 0){
        return new A();
    } else {
        return new B();
    }
}
/////
C() {
    int case = 0;
    p = chooseType(case);
}

And it will be called in compile-time, so as choice of class.

Raelysk
  • 49
  • 7
-1

If you can't change the interface, want to get rid of #ifdefs, and have compile-time guarantee of types being used, without run-time checks - I would suggest using combination of templates, and overloaded functions.

First of all, I would change class C to be a template:

template<typename Type>
class C
    {
    static_assert(std::is_base_of<Base, Type>::value, "Template argument of C is not base of Base!");
    public:
        C () {p = new Type;}
        ~C() {delete p;}

        void fun () {u.useClass (p);}

    private:
        Type* p;
        User u;
    };

And, then would change User class to switch between different possible implementations of Base with overloaded functions:

class User
    {
    public:
        void useClass (A* p) {p->aFun();}
        void useClass (B* p) {p->bFun();}
    };

And, then you would create object of C as follows:

C<A> ca;

If you forgot to implement type-specific useClass, or tried to use wrong type in C (i.e. not inherited from Base), you would get compile-time errors.

In addition, if some of the child classes of Base, that you want to switch between, have non-default constructors, you may pass a functor (e.g. std::function<Type*()>) to a C constructor, and use that to create an object.

Such a constructor may look like:

C (std::function<Type* ()> function) {p = function();}

And usage of it would look like:

C<Z> cz ([&]{return new Z(someInt);});
Algirdas Preidžius
  • 1,769
  • 3
  • 14
  • 17