Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 04.01.2011, 18:47   #1
Gongled
Пользователь
 
Регистрация: 17.02.2009
Сообщений: 78
Вопрос Вектор из callback'ов методов класса

С++ изучаю недавно, могут быть проблемы с пониманием.

Существует произвольный class_A, имеющий несколько методов, различные по своему алгоритму, но имеющие всего один входной параметр произвольного типа данных. Необходимо создать класс Callback_Manager, основными методами которого являются set (занести коллбэк в вектор) и get (вызвать каждый из коллбэков вектора).

На данном этапе готов функтор и заполнение вектора из main. (компилируется и работает)
Код:
using namespace std;

#include <list>
#include <iostream>
#include <vector>

template < class classType, typename returnType, typename parameterType >

// функтор
class Callback
{
    typedef returnType (classType::*methodType)(parameterType *);

    private:

        classType * class_instance;

        methodType method;

        parameterType * parameter;

    public:

        Callback (classType * class_instance, methodType method, parameterType * parameter)
        {
            this->class_instance = class_instance;
            this->method = method;
            this->parameter = parameter;
        }

        returnType operator () ()
        {
            return (class_instance->*method)(parameter);
        }
};

// произвольный класс
class class_A
{
    public:

        class_A () {};

        ~class_A () {};

        void method_A (int * data)
        {
            cout << "result: " << (*data) << endl;
        }
};

int main()
{
    // тип коллбэка
    typedef Callback < class_A, void, int > callbackType;

    // вектор коллбэков
    typedef std::vector < callbackType > callbackVectorType;

    // данные
    int * data = new int (42);

    // вектор коллбэков
    callbackVectorType * callback_list = new callbackVectorType ();

    // произвольные экземпляры класса
    class_A * A = new class_A ();

    // добавление коллбэка в вектор для вызова с параметром
    callback_list->push_back(callbackType(A, &class_A::method_A, data));

    // вызов коллбэков
    for (callbackVectorType::iterator it = callback_list->begin(); it != callback_list->end(); (*it)(), it++);

    // освобождаем память
    delete A, data, callback_list;

    return 0;
}
Требуется сделать нечто похожее на это (не компилируется):
Код:
// ...

// произвольный класс
class class_A
{
    public:

        class_A () {};

        ~class_A () {};

        void method_A (int * data)
        {
            cout << "int: " << (*data) << endl;
        }

        void method_B (string * str)
        {
            cout << "string: " << str->data() << endl;
        }
};

template < class classType, typename optionType >

class Callback_Manager
{
    // тип коллбэка
    typedef Callback < classType, void, optionType > callbackType;
    // вектор коллбэков
    typedef std::vector < callbackType > callbackVectorType;

    private:

        callbackVectorType * callback_list;

    public:

        Callback_Manager ()
        {
            // новый список коллбэков
            callback_list = new callbackVectorType ();
        }

        ~Callback_Manager ()
        {
            // ...
            // освобождение памяти
            delete callback_list;
        }

        // добавление
        template < typename setType >
        void set (void * method, setType option)
        {
            // создаём экземпляр класса
            classType * class_instance = new classType ();
            // добавляем коллбэк в вектор
            callback_list->push_back(callbackType(class_instance, method, option));
        }

        // вызов
        void get ()
        {
            // вызов каждого из коллбэков
            for (callbackVectorType::iterator it = callback_list->begin(); it != callback_list->end(); (*it)(), it++);
        }
};

int main ()
{
    //
    Callback_Manager < class_A, void > * cm = new Callback_Manager ();

    // занести коллбэк (метод method_A из класса class_A) с параметром типа int в вектор
    cm->set < int > (&class_A::method_A, 42);

    // занести коллбэк (метод method_B из класса class_A) с параметром типа string в вектор
    cm->set < std::string > (&class_A::method_B, "hello world");

    // вызвать все коллбэки
    cm->get ();

    // освободить память
    delete cm;

    return 0;
}
Каким образом это можно реализовать без сторонних средств? В какую сторону копать? Если не трудно, представьте небольшой алгоритм. Спасибо. Компилятор GCC 4.4.5.
Пишу глупости.
Gongled вне форума Ответить с цитированием
Старый 05.01.2011, 05:15   #2
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2Gongled
во-первых, хочу поблагодарить за такой ШИКАРНЕЙШИЙ вопрос. просто мега респект, чел. мне очень понравилось) от души вот

во-вторых, ты реально жжошь, если въехал во все, что тут написал. я плюсы изучаю 3 года, и то, только подошел к таким сложным вещам. что показывает мою тупость...

ну и в третьих мои замечания
1. код плохо форматирован
2. слишком много объявлений синонимов, тяжело читается
3. немного не правильная идея реализации. зачем менеджеру коллбеков знать о параметрах, возвращаемых значениях и тип коллбеков? таким образом, ты привязал свой манагер к одному типу коллбеков, что не пять.
4. в векторе хранишь значения коллбеков. а нада указатели. зачем лишние копирования

может быть еще что, но я особо не вчитывался
моя реализация.

