Как использовать разные функтоиды в массиве или векторе

Я написал небольшой фрагмент кода, в котором я могу вызывать функции установки и получения, упакованные в функтоид, используя шаблоны mem_fun.

Теперь я хотел бы использовать этот подход поверх иерархии классов, где каждый класс может иметь геттер и сеттер, которые можно зарегистрировать как пару в векторе или массиве, чтобы при необходимости можно было вызывать геттер и сеттер. GUIObject и GUICompositeObject являются примерами классов из описанной иерархии классов. К сожалению,bound_mem_fun_t для объектов имеют разные типы, и поэтому я не знаю, как интегрировать их в массив/вектор указателей на функторы.

В С++ 11 я бы использовал std::function. Есть ли способ эмулировать это в С++ 98?

Поскольку наш компилятор поддерживает только С++98, я не могу использовать новые возможности С++11 или С++14. Также не допускается повышение.

#include <functional>

class GUIObject
{
    int m_Alpha;
public:
    void SetAlpha(int a) { m_Alpha = a;};
    int GetAlpha() {return m_Alpha;};
};


class GUICompositeObject: public GUIObject
{
    int m_NumOfChilds;
public:
    void SetNumOfChilds(int NumOfChilds) { m_NumOfChilds = NumOfChilds;};
    int GetNumOfChilds() {return m_NumOfChilds;};
};

template<typename T>
struct bound_mem_fun_t 
{ 
    bound_mem_fun_t(std::mem_fun_t<int, T> GetFunc, std::mem_fun1_t<void, T, int> SetFunc, T* o) :
            m_GetFunc(GetFunc), m_SetFunc(SetFunc), obj(o) { } ;
    int operator()() { return m_GetFunc(obj); } ;
    void operator()(int i) { m_SetFunc(obj, i); } ;
    std::mem_fun_t<int, T> m_GetFunc; 
    std::mem_fun1_t<void, T, int> m_SetFunc; 
    T* obj; 
};


int main()
{
    GUIObject kGUIObject;
    GUICompositeObject kCompObj;

    bound_mem_fun_t<GUIObject> GUIObjectFunc(std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject);
    GUIObjectFunc(17);
    int ii = GUIObjectFunc();

    bound_mem_fun_t<GUICompositeObject> GUICompObjectFunc(std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj);
    GUICompObjectFunc(17);
    int iChilds = GUICompObjectFunc();

    return 0;
}

Вот полное решение после ответа @filmors:

#include <functional>
#include <vector>
#include <iostream>

class GUIObject
{
    int m_Alpha;
public:
    void SetAlpha(int a) { m_Alpha = a;};
    int GetAlpha() {return m_Alpha;};
};


class GUICompositeObject: public GUIObject
{
    int m_NumOfChilds;
public:
    void SetNumOfChilds(int NumOfChilds) { m_NumOfChilds = NumOfChilds;};
    int GetNumOfChilds() {return m_NumOfChilds;};
};

struct bound_mem_fun_base 
{
    virtual int operator()() =0;
    virtual void operator()(int) =0;
};

template<typename T>
struct bound_mem_fun_t : public bound_mem_fun_base
{ 
    bound_mem_fun_t(std::mem_fun_t<int, T> GetFunc, std::mem_fun1_t<void, T, int> SetFunc, T* o) :
            m_GetFunc(GetFunc), m_SetFunc(SetFunc), obj(o) { } ;
    virtual int operator()() { return m_GetFunc(obj); } ;
    virtual void operator()(int i) { m_SetFunc(obj, i); } ;
    std::mem_fun_t<int, T> m_GetFunc; 
    std::mem_fun1_t<void, T, int> m_SetFunc; 
    T* obj; 
};

template<typename T> bound_mem_fun_t<T>* make_setter(std::mem_fun_t<int, T> GetFunc, std::mem_fun1_t<void, T, int> SetFunc, T* o)
{
    return new bound_mem_fun_t<T> (GetFunc, SetFunc, o);
}

int main()
{
    GUIObject kGUIObject;
    GUICompositeObject kCompObj;

    std::vector<bound_mem_fun_base*> kBoundVector;

    kBoundVector.push_back(new bound_mem_fun_t<GUIObject> (std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject));
    kBoundVector.push_back(new bound_mem_fun_t<GUICompositeObject> (std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj));
    kBoundVector.push_back(make_setter<GUIObject> (std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject));
    kBoundVector.push_back(make_setter<GUICompositeObject> (std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj));

    for (int i = 0; i < 4 ; i++)
    {
        (*kBoundVector[i])(i*10);
        int res = (*kBoundVector[i])();
        std::cout << "Getter result " << res << "\n";
    }

    return 0;
}

