Iam
Iam

Reputation: 401

Workaround for dynamically adding members to C++ class


I would like to know how would you address such a problem:

I have a class Foo:

class Foo
{
public:
    Foo()  {   }
    ~Foo() {   }
    float member1() { return _member1; }
private: 
    float _member1;
    // other members etc...
}

A container class that, among other things, holds a container of pointers to Foo instances

class FooContainer
{
public:
   FooContainer() {   }
   ~FooContainer() {   }
   void addFoo(Foo* f) {_foos.push_back(f);}
private:
   boost::ptr_vector<Foo> _foos;
}

My problem is this: at runtime I am required to "add" new (completely different) members to Foo, depending on the instructions from the GUI. I could address the problem by creating two "decorators" like this:

class Decorator1
{
public:
   int   alpha() { return _alpha; }
   float beta()  { return _beta; }
private:
   int _alpha;
   float _beta;
}

class Decorator2
{
typedef std::complex<float> cmplx;
public:
   cmplx  gamma() { return _gamma; }
   double delta()  { return _delta; }
private:
   cmplx  _gamma;
   double _delta;
}

and then I would create two different Foo implementations:

class Foo1 : public Foo, public Decorator1 
{   }

class Foo2 : public Foo, public Decorator2 
{   }

and use each one according to the GUI command. However such a change would propagate through all my code and would force me to create two different versions for each class that uses Foo1 and Foo2 (e.g. I'd have to create FooContainer1 and FooContainer2).

A less intrusive way of doing this would be to create

class Bar: public Foo, public Decorator1, public Decorator2
{   }

and use this instead of Foo. In this case I'd call only the functions I need from Decorator1 and Decorator2 and ignore the others, but this seems to go against good OOP techniques.

Any suggestions regarding the problem ?

Upvotes: 3

Views: 4338

Answers (4)

Linuxios
Linuxios

Reputation: 35788

How about policy based templates? Have a template class Foo that takes a class as a template parameter. Then, have two methods that call the decorator methods:

tempate <class Decor>
class Foo
{
public:
    Foo() : { __d = Decor()  }
    ~Foo() {   }
    float member1() { return _member1; }
    Decor::method1type decoratorMember1() { return __d.getValueMethod1();}
    Decor::method2type decoratorMember2() { return __d.getValueMethod2();}
private: 
    float _member1;
    Decor __d;
    // other members etc...
}

Then, in your complex decorator:

class Decor1 {
  typedef std::complex<float> method1type;
  typedef double method2type;
public:
  method1type getValueMethod1() {return _gamma}
  method2type getValueMethod2() {return _delta}
private:
  method1type _gamma;
  method2type _delta;
}

Same for the other. This way, your Foo code can have anything added to it, even if it's already compiled. Just make a declarator class. And instead of instantiating Foo1, do this:

Foo<Decor1> f;

Upvotes: 0

Walter
Walter

Reputation: 45444

the fundamental concept of a class is that it's encapsulated and hence that one cannot add members after the definition (though you can use polymorphism and create derived classes with additional members, but they cannot be called through pointer of the original class: you must cast them to derived which is dangerous), in particular not at run time.

So it seems to me you're requirement breaks the essential idea of OO programming. This suggests a simple solution: use non-member functions. They can be defined at any time, even run time (when you would also need to compile them). The overhead of the function pointer is the same as before (when you would need a pointer to a new member function).

Upvotes: 0

user257111
user257111

Reputation:

It sounds like you're looking to handle mixin-type functionality. To do that, you could use templates. This isn't run time in the sense that copies of each class will be generated, but it does save you the typing.

So for each decorator, do something like:

template<class TBase> class Decorator1 : public TBase
{
public:
    void NewMethod();
}

Then you can, for example:

Foo* d = new Decorator1<Foo1>(...);

Of course, the only way to make this work at runtime is to decide which type you're going to create. However, you still end up with the type Foo, Foo1 and Decorator1 so you can cast between them/use RTTI as you need to.

For more on this, see this article and this document


Although I've suggested it as a potential solution, I personally would be tempted to go with the polymorphism suggestion if at all possible - I think that makes for better, easier to maintain code because parts of class implementations aren't scattered all over the place using mixins. Just my two cents - if you think it works, go for it.

Upvotes: 0

PermanentGuest
PermanentGuest

Reputation: 5331

Why don't you use simple polymorphism like this?

class Foo
{
public:
    Foo()  {   }
    virtual ~Foo() {   }
    float member1() { return _member1; }
private: 
    float _member1;
    // other members etc...
}

class Foo1 : public Foo
{   
    public:
   int   alpha() { return _alpha; }
   float beta()  { return _beta; }
private:
   int _alpha;
   float _beta;
}

class Foo2 : public Foo
{   
    typedef std::complex<float> cmplx;
public:
   cmplx  gamma() { return _gamma; }
   double delta()  { return _delta; }
private:
   cmplx  _gamma;
   double _delta;
}

class FooContainer
{
public:
   FooContainer() {   }
   ~FooContainer() {   }
   void addFoo(Foo* f) {_foos.push_back(f);}
private:
   boost::ptr_vector<Foo> _foos;
}

Then the client code need not change. According to the GUI command you can create Foo1 or Foo2 and add it to the single container. If necessary, you can use the dynamic_cast on Foo pointer to cast to Foo1 or Foo2. But, if you have written the client code properly, then this wouldn't be needed.

Upvotes: 8

Related Questions