Код:
#include <iostream>
#include <list>
#include <vector>
#include <string>

using namespace std;

//--------------------------------------------------------------
class A {
public:
	void foo(int i)
	{
		cout << i << endl;
	}

	string bar(string str)
	{
		cout << str << endl;
		return str;
	}
};

//--------------------------------------------------------------
class CallbackBase {
public:
	virtual ~CallbackBase() {};
	virtual void call() = 0;
	
	void operator()()
	{
		return call();
	}
};

//--------------------------------------------------------------
template <class T, typename ret, typename param>
class Callback : public CallbackBase {
	typedef ret (T::*method)(param);

public:
	Callback(T *t, method m, param p)
		: 	m_Instance(t),
			m_Method(m),
			m_Param(p)
	{

	}

	void call()
	{
		(m_Instance->*m_Method)(m_Param);
	}

private:
	T *m_Instance;
	method m_Method;
	param m_Param;
};

//--------------------------------------------------------------
template <class T, typename ret, typename param>
CallbackBase *createCallback(T *t, ret(T::*m)(param), param p)
{
	return new Callback<T, ret, param>(t, m, p);
}

//--------------------------------------------------------------
class CallbackManager {
public:
	void set(CallbackBase *callback)
	{
		m_CallbackList.push_back(callback);
	}

	void get()
	{
		for (vector<CallbackBase *>::const_iterator It = m_CallbackList.begin(); It != m_CallbackList.end(); ++It)
			(**It)();
	}

private:
	vector<CallbackBase *> m_CallbackList;
};

//--------------------------------------------------------------
int main()
{
	A a;
	CallbackManager manager;

	manager.set(createCallback(&a, &A::foo, 111));
	manager.set(createCallback(&a, &A::foo, 222));
	manager.set(createCallback(&a, &A::foo, 333));
	manager.set(createCallback(&a, &A::foo, 444));

	manager.set(createCallback(&a, &A::bar, string("str1")));
	manager.set(createCallback(&a, &A::bar, string("str2")));
	manager.set(createCallback(&a, &A::bar, string("str3")));
	manager.set(createCallback(&a, &A::bar, string("str4")));

	manager.get();

	return 0;
}
сразу смотри на функцию main, как просто использовать твои коллбеки
недоделки в моем коде:
1. полиморфные объекты-коллбеки, хранящиеся в векторе, не уничтожаются. нужен smart_ptr, либо иначе освободить память. мне было влом
2. operator() у меня не возвращает тип твоего коллбека, а просто void. иначе яб не смог сделать callbackManager таким, какой он получился если это действительно так надо, нужно думать

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

вообще неплохо сесть и заново все обдумать, думаю, можно сделать еще красивее но если тебе не критично это возвращаемое значение у operator(), то тебе должен понравиться мой код)
удачи
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Последний раз редактировалось pproger; 05.01.2011 в 14:33.
pproger вне форума Ответить с цитированием
Старый 05.01.2011, 11:40   #3
still_alive
Great Code Monkey
Форумчанин
 
Аватар для still_alive
 
Регистрация: 09.08.2007
Сообщений: 533
По умолчанию

Цитата:
Сообщение от Gongled
имеющие всего один входной параметр произвольного типа данных.
Интересуют модификаторы этого параметра. Любые?

Цитата:
Сообщение от pproger
operator() у меня не возвращает тип твоего коллбека, а просто void
Мне кажется, что можно без этого обойтись. Иначе возникают траблы с полиморфной реализацией.

Код:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

class A {
public:
   int     foo(int i)        { return i; }
   string bar(string str) { return str; }
};

class CallbackBase {
public:
   virtual ~CallbackBase() { }
   virtual void call() = 0;
   
   void operator()() { call(); }
};

template <typename T, typename RetType, typename ParamType>
class Callback: public CallbackBase {
public:
   typedef RetType (T::*MethodType)(ParamType);

   Callback(T *t, RetType *r, MethodType m, ParamType p)
      : instance_(t), retval_(r), method_(m), param_(p) { }

   void call() {
      retval_ ? *retval_ = (instance_->*method_)(param_)
                : (instance_->*method_)(param_);
   }

protected:
   Callback(const Callback &);
   Callback& operator=(const Callback &);

   T               *instance_;
   RetType      *retval_;
   MethodType method_;
   ParamType   param_;
};

template <typename T, typename RetType, typename ParamType>
CallbackBase *createCallback(T *t, RetType(T::*m)(ParamType), ParamType p, RetType *r=0) {
   return new Callback<T, RetType, ParamType>(t, r, m, p);
}

struct SimpleCall {
    void operator()(CallbackBase *fn) const { (*fn)(); }
};

class CallbackManager {
public:
   void set(CallbackBase *callback) {
      callbacks_.push_back(callback);
   }

   void get() const {
      for_each(callbacks_.begin(), callbacks_.end(), SimpleCall());
   }

private:
   vector<CallbackBase*> callbacks_;
};

