IMHO.WS

IMHO.WS (http://www.imho.ws/index.php)
-   Программирование (http://www.imho.ws/forumdisplay.php?f=40)
-   -   таблица пойнтеров на методы разных классов (http://www.imho.ws/showthread.php?t=118635)

crawler 30.04.2007 11:46

таблица пойнтеров на методы разных классов
 
Вопрос к гуру С++ :
Имеется в наличии
Код:

void C1::m1(int );
void C1::m2(int );
void C2::m3(int );
void C2::m4(int );

- у всех методов одинаковые входные и выходные параметры

нужно сделать такой выкрутас чтобы
а) хранить таблицу пойнтеров на методы (заметьте разных классов - кастинг в пойнтер войд не предлагать)
б) в зависимисти от инпута вызывать соответсвующий метод по пойнтеру: т.е если инпут 1 - вызывать по пойнтеру на C1::m1(), а если 3 - вызывать C2::m3()

Сегодня у меня есть уже класс для вызова методов но только для одного класса, и я вызываюнужный метод по
Код:

(m_pBaseClass->*pMethod)(nIntValue)
. Как сделать это для нескольких (или хотя бы 2) классов.
Заранее спасибо.

EvroStandart 30.04.2007 13:39

Я конечно не гуру С++, но если у тебя куча классов с одинаковыми методами, это похоже на стандартный фабричный шаблон.

http://www.google.ee/search?hl=et&cl...&btnG=Otsi&lr=

crawler 30.04.2007 15:10

похоже на правду. А примерчик накидать можно? как сделать
Код:

C3 executor;
executor.run(3, 18);

чтобы вызвалась C2::m3(18)

P.S. я не программер, и пример жизненно необходим. Если C1 и C2 наследники С0 - это что-то меняет?

Drakosha 30.04.2007 16:08

Предложение: не надо хранить указатели на функции. Храни функторы (functor). Т.к. это нормальные объекты то делаешь наследование и всё ок.
Вот объяснение: http://en.wikipedia.org/wiki/Function_object

crawler 01.05.2007 13:51

Повторю еще раз Я -НЕ ПРОГРАММИСТ. Мне нужно сделать так, чтобы я мог запускать методы разных классов не зная их имени и порядок во время компиляции.

Фраза
Цитата:

Т.к. это нормальные объекты то делаешь наследование и всё ок
мне ничего не говорит. чем "нормальные объекты" отличаются от ненормальных ? зачем делать наследование?:idontnow:
Код:


class T;
class Run
{
typedef int (T::*Method)(int );
typedef struct {
    int    index
public:
    Run(T* pT)    { m_pBaseClass = pT; };

    int Call( int nMethIndex, int nParam)
        {
    int res;
    if (nMethIndex == 1)
        res = (m_pBaseClass->*m_m1)(nParam);
    if (nMethIndex == 2)
        res = (m_pBaseClass->*m_m2)(nParam) ;
    return res;
}
    void    Map( Method m1, Method m2) { m_m1 = m1; m_m2 = m2;}
private:
    T*    m_pBaseClass;
    Method    m_m1;
    Method    m_m2;
};

Как вот это расширить на 2 класса ?

Drakosha 01.05.2007 14:08

примерно так выглядят фанкторы:

Код:

class Functor {
public:
  virtual void Call(Smth & smth) = 0;
};

class FunctorA : public Functor {
public:
  virtual void Call(Smth & smth) { smth->RunA(); }
};

class FunctorB : public Functor {
public:
  virtual void Call(Smth & smth) { smth->RunB(); }
};

в таблице ты хранишь указатели: Functor*
надеюсь стало понятнее

crawler 01.05.2007 14:24

Код:

class C1;
class C2;

class FunctorA : public Functor {
public:
  virtual void Call( C1 & smth) { smth->RunA(); }
};

class FunctorB : public Functor {
public:
  virtual void Call(C2 & smth) { smth->RunB(); }
};

...

Functor fTable[2];
... fill table here
...
fTable[0].Call(); // вызов RunA();
fTable[1].Call();// вызов RunВ();

что-то в этом роде ?

Drakosha 01.05.2007 14:32

к сожалению, прототип Call должен быть одинаков во всех классах. Поэтому может так:

Код:

class C1;
class C2;

class FunctorA : public Functor {
public:
  virtual void Call( Input & input) { m_c1.Do(input); }
C1 & m_c1;
};

class FunctorB : public Functor {
public:
  virtual void Call(Input & input) { m_c2.Do(input); }
C2 & m_c2;
};


crawler 01.05.2007 18:06

чего-то слишком сложно получается... Я ведь с самого начала хотел избежать выраженного вызова m_c1.Do() Если несколько методов у каждого класса C1, C2, то как я с ними расправлюсь?
... или я чего не понимаю ?

пока додумался использовать темплейты, но как сделать для 2 классов одновременно - не понимаю

Код:

template <typename BaseClass>
class Caller
{
    typedef int (BaseClass::*Method)(int);

public:
    Caller    (BaseClass *pInstance) { m_pInstance = pInstance; }
    void        Map    ( std::string Name, Method mPtr) { m_Method[Name]= mPtr; }
    int        Call    ( std::string Name, int param) {
        if (m_Method.find(Name)!=m_Method.end())
            return  (m_pInstance->*(m_Method[Name]))(param);
        else return 0;
    }
private:
    std::map<std::string,Method>    m_Method;
    BaseClass                      *m_pInstance;
};


Drakosha 01.05.2007 18:14

Я наверное сам ничего не понимаю, запутался. У тебя есть:
class B;
class D1 : public B;
class D2 : public B;

почему бы не определить методы m... виртуальными и держа указатель на B просто вызывать их?

PSyton 01.05.2007 19:58

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

crawler 02.05.2007 10:35

Для чего это нужно - я написал простенький интерпретатор, заточенный под мои нужды. У меня есть несколько алгоритмов которые применяются к интерфейс-классу параметров (в примере заменено на int). Алгоритмы запускаются один за другим в какой-то последовательности, заданной в файле конфигурации. Я читаю файл конфигурации и запускаю алгоритмы в том порядке, как в нем указано.
У меня есть несколько уже существующих классов для разных семейств алгоритмов. Пока я работаю с каким-то одним классом -все нормально, тот код что я привел в посте #9 меня полностью устраивает: там есть инстанс (я имею в виду инициализированный об'ект - пользуююсь своим жаргоном) класса, и я запускаю его методы как мне и нужно.
Но если мне надо работать с 2 классами одновременно - то мне нужно какое-то образование, чтобы держало инстансы всех нужных мне классов, и вызывало их методы в зависимости от входных данных. Надо учесть что классы алгоритмов уже написаны и их сильно менять не желательно -сегодня я просто добавил "обертку" на существующие методы, чтобы унифицировать интерфейс.
Цитата:

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

как бы я хотел, чтобы это выглядело в коде :
Код:

class CAlg1; // has methods m11(), m12(), m13()
class CAlg2; // has methods m11(), m12(), m13()
// all method have same declaration int mXX( int )
...
CAlg1 a1(1,2,3); // initialize class
CAlg2 a2(3,4);  // initialize class

Caller interp(a1, a2); // initialize Caller class with necessary algoritihms instances

// Set mapping from name to method
interp.Map("m11", &CAlg1::m11);
interp.Map("m12", &CAlg1::m12);
interp.Map("m13", &CAlg1::m13);
interp.Map("m21", &CAlg2::m21);
interp.Map("m22", &CAlg2::m22);
...
interp.Call("m11", 0);  // call specific algorithm
interp.Call("m21", 10);  // call specific algorithm
while ( /* read method and params from file */  )
    interp.Call(method, params);


PSyton 02.05.2007 12:51

Так и думал что это интерпретатор. :-)
Доберусь домой, попробую накропать маленький приерчик.

EvroStandart 02.05.2007 14:46

Цитата:

Сообщение от crawler (Сообщение 1401448)
но как сделать для 2 классов одновременно - не понимаю

http://www.codesampler.com/miscsrc.htm
Тут я нашёл простой пример фабрики с коментами.
Гугл рулит. :)

