Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Список указателей на функции класса  (Прочитано 23558 раз)
0 Пользователей и 1 Гость смотрят эту тему.
mikeparadox
Гость
« : 10-09-2008 11:45 » 

Доброго времени суток.
По неопытности столкнулся с такой проблемкой:
Нужно сделать, чтобы по ходу выполнения программы указатели на определённые функции складывались в так называемый стек,
по типу первым зашёл – последним вышел.
Затем в определённом месте программы нужно вызвать по очереди все функции из стека.

Я нашел готовый РАБОЧИЙ пример, в котором функции складывающиеся в стек не являются методами класса. Если я объединяю их в класс, и пытаюсь положить в стек, то выдает ошибку:
'WORK::Func1': function call missing argument list; use '&WORK::Func1' to create a pointer to member

Более того, весь этот код находится в DLL.
Как это решить. Если можно, то с примером. Спасибо.

Код:

#define MB(s) MessageBox(NULL, s, s, MB_OK);

#include <windows.h>


class cProcessManager
{
    // Структура для хранения указателей на функции
    // в виде связанного списка
    typedef struct sProcess {
        void (*Function)();
        sProcess *Next;
    } sProcess;

    protected:
        sProcess *m_ProcessParent; // Верхнее состояние в стеке
                                   // (голова стека)
    public:
        cProcessManager() { m_ProcessParent = NULL; }

        ~cProcessManager()
        {
            sProcess *ProcessPtr;

            // Удаляем все процессы из стека
            while((ProcessPtr = m_ProcessParent) != NULL) {
                m_ProcessParent = ProcessPtr->Next;
                delete ProcessPtr;
            }
        }

        // Добавляем функцию в стек
        void Add(void (*Process)())
        {
           
            if(Process != NULL) {
                // Создаем новый процесс
                // и помещаем его в стек
                sProcess *ProcessPtr = new sProcess;
                ProcessPtr->Next = m_ProcessParent;
                m_ProcessParent = ProcessPtr;
                ProcessPtr->Function = Process;
            }
        }

        // Выполняем все функции
        void Process()
        {
            sProcess *ProcessPtr = m_ProcessParent;
            while(ProcessPtr != NULL) {
                ProcessPtr->Function();
                ProcessPtr = ProcessPtr->Next;
            }
        }
};

class WORK
{
public:
void Func1() { MB("1"); }
void Func2() { MB("2"); }
void Func3() { MB("3"); }
};


int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, \
                   LPSTR szCmdLine, int nCmdShow)
{
cProcessManager PM;
WORK JOB;

    PM.Add(JOB.Func1);


    PM.Process();
}

Записан
McZim
Команда клуба

ru
Offline Offline
Пол: Мужской
Я странный


WWW
« Ответ #1 : 10-09-2008 12:18 » 

WORK JOB;

замени

WORK* JOB = new WORK();
Записан

The CBO without stats is like a morning without coffee. (c) T.Kyte.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 10-09-2008 12:22 » 

по моему не будет разницы.
Нет возможности щас разбираться, но тип WORK где то в типе указателя на функцию надо указать
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 10-09-2008 12:26 » 

Цитата: mikeparadox
Как это решить. Если можно, то с примером.
Шаблон (pattern) проектирования "Команда" (Command). Книжка "Design Patterns" Гамма, Хелм, Джонсон, Влиссидес. Можно взять тут https://club.shelek.ru/download.php?id=138 , но здесь на английском почему-то, хотя, помнится, была и на русском.

В результате получится стек объектов, которые умеют выполнять нужные функции.
« Последнее редактирование: 10-09-2008 12:31 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
McZim
Команда клуба

ru
Offline Offline
Пол: Мужской
Я странный


WWW
« Ответ #4 : 10-09-2008 12:30 » 

Алексей1153++, просто объекты нужно по человечески размещать в памяти и работать через указатели с функциями-членами.
Записан

The CBO without stats is like a morning without coffee. (c) T.Kyte.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #5 : 10-09-2008 12:41 » 

