1

Let a class hierarchy :

class Base { virtual ~Base() throw(); };

class DerivedA : public Base { };

class DerivedB : public Base { };

I would like to have some code specific to each of these derived classes. However that code also being specific to the application that makes use of this class hierarchy, I do not want to embbed this derived-class-specific code into these derived classes. To avoid doing so, I thought about writing free functions :

void DerivedASpecificWork( DerivedA da );

void DerivedBSpecificWork( DerivedB db );

However, when given an instance of a derived class through a reference/pointer to a Base, I do not have access to the actual type of the instance, and thus cannot call the proper Derived*SpecificWork() function.

I would like to know if there is nome kind of design pattern that would allow me to call a derived-class-specific function without knowing the actual type of the instance, i.e having the same mechanism as virtual functions provide, but without having these virtual functions that would require me to embbed application-specific code into that class hierarchy.

Actually, why I want to do that is to provide informations about an exception that occured within a natively implemented function called by a Lua script. Each exception carrying its own set of information, the way I want to represent the error within the script depends on the type of the exception. I could create a pure virtual method in the base class that would be implemented by derived classes, but this would require me to embbed Lua-related code into my exception hierarchy, which I do not want to do since the Lua is specific to one of the application using that exception hierarchy.

Also I cannot use C++11.

Thank you.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Virus721
  • 8,061
  • 12
  • 67
  • 123
  • 1
    Your parent destructor is private though.. – Marco A. Nov 25 '16 at 10:12
  • 1
    The typical pattern here is to use *actual* virtual functions. Everything else is an [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern). – Some programmer dude Nov 25 '16 at 10:13
  • @Someprogrammerdude Yes that seems logical, but I would like not to pollute what is used as a library with application-specific code, which is why I'm asking if there are work arounds. – Virus721 Nov 25 '16 at 10:15
  • 1
    @Virus721 [Visitor Pattern?](http://stackoverflow.com/questions/10116057/visitor-pattern-explanation) – PaulMcKenzie Nov 25 '16 at 10:21
  • @PaulMcKenzie I must do some experiment with that first, but it seems to be what I need. Thanks for your help. – Virus721 Nov 25 '16 at 10:48

3 Answers3

2

May be Brigde pattern can help you.

This pattern can be used when you want to avoid a permanent binding between an abstraction and it's implementation.

(I don't see your comment about your restriction in using c++11, but you can remove std::unique_ptr, std::move and override keyword)

class AppSpecificImp
{
public:
   virtual void DoWork() = 0;
};

class Base 
{ 
public:
   virtual ~Base() throw(); 

   virtual DoWork() = 0;
};

class DerivedA : public Base 
{ 
public:
   DerivedA(std::unique_ptr<AppSpecificImp> appImp)
      : imp(std::move(appImp))
   {
   }

   void DoWork() override
   {
       // DerivedA specific code 

       imp->DoWork();
   }

private:
   std::unique_ptr<AppSpecificImp> imp;
};

class DerivedB : public Base 
{ 
public:
   DerivedB(std::unique_ptr<AppSpecificImp> appImp)
      : imp(std::move(appImp))
   {
   }

   void DoWork() override
   {
       // DerivedB specific code 

       imp->DoWork();
   }

private:
   std::unique_ptr<AppSpecificImp> imp;
};

Edit to show Visitor pattern usage:

With visitor pattern you can do what you want but with more Effort.

class Visitor
{
public:
   virtual void VisitDerivedA(DerivedA* object) = 0;

   virtual void VisitDerivedB(DerivedB* object) = 0;
};

class Base
{
public:
   virtual void Visit(Visitor* visitor) = 0;
};

class DerivedA : public Base 
{ 
public:
   virtual void Visit(Visitor* visitor)
   {
       visitor->VisitDerivedA(this);
   }
};

class DerivedB : public Base 
{ 
public:
   virtual void Visit(Visitor* visitor)
   {
       visitor->VisitDerivedB(this);
   }
};

class AppSpecificVisitor : public Visitor
{
public:
   void VisitDerivedA(DerivedA* object)
   {
       // Do any work related to DerivedA class
   }

   void VisitDerivedB(DerivedB* object)
   {
       // Do any work related to DerivedB class
   }
}


int main()
{
    AppSpecificVisitor myVisitor;

    Base* myBase = // any class in your hierarchy

    myBase->Visit(&myVisitor);
}

As I said in comments with Visitor pattern you can add new functionally without changing the main hierarchy(Base->Derived types). You just define a new visitor implementation and write your logic for every class in main hierarchy. In your example you can pack app specific logic in an object and reference that in your derived objects that is an easier approach.

MRB
  • 3,752
  • 4
  • 30
  • 44
  • Thanks for your answer. This looks a bit like the visitor pattern, except that you store the app-specific object, right ? – Virus721 Nov 25 '16 at 10:59
  • @Virus721 One use case for visitor pattern is Double-Dispatching meaning that the operation that will be executed depend on both caller and callee(the object that operation get executed on it). Also Visitor define two class hierarchy: one the visitor and it's implementations and other hierarchy that accept visitor objects. In this way you can add new functionality with writing new visitor implementation without changing other hierarchy that accept visitor. In your case you don't need double dispatch, you only need a way to inject a logic to your library class hierarchy that Bridge pattern do. – MRB Nov 25 '16 at 11:23
1

Why not using a new set of hierarchy for application specific implementation ?

class AppBase
{
public:
    virtual ~AppBase() throw();
    virtual void work_with_app() = 0;
};

class Base
{
public:
    Base(AppBase& app) : m_app(app) {}
    virtual ~Base() throw();
protected:
    AppBase& m_app;
};

class DerivedA : public Base { DerivedA(AppBase& app) : Base(app) {} };

class DerivedB : public Base { DerivedA(AppBase& app) : Base(app) {} };

// Application specific implementation :
class AppLuaSpecific : public AppBase
{
public:
    void work_with_app()  { /* Lua app specific */ }
};

This way, your 1st hierarchy : Base, DerivedA, DerivedB can live without knowing anything about the app specific code implemented in AppLuaSpecific.

shrike
  • 4,449
  • 2
  • 22
  • 38
  • Thanks for your answer. This also looks similar to the visitor pattern, right ? – Virus721 Nov 25 '16 at 11:01
  • @Virus721 : No, this is not a visitor pattern. This is a simplified Bridge pattern : 1 instance of DerivedA/B can interact with 1 single instance of AppSpecific (which is defined at DerivedA/B object construction). The visitor pattern would be required If you needed multiple dispatch, meaning that an object of type DerivedA/B to be able to interact, in a single application, with several Application specific class derived from AppBase (which makes no sense as far as I understood your needs). So the Bridge pattern solution as I proposed should fit your needs (as suggested by MohammadRB too). – shrike Nov 25 '16 at 12:49
  • Well, within my application, I also have multiple ways to represent a (derived) exception within a Lua script. I can easier represent it as a string contaiing all the infos, or as multiple Lua values. This means that what gets executed depends one two things : the actual type of the exception, and the way I want to use it. So I guess visitor works better, if I understand correctly. – Virus721 Nov 25 '16 at 13:48
1

You can implement your own app-specific dispatch as follows (check it live on Coliru):

#include <iostream>
#include <typeinfo>


struct Base { virtual ~Base() {} };

struct DerivedA : public Base { };

struct DerivedB : public Base { };

namespace AppSpecific
{

template<class F>
void dispatch(const Base& b)
{
    const std::type_info& t = typeid(b);
    if ( t == typeid(DerivedA) )
        F::doit(static_cast<const DerivedA&>(b));
    else if ( t == typeid(DerivedB) )
        F::doit(static_cast<const DerivedB&>(b));
}

struct Foo
{
    static void doit(const DerivedA& da) { std::cout << "Foo(DerivedA)\n"; }
    static void doit(const DerivedB& db) { std::cout << "Foo(DerivedB)\n"; }
};

struct Bar
{
    static void doit(const DerivedA& da) { std::cout << "Bar(DerivedA)\n"; }
    static void doit(const DerivedB& db) { std::cout << "Bar(DerivedB)\n"; }
};

} // namespace AppSpecific

int main()
{
    DerivedA da;
    DerivedB db;

    Base& b1 = da;
    Base& b2 = db;
    AppSpecific::dispatch<AppSpecific::Foo>(b1);
    AppSpecific::dispatch<AppSpecific::Foo>(b2);
    AppSpecific::dispatch<AppSpecific::Bar>(b1);
    AppSpecific::dispatch<AppSpecific::Bar>(b2);
}
Leon
  • 31,443
  • 4
  • 72
  • 97