crawler 02.05.2007 16:01

EvroStandart, Спасибо, но сюда фабрика не подходит. Фабрика дает выбрать какой именно об'ект инициализировать. В моем случае я должен держать инстансы обеих классов, и вызывать подходящий метод из какого-то одного. Хотя на той же странице я нашел "Chain of Responsibility Design Pattern" - это судя по всему то что мне нужно. Сча буду разбираться.

PSyton 02.05.2007 17:29

Естественно это не скомпилируется сразу - это только первые наметки, еслои так можно сказать. Суть данного метода в том что можно сделать как раз обертку на уже готовый функционал. С помощью шаблонов все это можно расширить таким образом чтобы при наследовании от уже написанного ранее класса алгоритма (не по данной схеме) его конструктор вызывался автоматом, после чего можно получить более красивые вещи, например, для регистрации алгоритмов использовать более хитрые шаблоны, более корректно проверяющие что им подают в качестве параметров шаблона...
Еще раз повторяю что все что приведено ниже это просто наметки и намеки, на то как можно сделать.
Код:

class ArgumentsList
{
private:
        //...
public:
        ArgumentsList() {}
        ArgumentsList(const ArgumentsList& other)
        {
                *this = other;
        }
        ArgumentsList &operator=(const ArgumentsList& other)
        {
                if (this != &other)
                {
                        //Копируем все что надо
                }
                return *this;
        }
        ~ArgumentsList() {};
};

class Results
{
public:
        Results();
        //Вся реализация конструкторы копирования и т.п.
        ~Results();
};