McZim, да там не в этом дело ) Это дело второе - где создать, у него компилятор ругается
Записан

McZim
Команда клуба

ru
Offline Offline
Пол: Мужской
Я странный


WWW
« Ответ #6 : 10-09-2008 12:59 » 

ну и чего не понятного в: missing argument list
Записан

The CBO without stats is like a morning without coffee. (c) T.Kyte.
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #7 : 10-09-2008 13:07 » 

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

Код:
DWORD GetFunc1Addr() { return (DWORD)(void*)Func1; }

И результат добавляй в стек.

зы можно даже без ф-ций, только у тебя наверное геммор получится в Add


void *p = (void*)(w.Func1); // пример

PM.Add((void*)(JOB.Func1)); // здесь она у тебя ожидает вобще-то что? Поэтому сделай лучше DWORD. А при вызове делаешь кастинг в тип ф-ции. Тебе его кстати надо будет ещё определить.
« Последнее редактирование: 10-09-2008 13:12 от Джон » Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
mikeparadox
Гость
« Ответ #8 : 10-09-2008 13:17 » 

Всем огромное спасибо, особенно dimke за полезную ссылку и Джону закамень в мой огород(в хорошем смысле). Я конечно ещё тот извращенец, но не до такой степени, чтобы прописывать функцию возврата адреса для каждой из 170 функций. Как это сделать правильно?
Записан
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #9 : 10-09-2008 13:19 » 

Улыбаюсь плюс ещё к тому, что ты должен сделать все ф-ции статическими. Ага Я рад, что ты меня совершенно правильно понял.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 10-09-2008 13:33 » new

Лично меня не "заломало" бы написать 170 классов-команд для вызова нужных функций Улыбаюсь

Если функции однотипные (с одинаковой сигнатурой) - а судя по примеру, это так - можно вместо 170 классов написать 170 раз обращение к макросу, реализующему нужный класс с вызовом нужной функции, а параметром макроса передавать имена функций Улыбаюсь. А ещё лучше не макросом, а шаблоном (template).
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
mikeparadox
Гость
« Ответ #11 : 10-09-2008 16:18 » 

Цитата
А ещё лучше не макросом, а шаблоном (template).
А можно чуть подробнее пожалуйста. Скромно так...
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 10-09-2008 19:34 » 

Цитата: mikeparadox
А можно чуть подробнее пожалуйста.
Элементарно

Код: (C++)
#include <iostream>
#include <stack>

using namespace std;

// Пространство имён, определяющее все абстрактные команды, умеющие вызывать
// функции-члены с определёнными сигнатурами произвольных классов.
namespace invoker
{
        // Интерфейс всех разновидностей команд, скрывающий их особенности.
        class IFunctionInvoker
        {
        public:
                // Метод вызова функции-члена передаваемого объекта указанного класса.
                virtual void invoke() = 0;
        };

        // Стек команд на базе стека стандартной библиотеки.
        typedef stack<IFunctionInvoker *> FunctionInvokersStack;


        // Команды разной сложности, зависящие от типа функций-членов конкретного класса,
        // которые они умеют вызывать. Экземпляры этих классов будут помещаться в стек.
       
        template<class Class>
        class SimpleFunctionInvoker:
                public IFunctionInvoker
        {
        public:
                // Тип вызываемых командой функций-членов объекта класса.
                typedef void (Class::*Function)();
        private:
                // Указатель на вызываему функцию-член. Она не может быть определена как параметр шаблона,
                // поскольку её адрес неизвестен на этапе компиляции.
                Function function;
                // Объект, у которого вызывается функция-член.
                Class &instance;
        public:
                // Конструктор команды, сохраняющий указатель на вызываемую функцию-член.
                SimpleFunctionInvoker(const Function &function, Class &instance):
                        function(function),
                        instance(instance)
                {}
                // Реализация IFunctionInvoker.
                virtual void invoke()
                {
                        (this->instance.*(this->function))();
                }
        };

