Reputation: 22366
What I mean by an 'intermediate' functor is: a normal functor where one of the arguments can be specified at call-time. The problem is that I have an animation timeline (essentially scalar values at particular frames), and the output of this needs to be piped to and from getter/setter methods within the objects to be animated. Here is a simplified example of what I was attempting:
template < class ObjType, class Getter, class Setter, typename Scalar >
class Sy_propertyBridge : public Sy_abstractPropertyBridge
{
public:
Sy_propertyBridge( ObjType* object, Getter getter,
Setter setter )
: obj_( object ), get_( getter ),
set_( setter ) {}
virtual ~Sy_propertyBridge() {}
inline virtual float get() const
{
// Cannot handle default arguments.
Scalar tmp = ( obj_->*get_ )();
return static_cast< float >( tmp );
}
inline virtual void set( float value )
{
Scalar tmp = static_cast< Scalar >( value );
( obj_->*set_ )( tmp );
}
private:
ObjType* obj_;
Getter get_;
Setter set_;
};
The timeline only holds floats, so whatever scalar type the object uses for it's getter/setter methods, must be cast (I have a partial specialisation for floats that does away with the cast). ObjType
is the animated object type, Getter
and Setter
are pointers-to-methods, and Scalar
is the type Getter and Setter and expected to deal with.
I thought this would be fine, but compilation failed because some of the getter/setters had other default-initialised arguments. I didn't think this would be a problem, because they're default! But when the compiler expected more args for the pointer-to-method than I was supplying, it failed.
I then tried using variadic template args so the default values could be manually put in, but fell at the first hurdle because I can't store the parameter pack as a member to be repeatedly applied as args to the pointer-to-methods. I have also been looking at std::function and std::bind, I was hoping to be able to store a std::function as a member with the getter/setter method's default args preset - and change the appropriate arg for one from the timeline just before calling. Only I can't find a way to do that...
Does anyone have any suggestions to achieve what I am aiming for? Or is my design fundamentally flawed and there is an easier approach?
Upvotes: 1
Views: 302
Reputation: 47770
std::function
would be the way to go. Will just use std::function<Scalar(const ObjType*)>
as your getter, std::function<void(ObjType*, Scalar)>
as your setter (if the Scalar
s are implicitly convertible to/from float
s, I'd even use std::function<float(ObjType const*)>
and std::function<void(ObjType*, float)>
, resp.). You can initialize these eg. with lambda functions:
Sy_propertyBridge(my_obj,
[](const MyObjType* o) -> float { return o->opacity; },
[](const MyObjType* o, float f) { o->opacity=f; })
Note that there may be more elegant ways of doing this (eg. there could be only one function that could serve both as a getter and a setter).
Taking it even further, you could
obj_
member variableo
parameters to the functionsThe lambdas would then have to remember the object they will operate on. So the above constructor call would become
Sy_propertyBridge(
[]() -> float { return my_obj->opacity; },
[](float f) { my_obj->opacity=f; })
Upvotes: 1
Reputation: 7010
From what I understand, you are trying to use "get" and "set" methods that vary per class you're passing in here? If so, I think you should be trying to use a purely virtual base class on your "ObjType" objects, and then implement that in your classes you're passing in. In C#/Java terms, an interface.
Basically this:
class IGetSetter
{
virtual float get() const = 0;
virtual void set(float) = 0;
}
class ObjType1 : public IGetSetter
{
virtual float get() const
{
// Implementation
}
virtual void set(float a)
{
// Implementation
}
}
class ObjType2 : public IGetSetter
{
virtual float get() const
{
// Implementation
}
virtual void set(float a)
{
// Implementation
}
}
Then your class becomes:
template < typename Scalar >
class Sy_propertyBridge : public Sy_abstractPropertyBridge
{
public:
Sy_propertyBridge( IGetSetter* object)
: obj_( object ) {}
virtual ~Sy_propertyBridge() {}
inline virtual float get() const
{
Scalar tmp = obj_->get(); // Uses polymorphism to find the right method
return static_cast< float >( tmp );
}
}
inline virtual void set( float value )
{
Scalar tmp = static_cast< Scalar >( value );
obj_->set( tmp ); // Uses polymorphism to find the right method
}
private:
ObjType* obj_;
};
But actually, there's probably a straightforward way of axing your Sy_propertyBridge class entirely. Just store an array of pointers to IGetSetter
and then invoke those directly if they do what you want.
Upvotes: 0