0

I have two variants of the same method. I also have an instance of a base class type, but I don't know what specific class it is an instance of. I now want to automatically select the appropriate method depending on the actual type of the object. It seems impossible though and the only solution I can come up with is to check all possibilities by casting. There has to be a nicer solution though.

Here is my minimal example:

// Example program
#include <iostream>
#include <string>
#include <memory>

class A
{
public:    
    virtual void bar() const = 0;
};

class B : public A
{
public:
    void bar() const
    {
        std::cout << "B.bar()" << std::endl;
    }
};

class C : public A
{
public:    
    void bar() const
    {
        std::cout << "C.bar()" << std::endl;
    }    
};

class Z
{
public:
    Z(int variable) : m_variable(variable) {};

    void foo(std::shared_ptr<B> b)
    {
        std::cout << "Calling foo(B) method! " << m_variable << std::endl;
        b->bar();
    }

    void foo(std::shared_ptr<C> c)
    {
        std::cout << "Calling foo(C) method!" << m_variable << std::endl;
        c->bar();
    }
private:
  int m_variable;
};


int main()
{
  std::shared_ptr<A> b(new B());

  Z z(42);

  //z.foo(b); // This doesn't work

  // But this does
  std::shared_ptr<B> b_cast = std::dynamic_pointer_cast<B>(b);
  if (b_cast.get())
    z.foo(b_cast);
}

http://cpp.sh/9fqne

At the moment I have to resort to dynamic_pointer_cast, but I find it kinda ugly and not very maintainable.

I also don't want to add the functionality of foo() to the classes B and C, because those are small independent data structures on which many other classes operate.

Thank you very much!

EDIT: In the original post I simplified a bit too much. The new example should clear things up.

Pfaeff
  • 105
  • 7
  • 3
    Have you heard of polymorphism? You can make them virtual member functions and get this behavior. – NathanOliver Jul 24 '17 at 13:37
  • 1
    Note that the line `a = b;` results in slicing. (There aren't any data members here, but notably this line doesn't make `a` suddenly a `B`.) – cdhowie Jul 24 '17 at 13:38
  • 1
    Read up on polymorphism, inheritance, interfaces, virtual functions and pointers to name a few. – Ron Jul 24 '17 at 13:40
  • The classes B and C are only for holding data. The method foo should absolutely not be a member of either of those as it doesn't have anything to do with those classes themselves. – Pfaeff Jul 24 '17 at 13:43
  • 1
    @Pfaeff Sounds like a **visitor pattern**? – BoBTFish Jul 24 '17 at 13:57

2 Answers2

1

Add a pure virtual function foo() to your base class and override in subsequent derived classes. Then have your global function foo() (which has nothing to do with member functions with the same name) accept a reference to std::shared_ptr const as a parameter:

#include <iostream>
#include <memory>
class A{
public:
    virtual void foo() = 0;
};

class B : public A{
public:
    void foo() override{
        std::cout << "Calling foo(B) method!" << std::endl;
    }
};

class C : public A{
public:
    void foo() override{
        std::cout << "Calling foo(C) method!" << std::endl;
    }
};

void foo(const std::shared_ptr<A>& param){
    param->foo();
}

int main(){
    std::shared_ptr<A> b = std::make_shared<B>();
    std::shared_ptr<A> c = std::make_shared<C>();
    foo(b);
    foo(c);
}
Ron
  • 14,674
  • 4
  • 34
  • 47
  • Imagine `foo` to be a member function of another class that operates on `B` and `C` type objects. As I have many classes like that, I don't want to add all that functionality to `B` and `C` themselves. The result of `foo()` also depends heavily on the member variables of the implementing class. – Pfaeff Jul 24 '17 at 14:14
0

As BoBTFish pointed out, the visitor pattern is a potential solution for this problem:

// Example program
#include <iostream>
#include <string>
#include <memory>

class B;
class C;

class Visitor
{
public:
    virtual void visit(B* b) const = 0;
    virtual void visit(C* b) const = 0;
};

class A
{
public:    
    virtual void bar() const = 0;
    virtual void accept(const Visitor* visitor) = 0;
};

class B : public A
{
public:
    void bar() const
    {
        std::cout << "B.bar()" << std::endl;
    }

    void accept(const Visitor* visitor) 
    {
        visitor->visit(this);
    }
};

class C : public A
{
public:    
    void bar() const
    {
        std::cout << "C.bar()" << std::endl;
    }

    void accept(const Visitor* visitor) 
    {
        visitor->visit(this);
    }    
};

class Z : public Visitor
{
public:
    Z(int variable) : m_variable(variable) {};

    void visit(B* b) const
    {
        std::cout << "Calling foo(B) method! " << m_variable << std::endl;
        b->bar();
    }

    void visit(C* c) const
    {
        std::cout << "Calling foo(C) method!" << m_variable << std::endl;
        c->bar();
    }
private:
  int m_variable;
};


int main()
{
  std::shared_ptr<A> b(new B());

  Z z(42);

  b->accept(&z);
}

http://cpp.sh/2vah5

Thank you very much!

Pfaeff
  • 105
  • 7