views:

1261

answers:

7

I'm sorry if my question is so long and technical but I think it's so important other people will be interested about it

I was looking for a way to separate clearly some softwares internals from their representation in c++

I have a generic parameter class (to be later stored in a container) that can contain any kind of value with the the boost::any class

I have a base class (roughly) of this kind (of course there is more stuff)

class Parameter 
{
public:
    Parameter()
    template typename<T> T GetValue() const { return any_cast<T>( _value ); }
    template typename<T> void SetValue(const T& value) { _value = value; }
    string GetValueAsString() const = 0;
    void SetValueFromString(const string& str) const = 0;
private:
    boost::any _value;
}

There are two levels of derived classes: The first level defines the type and the conversion to/from string (for example ParameterInt or ParameterString) The second level defines the behaviour and the real creators (for example deriving ParameterAnyInt and ParameterLimitedInt from ParameterInt or ParameterFilename from GenericString)

Depending on the real type I would like to add external function or classes that operates depending on the specific parameter type without adding virtual methods to the base class and without doing strange casts

For example I would like to create the proper gui controls depending on parameter types:

Widget* CreateWidget(const Parameter& p)

Of course I cannot understand real Parameter type from this unless I use RTTI or implement it my self (with enum and switch case), but this is not the right OOP design solution, you know.

The classical solution is the Visitor design pattern http://en.wikipedia.org/wiki/Visitor_pattern

The problem with this pattern is that I have to know in advance which derived types will be implemented, so (putting together what is written in wikipedia and my code) we'll have sort of:

struct Visitor 
{
  virtual void visit(ParameterLimitedInt& wheel) = 0;
  virtual void visit(ParameterAnyInt& engine) = 0;
  virtual void visit(ParameterFilename& body) = 0;
};

Is there any solution to obtain this behaviour in any other way without need to know in advance all the concrete types and without deriving the original visitor?

Thanks for your time

A: 

If I understand this correctly...

We had a object that could use different hardware options. To facilitate this we used a abstract interface of Device. Device had a bunch of functions that would be fired on certain events. The use would be the same but the various implementations of the Device would either have a fully-fleshed out functions or just return immediately. To make life even easier, the functions were void and threw exceptions on when something went wrong.

graham.reeds
+1  A: 

I've used this ("acyclic visitor") to good effect; it makes adding new classes to the hierarchy possible without changing existing ones, to some extent.

DrPizza
A: 

For completeness's sake:

it's of course completely possible to write an own implementation of a multimethod pointer table for your objects and calculate the method addresses manually at run time. There's a paper by Stroustrup on the topic of implementing multimethods (albeit in the compiler).

I wouldn't really advise anyone to do this. Getting the implementation to perform well is quite complicated and the syntax for using it will probably be very awkward and error-prone. If everything else fails, this might still be the way to go, though.

Konrad Rudolph
A: 

Dr. Pizza: thanks, your solution seems the closest to what I was thinking, but the problem is still the same and the method is actually relying on dynamic_cast, that I was trying to avoid as a kind of (even if weak) RTTI method

Maybe it is better to think to some solution without even citing the visitor Pattern and clean our mind. The purpose is just having the function such:

Widget* CreateWidget(const Parameter& p)

behave differently for each "concrete" parameter without losing info on its type

martjno
A: 

I am having trouble understanding your requirements. But Ill state - in my own words as it were - what I understand the situation to be:

  • You have abstract Parameter class, which is subclassed eventually to some concrete classes (eg: ParameterLimitedInt).

  • You have a seperate GUI system which will be passed these parameters in a generic fashion, but the catch is that it needs to present the GUI component specific to the concrete type of the parameter class.

  • The restrictions are that you dont want to do RTTID, and dont want to write code to handle every possible type of concrete parameter.

  • You are open to using the visitor pattern.

With those being your requirements, here is how I would handle such a situation:

I would implement the visitor pattern where the accept() returns a boolean value. The base Parameter class would implement a virtual accept() function and return false.

Concrete implementations of the Parameter class would then contain accept() functions which will call the visitor's visit(). They would return true.

The visitor class would make use of a templated visit() function so you would only override for the concrete Parameter types you care to support:

class Visitor
{
public:
  template< class T > void visit( const T& param ) const
  {
    assert( false && "this parameter type not specialised in the visitor" );
  }
  void visit( const ParameterLimitedInt& ) const; // specialised implementations...
}

Thus if accept() returns false, you know the concrete type for the Parameter has not implemented the visitor pattern yet (in case there is additional logic you would prefer to handle on a case by case basis). If the assert() in the visitor pattern triggers, its because its not visiting a Parameter type which you've implemented a specialisation for.

One downside to all of this is that unsupported visits are only caught at runtime.

genix
+4  A: 

For a generic implementation of Vistor, I'd suggest the Loki Visitor, part of the Loki library.

Josh
A: 

Genix: you got my requirements except one: I will write code for every kind of possible concrete parameter but I don't want that all the concrete parameters know all the other parameter types, so your solution won't fit since I have anyway to declare all template specialization available in the Visitor declaration.

I'm open to any implementation (not necessarily visitor) that will allow external functions correctly depending on the concrete type of the parameter.

Josh: thanks for the link to Loki, I'll use it if I don't find any better solution and I'll go with the standard Visitor.

martjno