int main()
{
   A a;
   CallbackManager manager;
   int b;
   string c;

   manager.set(createCallback(&a, &A::foo, 111, &b));
   manager.set(createCallback(&a, &A::foo, 222));
   manager.set(createCallback(&a, &A::foo, 333));
   manager.set(createCallback(&a, &A::foo, 444));

   manager.set(createCallback(&a, &A::bar, string("str1"), &c));
   manager.set(createCallback(&a, &A::bar, string("str2")));
   manager.set(createCallback(&a, &A::bar, string("str3")));
   manager.set(createCallback(&a, &A::bar, string("str4")));

   manager.get();
   cout << b << ' ' << c << endl;

   return 0;
}
Здесь тоже не освобождается память, ибо тоже влом. boost::shared_ptr спас бы, но раз надо без сторонних...
Кстати, лучше все-таки писать typename вместо class.

Последний раз редактировалось still_alive; 05.01.2011 в 11:46.
still_alive вне форума Ответить с цитированием
Старый 05.01.2011, 14:14   #4
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2still_alive
Цитата:
Кстати, лучше все-таки писать typename вместо class.
не, у автора typename используется для встроенных типов, class для user-type. хотя его typename могут быть и user-type. все по александреску вобщем

с const методами да, постоянно забываю а for_each не использовал из за необходимости еще одного функтора) кстате, не в курсе, можно ли в for_each третьим параметром как нить передать текущий итератор?) нам же нужно всего лишь вызвать operator(), который можно вызывать через наш итератор. какой нить класс current_iterator, я не знаю там

да, твоя реализация возвращаемых значений мне не понравилась) без обид нужно придумать поудобнее)
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Последний раз редактировалось pproger; 05.01.2011 в 14:38.
pproger вне форума Ответить с цитированием
Старый 05.01.2011, 14:49   #5
Gongled
Пользователь
 
Регистрация: 17.02.2009
Сообщений: 78
Стрелка Огромное спасибо

pproger, спасибо за алгоритм, именно то, что мне и нужно было. Говоря о нечитабельности кода, давно ищу литературу, в которой было бы подробно пояснено как правильно называть переменные, какими должны быть хорошие комментарии и какими принципами стоит руководствоваться при разработке приложений. У вас есть на примете такая?

still_alive, да, модификаторы произвольные. Благодарствую за отклик, вы мне очень помогли. Для контролем за памятью рекомендуете воспользоваться boost::shared_prt, а можно для этой цели использовать умный указатель из std::auto_ptr?
Пишу глупости.
Gongled вне форума Ответить с цитированием
Старый 05.01.2011, 15:22   #6
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2Gongled
не знаю, complete code попробуй Макконнелла
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance
pproger вне форума Ответить с цитированием
Старый 05.01.2011, 16:05   #7
still_alive
Great Code Monkey
Форумчанин
 
Аватар для still_alive
 
Регистрация: 09.08.2007
Сообщений: 533
По умолчанию

Цитата:
Сообщение от pproger Посмотреть сообщение
не, у автора typename используется для встроенных типов, class для user-type. хотя его typename могут быть и user-type. все по александреску вобщем
В общем class - там, где может быть только класс, typename - во всех других местах.

Цитата:
Сообщение от pproger Посмотреть сообщение
а for_each не использовал из за необходимости еще одного функтора) кстате, не в курсе, можно ли в for_each третьим параметром как нить передать текущий итератор?) нам же нужно всего лишь вызвать operator(), который можно вызывать через наш итератор. какой нить класс current_iterator, я не знаю там
Там необязателен именно свой функтор) Его можно вообще убрать, заадаптировав функцию-член.
Код:
for_each(callbacks_.begin(), callbacks_.end(), mem_fun(&CallbackBase::operator()));
Цитата:
Сообщение от pproger Посмотреть сообщение
да, твоя реализация возвращаемых значений мне не понравилась) без обид нужно придумать поудобнее)
А что с ней не так?)
Придумай) А то мне сейчас некогда думать, у меня другие проблемы, связанные с многопоточностью)

2Gongled
На счет стиля - лично я смешал стиль кьют со стилем гугла. Поищи qt google coding style.
auto_ptr и стандартные контейнеры плохо переносят друг друга. Очень не рекомендую.

Последний раз редактировалось still_alive; 05.01.2011 в 16:07.
still_alive вне форума Ответить с цитированием
Старый 05.01.2011, 16:29   #8
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2still_alive
Цитата:
for_each(callbacks_.begin(), callbacks_.end(), mem_fun(&CallbackBase:perator())) ;
про mem_fun не знал)

думать я не особо люблю)

auto_ptr вообще нельзя хранить в контейнерах)
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance
pproger вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
вынос методов класса в дин библиотеки(dll) Пепел Феникса Общие вопросы C/C++ 8 07.12.2010 20:55
массив методов класса DartDayring Общие вопросы C/C++ 0 07.12.2010 20:00
вектор как закрытый член класса, как изменять его значения? Zhigool' Общие вопросы C/C++ 3 08.08.2010 23:19
Создание класса "Трехмерный вектор" steel_may Помощь студентам 1 09.05.2009 12:29
Использование методов вне класса prikolist Общие вопросы C/C++ 12 07.05.2009 13:57