Sign in to follow this  
Timkin

C++ OO design query

Recommended Posts

Timkin    864
Can anyone think of a better design for achieving the following... ? I'm writing some library functions which will operate on a known type. However, the operator is provided by the end user (both the 'type' of operator and the function to determine the result of applying the operator to the given object type) My current solution is to implement a functor adapted from an object they supply, as in:
template <typename RETURNTYPE, typename ARG1TYPE, typename ARG2Type>
class Functor2Base{
public:
	virtual RETURNTYPE operator()(ARG1TYPE, ARG2TYPE)=0;
};

template <typename ObjType, typename RetType, typename Arg1Type, typename Arg2Type>
class AdaptedFunctor: public Functor2Base<RetType,Arg1Type,Arg2Type>{
public:
	typedef RetType (ObjType::*pointer)(Arg1Type, Arg2Type);

private:
	ObjType		m_rObj;
	pointer		m_pAdFunc;

public:
	AdaptedFunctor(ObjType& _rObj, pointer _fp):m_rObj(_rObj),m_pAdFunc(_fp){}
	RetType operator()(Arg1Type _arg1, Arg2Type _arg2){
		return m_rObj.*m_pAdFunc(_arg1,_arg2);
	}
};


template <typename ObjType>
class Operator{
public:
	typedef typename ObjType::operator_type op_type;
	typedef Functor2Base<data_type,data_type,op_type>*   functor_pointer;
	typedef data_type (ObjType::*function_pointer)(data_type, op_type);
        typedef std::list<op_type>     op_list;
        typedef typename op_list::const_iterator op_iterator;


private:
	functor_pointer		m_pOpFunc;
        op_list      m_cOpList;

public:
// ... detail omitted ... //

	void RegisterOperatorFunction(ObjType& _rObj, function_pointer _fp){
		m_pOpFunc = (functor_pointer)new AdaptedFunctor<ObjType,data_type,data_type,op_type>(_rObj,_fp);
	}

        void RegisterOperation(op_type& _op){
                m_cOpList.insert(_op);
        }
};


and then somewhere in my Operator class methods I'm making calls of the type
op_iterator opIter = m_cOpList.begin();
for(;opIter!=m_cOpList.end();opIter++){
  newData = m_pOpFunc(oldData,*opIter);
  // do something with new data
};

So, my question is, "is there a better way?" I had considered doing this with an object heirarchy (designing a pure abstract base class interface) and force them to derive from it, but that seems a little more restrictive than just enforcing that their object implements a member function with the signature 'data_type (data_type,op_type)'. (btw, since this is an OO design, I'm not going to give them the flexibility of using a non-member function as the operation function... mean, aren't I!) ;) Thanks for any and all constructive input. Cheers, Timkin

Share this post


Link to post
Share on other sites
Timkin    864
I hacked this solution together at 3am using some old code I had in another app (sheesh, doesn't that sound like a recipe for disaster!). It's distinctly likely that it doesn't do what I want it to do in an efficient manner!

The dynamic allocation was a result of my brain thinking about run time registration of function, based on external data, but that's probably not necessary. I need to consider that aspect a little more.

Again, the virtual call arose because I 'borrowed' from some functor code I had without thinking too deeply. My brain was thinking with regards to using base class pointers to store AdaptedFunctor objects in the Operator class. Again, probably because I was thinking of run time binding... but that doesn't seem to make sense to me this morning.

Perhaps you (or someone else) can offer some advice then. What I want to achieve is the following:

I'm relying on the user of the library to provide an object that implements a member function that takes two arguments, data_type and op_type (where op_type is a type defined in their object) and returns a data_type object. Im not certain its relevant, but data_type is a template type. I then need to call that member function regularly within my library code. Obviously though, when I'm compiling the library, I have no idea of the type of object they'll be supplying. The two possibilities that seemed obvious to me were inheritence from a base interface class that I define and calling their implementation of the method (which seemed overly restrictive), or, binding their object and member function to another object of known type and making calls through that object (so writing an interface object, essentially).

Is there a 3rd option? If not, is there a reason to prefer one of the solutions I mentioned over the other? I'm not expecting anyone to do my work for me, but obviously if there are some errors in the way I'm going about my implementation, some snippets of pseudo code indicating the correct way would really be appreciated.

Thanks,

Timkin

Share this post


Link to post
Share on other sites
Timkin    864
Okay, I've given it some more thought...

I require the dynamic allocation because the user's object and method are not bound when the Operator class is created, but rather by a subsequent call to the registration function. I *could* bind these when the Operator class object is created, but that to me seems to violate the idiom of don't initialise values when an object is created (unless it makes absolute sense to). Also, this would not give me the ability to easily rebind, without adding in some complicated mechanisms within the AdaptedFunctor class. With dynamic allocation I can simply rebind by deleting the old AdaptedFunctor instance and allocating a new one. Is there some reason I should not take this path (performance isn't an issue given the likely infrequency of rebinding).

As for the base class and virtual call, I could easily avoid this, but by providing it as an interface class I leave open the capacity for the user to derive from this class directly to establish their object and use the operator() method as the access method. In this case, AdaptedFunctor would just be an alias object for their object, with limited interface obviously. The benefit I see for using the pure virtual base class is it exposes only the small aspect of the derived classes that I want the user to be able to interact with. Obviously the downside is the increased memory footprint of the AdaptedFunctor class and a less efficient function call mechanism. Is this really such a big issue though?

Cheers,

Timkin

Share this post


Link to post
Share on other sites
Julian90    736
MY prefered solution would be boost::function + boost::bind or std::mem_fn
typedef boost::function<ObjType::data_type (ObjType::data_type, ObjType::op_type)> FuncType;
FuncType myfunc = boost::bind(ObjType::MyFunc, myObj, _1, _2);
but if thats not an option then a third option to the ones you mentioned would be to take the function as a template paramater (if it will be known at compile time) something along these lines...


template<typename ObjType,
typename ObjType::data_type (ObjType::*Func)(typename ObjType::data_type,
typename ObjType::op_type)>
struct MyStruct
{
void DoStuff()
{
ObjType obj;
typename ObjType::data_type val;
typename ObjType::op_type op;
val = obj->*Func(val, op);
}
};


Ofcourse that has tradeofs of its own, such as draging things into header files and increased compile times.... which may make it no good.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this