        template<class Class>
        class DifficultFunctionInvoker:
                public IFunctionInvoker
        {
        public:
                typedef int (Class::*Function)(int x);
        private:
                Function function;
                Class &instance;
                int argument;
                int result;
        public:
                DifficultFunctionInvoker(const Function &function, Class &instance, const int argument):
                        function(function),
                        instance(instance),
                        argument(argument),
                        result(0)
                {}
                virtual void invoke()
                {
                        this->result = (this->instance.*(this->function))(this->argument);
                }
                int getResult() const
                {
                        return this->result;
                }
        };
}

namespace api
{
        // Класс, содержащий вызываемые функции-члены с известными и описанными в командах сигнатурами.
        class MyAPI
        {
        public:
                // Первая функция.
                void function1()
                {
                        cout << "function1" << endl;
                }
                // Вторая функция.
                void function2()
                {
                        cout << "function2" << endl;
                }
                // Третья функция.
                int function3(int x)
                {
                        int y = x * 2;
                        cout << "function3 with argument " << x << " has result " << y << endl;
                        return y;
                }
        };

        typedef invoker::SimpleFunctionInvoker<MyAPI> MySimpleInvoker;
        typedef invoker::DifficultFunctionInvoker<MyAPI> MyDifficultInvoker;
}

// Основная программа.
int main() {
        using namespace api;

        // Экземпляр объекта с API.
        MyAPI myAPI;

        // Экземпляры команд для разных функций-членов объекта с API.
        invoker::IFunctionInvoker *function1 = &(MySimpleInvoker(&MyAPI::function1, myAPI));
        invoker::IFunctionInvoker *function2 = &(MySimpleInvoker(&MyAPI::function2, myAPI));
        invoker::IFunctionInvoker *function3 = &(MyDifficultInvoker(&MyAPI::function3, myAPI, 3));

        // Стек вызываемых фунцкий.
        invoker::FunctionInvokersStack stackInstance;

        // Как-ннибудь заполняем стек.
        stackInstance.push(function1);
        stackInstance.push(function2);
        stackInstance.push(function3);
        stackInstance.push(function2);

        // Работаем со стеком.
        while(!stackInstance.empty())
        {
                stackInstance.top()->invoke();
                stackInstance.pop();
        }

        // Извлекаем результаты из "сложных" команд.
        cout << "Result of function3 is " <<
                dynamic_cast<MyDifficultInvoker *>(function3)->getResult() << endl;

        getchar();
}

Код: (Text) Вывод программы
function2
function3 with argument 3 has result 6
function2
function1
Result of function3 is 6

И не надо функции делать статическими.
« Последнее редактирование: 10-09-2008 19:41 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
mikeparadox
Гость
« Ответ #13 : 10-09-2008 20:33 » 

Улыбаюсь) ОГРОМНОЕ ВСЕМ СПАСИБО!!! ОЧЕНЬ ПОМОГЛО
Записан
Malaja
Команда клуба

de
Offline Offline
Пол: Женский

« Ответ #14 : 11-09-2008 12:49 » 

dimka,

спасибо за инфу и за науку!  Улыбаюсь
Сначала конструкция
Код:
(this->instance.*(this->function))(); 
вызвала у меня некоторое недоумение Ага Т.е. я бы в этом месте this->instance не написала бы, т.к. исходила бы из того, что в
Код:
typedef void (Class::*Function)();
все уже сказано.
И долго бы пыталась потом понять, почему компилятор меня посылает лесом в дальние края  Отлично
Записан

холоднокровней, Маня, Ви не на работе
---------------------------------------
четкое определение сущности бытия:
- А мы в прошлом или в будущем?- спросила Алиса.
- Мы в жопе, - ответил кролик.
- А "жопа" - это настоящее? - спросила Алиса.
- А "жопа" - это у нас символ вечности.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 11-09-2008 18:23 » 

Malaja, ну для статических функций-членов неуказание instance будет нужным Улыбаюсь
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #16 : 12-09-2008 07:03 » 

dimka, от молодчина Улыбаюсь, хотя конечно для boost::function удобней Улыбаюсь

