С++ Попытка создать "промежуточный" функтор

Под «промежуточным» функтором я подразумеваю: нормальный функтор, в котором один из аргументов может быть указан во время вызова. Проблема в том, что у меня есть временная шкала анимации (по сути, скалярные значения в определенных кадрах), и вывод этого должен быть передан в и из методов получения/установки в объектах, которые нужно анимировать. Вот упрощенный пример того, что я пытался:

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

Временная шкала содержит только числа с плавающей запятой, поэтому любой скалярный тип, который объект использует для своих методов получения/установки, должен быть приведен (у меня есть частичная специализация для чисел с плавающей запятой, которая устраняет приведение). ObjType — тип анимированного объекта, Getter и Setter — указатели на методы, а Scalar — тип Getter и Setter, с которым должны иметь дело.

Я думал, что это будет нормально, но компиляция не удалась, потому что у некоторых геттеров/сеттеров были другие аргументы, инициализированные по умолчанию. Я не думал, что это будет проблемой, потому что они по умолчанию! Но когда компилятор ожидал больше аргументов для указателя на метод, чем я предоставил, он потерпел неудачу.

Затем я попытался использовать вариативные аргументы шаблона, чтобы значения по умолчанию можно было вводить вручную, но упал при первом же препятствии, потому что я не могу сохранить пакет параметров как член, который будет многократно применяться в качестве аргументов к указателю на методы. Я также просматривал std::function и std::bind, я надеялся сохранить std::function в качестве члена с предустановленными аргументами метода getter/setter по умолчанию - и изменить соответствующий аргумент для одного из временная шкала непосредственно перед звонком. Только я не могу найти способ сделать это...

Есть ли у кого-нибудь предложения по достижению того, к чему я стремлюсь? Или мой дизайн в корне ошибочен и есть более простой подход?


person cmannett85    schedule 19.04.2012    source источник


Ответы (2)


std::function будет правильным решением. Будет просто использовать std::function<Scalar(const ObjType*)> в качестве вашего геттера, std::function<void(ObjType*, Scalar)> в качестве вашего сеттера (если Scalars неявно конвертируются в/из floats, я бы даже использовал 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; })

Обратите внимание, что могут быть более элегантные способы сделать это (например, может быть только одна функция, которая может служить как геттером, так и сеттером).

Зайдя еще дальше, вы могли бы

  • избавиться от переменной-члена obj_
  • избавиться от o параметров функций

Тогда лямбда-выражения должны будут запомнить объект, с которым они будут работать. Таким образом, приведенный выше вызов конструктора станет

Sy_propertyBridge(
  []() -> float { return my_obj->opacity; }, 
  [](float f) { my_obj->opacity=f; })
person jpalecek    schedule 19.04.2012
comment
Я пытался найти предлог для использования лямбды! Спасибо, попробую, отпишусь, как у меня дела. - person cmannett85; 20.04.2012
comment
Прекрасно работает, фантастическое решение. - person cmannett85; 21.04.2012

Насколько я понимаю, вы пытаетесь использовать методы «получить» и «установить», которые различаются в зависимости от класса, который вы здесь передаете? Если это так, я думаю, вам следует попытаться использовать чисто виртуальный базовый класс для ваших объектов «ObjType», а затем реализовать его в ваших классах, которые вы передаете. В терминах С#/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, а затем вызывайте их напрямую, если они делают то, что вы хотите.

person Kevin Anderson    schedule 19.04.2012
comment
Анимируемые объекты могут иметь произвольное количество анимируемых свойств, поэтому одного метода получения/установки недостаточно. Также на уровне обслуживания использование определяемого пользователем объекта-моста полностью отделяет анимируемые свойства объекта от его временной шкалы и позволяет конечному пользователю отключить их, разорвав соединения. Но в принципе вы не ошиблись. - person cmannett85; 20.04.2012