Очень часто возникает ситуация, при которой объект, объявленный раньше, должен вызывать метод объекта, объявленного позже. Но поменять объявления местами не представляется возможным, ибо "нижестоящий" по коду ссылается на "вышестоящего".
Пример:
class foo |
_Winnie C++ Colorizer |
При попытки скомпилировать это, компилятор пошлёт к такой-то матери. Ну и понятно, ведь на момент компиляции foo, нет никакой информации о bar. Это же не позволяет использовать обычные указатели на методы класса.
Можно попытаться выкрутиться, используя интерфейсы:
struct i_bar |
_Winnie C++ Colorizer |
Данный код скомпилиться и будет работать. Но этот подход требует создание "костылей", что не радует глаз. К тому же нарушается иерархия. foo приходиться знать о bar, хотя знать о нём он не обязан. Всё что нужно - это вызов некой "абстрактной" функции/метода. А чья она - это уже не важно.
Можно "разнести" объявление и реализацию классов:
// bar.h |
_Winnie C++ Colorizer |
Но раскидывать код по файлам не всегда бывает удобно. К тому же снова идёт нарушении иерархии, как в предидущем пункте. Но как бы то ни было, этот метод очень распространён. И я пользовался им до недавнего времени.
Пока не начала курить boost =)
typedef boost::function< void( const char * ) > boost_func; |
_Winnie C++ Colorizer |
Вуаля! boost::bind + boost::function решает все проблемы. Раскидывать ничего не надо. К томуже foo не приходиться ничего знает о bar. Одни плюсы =)
Вот только я ещё не до конца понял, как это штука работает. Но работает - факт.
10 комментариев:
1.
template <class T>
class foo
{
public:
void test (T * t) { t->print("zx"); }
};
2. typedef void (*TFunc)(char * zxc);
class foo { public: void test(TFunc func) { func("fsdfsd"); } };
ну вот как то так.
"template <class T>
class foo
{
public:
void test (T * t) { t->print("zx"); }
};"
А если у нас есть 2 разных класса, методы которых должен вызывать foo?
Создавать для каждого свой объект?
т.е.
class bar_1 {...};
class bar_2 {...};
foo< bar_1 > foo_1;
foo< bar_2 > foo_2;
А если foo нужен только один? Нестыковочко.
----------------------
Тут у тебя просто указатель на функцию, а не на метод. Тогда уж должно быть:
typedef void (bar::*TFunc)( char * zxc );
Но как я уже писал выше - не скомпилицо.
Ага, про это все я подумал когда написал коммент.
Примеры надо корректнее делать =)
Никаких интерфейсов и выкрутасов не надо. forward declaration и отделение описания класса от объявления. объявлять зависимые функции можно в том же хидере, где и описание класса. просто объявить их встраивыми, что бы линкёр не ругался.
А boost.function очень мощный инструмент, но применение его здесь ведёт к обобщению типа входных параметров, что не всегда приемлимо.
к тому же если вызывается больше одной функции объекта bar*, то надо на каждую функцию отдельно передавать по отдельному объекту boost.function.
ну ещё не стоит забывать, что boost.function занимает на стеке 32 байта + динамически выделяет память, если нужно больше. а указатель на объект 4-8 байт.
forward declaration и отделение описания класса от объявления
Ну про это я во 2-ом варианте описывал. Но иногда бывает что весь класс с реализацией может занять 50 строк. Заводить для этого отдельный .cpp? Но раньше так и делал, да.
---
ну ещё не стоит забывать, что boost.function занимает на стеке 32 байта + динамически выделяет память, если нужно больше. а указатель на объект 4-8 байт.
Ну про скорость я умышленно ничего не написал =) К тому же такие "сложные" вызовы использую только при загрузке. Так что не критично.
з.ы. Как грустно без развлетвленных комментариев =(
можно в одном файле совместить.
// file - foo.h
class foo
{
void test(bar* bar);
};
#include "bar.h"
inline void foo::test(bar* bar)
{
...
}
немного правда придётся чуток заморочиться с гвардами включения, но это просто.
зы: говорят, что wordpress всё таки поддерживает древовидные комментарии. надо бы посмотреть как их включать
Чмоки всем в чате! ^_^
У меня была как-то такая трабла, когда надо было написать IDL для весьма накрученного и сложносочиненного куска проекта.
Там были ссылки всех видов, вверх, вниз, перекрестом, даром что не по графу.
В итоге средствами IDL это реализовать не получилось, из-за случаев, когда 1 ссылается на 2, а 2 на 1 :) + ко всему, он еще и не умел разделять функции с одним именем и разными конструкторами.
cyberzx
к тому же если вызывается больше одной функции объекта bar*, то надо на каждую функцию отдельно передавать по отдельному объекту boost.function
если надо вызывать у объекта bar* больше одной функции, то вызывающий либо знает о bar и тогда стоит хранить указатель на bar и явно вызывать нужные функции, либо действительно передавать несколько объектов boost::function, что полностью позволяет отвязаться от знания о "внешнем мире".
timai
я тут думал над примером с фабрикой. да, c boost::function получается гибче, без честных виртуальных функций. очень круто получается.
Только обрадовался в ЖЖ, как ты убил и разочаровал...
Подобное поведение легко сделать и на указателях на метод, boost.function - обыкновенный функциональный объект, касательно его реализации - Александреску, пятая глава.
Boost.Bind - банальное замыкание.
Издержки типа вызова по указателю все равно не избежать.
Потому методы типа интерфеёсов, разнесения реализации и декларации, функциональных объектов Boost.Function либо Loki::Function, сигналов слотов по типу Qtшных либо Boost.Signals - одного поля ягоды, увы.
Прямое решение проблемы - возможность писать такой код не создавая дополнительный уровень косвенности (а как следствие издержки от него) - без модификации языка невозможно.
А об убогости хидеров я уже говорил http://zabivator.livejournal.com/204657.html
zabivator
Подобное поведение легко сделать и на указателях на метод
Нельзя написать void (foo:*pfunc)() если компилятор ещё не знает о foo. Или под "указателем на метод" подразумелось что-то иное?
Потому методы типа интерфеёсов, разнесения реализации и декларации, функциональных объектов Boost.Function либо Loki::Function, сигналов слотов по типу Qtшных либо Boost.Signals - одного поля ягоды, увы.
Ну по большому счёту это просто буквы. Просто они складыватьсыя могут по разному, ага. Ну да, все эти вещи для одной цели. Вопрос в удобстве использования
Отправить комментарий