К сожалению, функция make_setter на самом деле не сокращает время создания функтора. Любые идеи будут приветствоваться.


person Bernd L.    schedule 14.04.2015    source источник


Ответы (2)


Просто дайте вашему bound_mem_fun_t<T> общий базовый класс и используйте динамическую диспетчеризацию для решения вашей проблемы:

struct bound_mem_fun_base {
    virtual int operator()() = 0;
    virtual void operator()(int) = 0;
};

template <typename T>
struct bound_mem_fun_t : bound_mem_fun_t ...

Затем вы можете сохранить указатели на bound_mem_fun_base в своем векторе и называть элементы как (*v[0])().

Кроме того, TR1 содержит std::tr1::function, это доступно?

person filmor    schedule 14.04.2015
comment
Спасибо за подсказку. Я пробовал это, но почему-то, возможно, я что-то упускаю. code std::vector‹bound_mem_fun_base› kBoundVector; bound_mem_fun_t‹GUIObject› GUIObjectFunc(std::mem_fun(&GUIObject::GetAlpha), std::mem_fun(&GUIObject::SetAlpha), &kGUIObject); bound_mem_fun_t‹GUICompositeObject› GUICompObjectFunc(std::mem_fun(&GUICompositeObject::GetNumOfChilds), std::mem_fun(&GUICompositeObject::SetNumOfChilds), &kCompObj); code Я получаю, чтоbound_mem_fun_base не может создать экземпляр абстрактного класса... - person Bernd L.; 14.04.2015
comment
Да, как сказано, вы должны использовать указатели, то есть std::vector<bound_mem_fun_base*> kBoundVector;, а затем kBoundVector.push_back(new bound_mem_fun_t<GUICompositeObject> .... Для удобства я бы также создал шаблонную функцию make_fancy_setter<T>, которая создает для вас bound_mem_fun_t<T>. - person filmor; 14.04.2015
comment
Спасибо еще раз. Я добавил выше более новую версию. Мне бы очень помогло, если бы вы снова помогли мне сделать еще один шаг. - person Bernd L.; 14.04.2015
comment
После дальнейшего чтения у меня есть полное решение, включая создание шаблона функции. К сожалению, функции make не сильно сокращают создание функтора. - person Bernd L.; 14.04.2015
comment
Ну, вы можете отказаться от параметра шаблона, make_setter(...) достаточно, так как можно вывести T. - person filmor; 14.04.2015
comment
Кроме того, я думаю, что вы также можете избавиться от этих вызовов std::mem_fun, сделав первые два аргумента либо явными указателями на функции-члены, либо просто аргументами шаблона. - person filmor; 14.04.2015
comment
Вывод типа сработал, и предложение явных указателей на функции тоже понятно. Не могли бы вы дать более подробную информацию об аргументах шаблона? - person Bernd L.; 14.04.2015

Сначала замечание о std::function из С++ 11: это не решит вашу проблему, потому что вам нужен уже ограниченный указатель функции. Этот указатель должен быть привязан к вашему объекту. Я считаю, что вам нужна собственная реализация std::bind.

Я начал только очень! небольшой класс Binder, который, надеюсь, станет отправной точкой для ваших нужд. Если вам нужны списки параметров шаблона в старых версиях С++, обратите внимание на loki. http://loki-lib.sourceforge.net/

В качестве подсказки я могу привести вам краткий пример того, что я сделал:

class A
{   
    private:
        int val;
    public:
        A(int i): val(i) {}  
        void Do(int i) { std::cout << "A " << val<< " " << i << std::endl; }
};  

class B
{   
    private:
        int val;
    public:
        B(int i): val(i){}
        void Go(int i) { std::cout << "B " << val << " " << i << std::endl; }
};  

class Base
{   
    public:
        virtual void operator()(int i)=0; 
};  

template <typename T>
class Binder: public Base
{   
    void (T::*fnct)(int);
    T* obj;

    public:
    Binder( void(T::*_fnct)(int), T*_obj):fnct(_fnct),obj(_obj){}  
    void operator()(int i)
    {   
        (obj->*fnct)(i);
    }   

};  

int main()
{   
    A a(100);
    B b(200);

    // c++11 usage for this example
    //std::function<void(int)> af=  std::bind( &A::Do, &a, std::placeholders::_1);
    //af(1);

    // hand crafted solution
    Base* actions[2];

    actions[0]= new Binder<A>( &A::Do, &a);
    actions[1]= new Binder<B>( &B::Go, &b);

    actions[0]->operator()(55);
    actions[1]->operator()(77);

}   
person Klaus    schedule 14.04.2015