// Базовый клас алгоритма.
class BaseAlgoritm
{
private:
        std::string m_invokedMethod;
protected:
        void dummyTest();
public:
        IBaseAlgoritm(const std::string& methodName)
                : m_invokedMethod(methodName)
        {
        }
        virtual ~IBaseAlgoritm()
        (
        )
        virtual const std::string& invikedMethod() const
        {
                return m_invokedMethod;
        }
        virtual bool canProccess(const std::string& methodName)
        {
                return (methodName == m_invokedMethod);
        }
        virtual Results proccess(const ArgumentsList& args) = 0;
};


class Algoritm
        : public BaseAlgoritm
{
public:
        Algoritm(const std::string& methodName)
                : BaseAlgoritm(methodName)
        {
                dummyTest();
        }
        virtual ~Algoritm()
        (
        )
};


//Предположим что есть класссы A1 и B1 которые что-то там делают... Нам нужно на их основе написать свои алгоритмы...

//Делаем наследника от A1

class AA1
        : public A1
        , public Algoritm
{
        AA1(int a, int b, int c)
                : A1(a, b, c)
                , Algoritm("m1")
        {}
        virtual ~AA1() {}
        //Реализуем функцию которая будет выполнять действия...
        virtual Results proccess(const ArgumentsList& args)
        {
                retrun A1::m1(args);
        }
};

//Аналогично для B1

class BB1
        : public B1
        , public Algoritm
{
        BB1(int a, int b)
                : B1(a, b)
                , Algoritm("m2")
        {}
        virtual ~BB1() {}
        //Реализуем функцию которая будет выполнять действия...
        virtual Results proccess(const ArgumentsList& args)
        {
                retrun B1::m2(args);
        }
};

// так далее. Суть в том что каждый наследник класса-примеси Algoritm обязан реализовать proccess, таким образом имеем общий интерфейс для все алгоритмов реализованных подобным образом.
// По идее можно не использовать примесь в виде уже реализованного класса, а просто делать наследника от Algoritm и реализовывать его соотв образом.
// Интерпретатор примет рпимерно такой вид:

class Interpret
{
private:
        typedef std::list<BaseAlgoritm *> AList;
        AList m_algoritms;
public:
        Interpret() {}
        virtual ~Interpret()
          {
                while (m_algoritms.size() > 0)
                {
                        delete *(m_algoritms.begin());
                                  m_algoritms.erase(m_algoritms.begin());
                }
          }
        Results invore(const std::string &method, const ArgumentsList& args)
        {
                AList::const_iterator i = m_algoritms.begin();
                AList::const_iterator ei = m_algoritms.end();
                for (; i != ei; ++i)
                {
                        if ((*i)->canProccess(method))
                                return (*i)->proccess(args)
                }
                return Results();
        }
        bool contains(const std::string &method)
        {
                AList::const_iterator i = m_algoritms.begin();
                AList::const_iterator ei = m_algoritms.end();
                for (; i != ei; ++i)
                {
                        if ((*i)->canProccess(method))
                                return true;
                }
                return false;
        }
        template <class AT>
        void register(int a, int b, int c)
        {
                AT* newalg = new AT(a, b, c);
                if (!contains(newalg->invikedMethod()))
                        m_algoritms.push_back(newalg);
                      else
                                delete newalg;
        }
        template <class AT>
        void register(int a, int b)
        {
                AT* newalg = new AT(a, b);
                if (!contains(newalg->invikedMethod()))
                        m_algoritms.push_back(newalg);
        }
};


int _tmain(int argc, _TCHAR* argv[])
{
        Interpret tmp;
        tmp.register<AA1>(10,5.3);
        tmp.register<BB1>(5,22);
        ArgumentsList lst1;
        ArgumentsList lst2;
        Results rr = tmp.invore("m1", lst1);
        Results rr1 = tmp.invore("m2", lst2);
}


Flexz 03.05.2007 11:23

посмотри boost::function

конкретно вот это:
http://boost.org/doc/html/function/t...html#id2688553

PS сильно не вникал в суть дискуссии, так что может быть мимо :rolleyes:

crawler 04.05.2007 18:20

PSyton, попытался расширить с помощь шаблонов - умудрился вышибить компайлер с ексепшеном :eek: послезавтра продолжу эксперимент - сделаю без расширения :)

Flexz, не это то что надо. Я тоже наткнулся на несколько решений - но чтобы их исползовать надо понять как - а весь boost написан так, что мне проще написать самому, чем разобраться как его пользовать.

--------------------------------
Сделал. В интерпретаторе хранятся пойнтеры на базисный класс, темплейтом сделал регистрацию с разным количеством входных переменных, и промежуточный класс тоже темплейтный. Вызывается виртуальные метод "Call" базисного класса, и из-за инициации наследников подходящими методами все работает.
Таким образом вызов выглядит так:
Код:

C1 c1;
C2 c2;

Interpreter i;

i.Register<C1> ( &c1,
    "m11",&C1::m1,
    "m12",&C1::m2,
    NULL );
i.Register<C2> ( &c2,
    "m21",&C2::m1,
    "m22",&C2::m2,
    "m23",&C2::m4,
    NULL );

i.Call("m23",12);

Огромное спасибо всем участникам дискуссии.


Часовой пояс GMT +4, время: 22:18.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.