Код:
class MyClass2;
class MyClass
{
public:
MyClass(){}

void Method1(int);
void Method2(int, MyClass2&);
void Method3(int p1, int p2, int p3, int p4)
{
std::cout << "p1=" << p1 << ";"
<< "p2=" << p2 << ";"
<< "p3=" << p3 << ";"
<< "p4=" << p4 << std::endl;
}
};

{
typedef std::vector<boost::function<void ()> > StackT;
StackT stack;
stack.push_back(boost::bind(&MyClass::Method1,&myClass, 1) ); // По значению
stack.push_back(boost::bind(&MyClass::Method2,&myClass, 1, boost::ref(myClass2)) ); // По значению и ссылке

for (StackT::iterator it = stack.begin(); it != stack.end(); ++it)
*it(); // вызываем всё это в цикле
}

{
typedef std::vector<boost::function<void (int, int)> > StackT;
StackT stack;

// Биндим или баиндим(кому как нравится) метод класса с четырьмя параметрами причем
// данные в параметр 2 и 4 мы будем передавать позже
stack.push_back(boost::bind(&MyClass::Method3,&myClass, 1, _1, 3, _2) );
// тоже самое, но поменяли местами параметры
stack.push_back(boost::bind(&MyClass::Method3,&myClass, 1, _2, 3, _1) );
// попробуем вызвать, то что набаиндили
stack[0](20,400); // резултат p1=1;p2=20;p3=3;p4=400
stack[0](20,400); // резултат p1=1;p2=400;p3=3;p4=20

}
Записан

Странно всё это....
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 12-09-2008 07:21 » 

а я бы поюзал просто кучку классов с виртуальной функцией , которые (классы) произведены от одного родителя. В общем то, по сути, будет то же самое, что Димка показал, только более наглядно ) Правда реализовывать не приходилось такое, возможно минусы есть
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #18 : 12-09-2008 09:34 » 

Алексей1153++, ну то, что ты предложил это не совсем, то, что предложил Димка.

Димкина версия более гибкая, сам часто пользуюсь, только сам не писал, а использую любимый boost
Записан

Странно всё это....
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #19 : 12-09-2008 10:24 » 

Ещё Вад высказывал мысль про употребление хелперов стандартной библиотеки mem_fun, конструирующих функторы для алгоритмов обработки контейнеров, но чего-то эту мысль не развил.

Если все функции имеют одинаковую сигнатуру, то можно воспользоваться и mem_fun, чтобы не писать свои классы:
Код: (C++)
#include <functional>
#include <iostream>
#include <stack>

using namespace std;

class MyAPI
{
public:
                // Первая функция.
                void function1()
                {
                        cout << "function1" << endl;
                }
                // Вторая функция.
                void function2()
                {
                        cout << "function2" << endl;
                }
};

typedef mem_fun_t<void, MyAPI> MyFunctionInvoker;

MyFunctionInvoker function1 = mem_fun<void, MyAPI>(&MyAPI::function1);
MyFunctionInvoker function2 = mem_fun<void, MyAPI>(&MyAPI::function2);

typedef stack<MyFunctionInvoker *> FunctionInvokersStack;

int main()
{
        MyAPI myAPI;

        FunctionInvokersStack stackInstance;

        stackInstance.push(&function1);
        stackInstance.push(&function2);
        stackInstance.push(&function1);
        stackInstance.push(&function2);

        while(!stackInstance.empty())
        {
                (*stackInstance.top())(&myAPI);
                stackInstance.pop();
        }

        getchar();

        return 0;
}
Но в случае различных сигнатур получатся функторы разных типов, и потребуется писать дополнительный код сохранения результатов их работы и сведению их к чему-то общему, что можно засунуть в стек. Кроме того, особенностью функторов стандартной библиотеки является обязательная передача экземпляра в момент вызова функтора (поскольку по замыслу туда должен передаваться очередной объект из контейнера, а мы это используем иначе) - это тоже не всегда удобно.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines