C++ 尝试创建"中间"函子

C++ Trying to create an 'Intermediate' functor

本文关键字:中间 函子 创建 C++      更新时间:2023-10-16

我所说的"中间"函子的意思是:一个普通的函子,其中一个参数可以在调用时指定。 问题是我有一个动画时间轴(本质上是特定帧的标量值),并且它的输出需要通过管道传输到要动画的对象中的 getter/setter 方法。 这是我正在尝试的简化示例:

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_;
};

时间轴只包含浮点数,因此无论对象用于其 getter/setter 方法的标量类型,都必须进行强制转换(我对浮点数进行了部分专门化,取消了强制转换)。 ObjType是动画对象类型,GetterSetter是指向方法的指针,Scalar是 Getter 和 Setter 类型,需要处理。

我认为这很好,但是编译失败了,因为一些getter/setter具有其他默认初始化的参数。 我不认为这会是一个问题,因为它们是默认的! 但是当编译器期望指针指向方法的参数比我提供的更多时,它失败了。

然后,我尝试使用可变参数模板参数,以便可以手动输入默认值,但在第一个障碍处失败了,因为我无法将参数包存储为成员以作为参数重复应用于指针指向方法。 我也一直在查看 std::function 和 std::bind,我希望能够使用 getter/setter 方法的默认 args 预设将 std::function 存储为成员 - 并在调用之前从时间线更改适当的参数。 只是我找不到办法...

有人对实现我的目标有任何建议吗? 还是我的设计从根本上存在缺陷,并且有更简单的方法?

std::function将是要走的路。只会使用 std::function<Scalar(const ObjType*)> 作为你的 getter,std::function<void(ObjType*, Scalar)> 作为你的二传手(如果 Scalar s 可以隐式转换为 float s,我什至会使用 std::function<float(ObjType const*)>std::function<void(ObjType*, float)> ,或者)。您可以初始化这些,例如。使用λ函数:

Sy_propertyBridge(my_obj, 
  [](const MyObjType* o) -> float { return o->opacity; }, 
  [](const MyObjType* o, float f) { o->opacity=f; })

请注意,可能有更优雅的方法可以做到这一点(例如,可能只有一个函数可以同时用作 getter 和 setter)。

更进一步,您可以

  • 摆脱obj_成员变量
  • 摆脱函数的o参数

然后,lambda 必须记住它们将对其进行操作的对象。所以上面的构造函数调用将变为

Sy_propertyBridge(
  []() -> float { return my_obj->opacity; }, 
  [](float f) { my_obj->opacity=f; })

据我了解,您正在尝试使用"获取"和"设置"方法,这些方法因您在此处传递的类而异? 如果是这样,我认为您应该尝试在"ObjType"对象上使用纯虚拟基类,然后在要传入的类中实现它。 在 C#/Java 术语中,这指的是接口。

基本上是这样的:

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
    }
}

然后你的类变成:

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_;
};

但实际上,可能有一种直接的方法可以完全取消您的Sy_propertyBridge类。 只需存储指向IGetSetter的指针数组,然后在它们执行所需操作时直接调用它们。