- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
template<class TVisitedComponentList>
TUniqueIDOfVisitedComponentList getUniqueIDOfVisitedComponentTypeList(void)
{
static Type2Type<TVisitedComponentList> UniqueObjectForVisitedComponentTypeList;
return (size_t)(&UniqueObjectForVisitedComponentTypeList);
}
template<class TVisitedComponentList>
TCastWindowComponentTo* const castWindowComponentInternal(PegThing* const Window)
{
ASSERT(Window!=NULL);
if(Window==NULL)
return NULL;
STATIC_CHECK(!(boost::mpl::empty<TVisitedComponentList>::value), TVisitedComponentList_must_be_not_empty_and_be__boost_mpl_list_c__type);
const TUniqueIDOfVisitedComponentList UniqueIDOfVisitedComponentList=this->getUniqueIDOfVisitedComponentTypeList<TVisitedComponentList>();
const TCasterRepositorys::const_iterator NotFound=_casterRepositorys.end();
TCasterRepositorys::const_iterator findedCasterRepositoryForThisVisitedComponentList=_casterRepositorys.find(UniqueIDOfVisitedComponentList);
if(findedCasterRepositoryForThisVisitedComponentList==NotFound)
{
this->registerVisitedComponentList<TVisitedComponentList>();
findedCasterRepositoryForThisVisitedComponentList=_casterRepositorys.find(UniqueIDOfVisitedComponentList);
}
ASSERT(findedCasterRepositoryForThisVisitedComponentList!=NotFound);
const TCasterRepository::const_iterator NotFoundCaster=findedCasterRepositoryForThisVisitedComponentList->second->end();
const unsigned int ComponentType=const_cast<PegThing* const>(Window)->Type();
const TCasterRepository::const_iterator findedCaster=findedCasterRepositoryForThisVisitedComponentList->second->find(ComponentType);
if(findedCaster==NotFoundCaster)
return NULL;
ASSERT(findedCaster!=NotFoundCaster);
return (*(findedCaster->second))(Window);
}
template<class TCurrentItem, class TEnd>
void registerVisitedComponentListItem(TCasterRepository& casterRepository, TCurrentItem CurrentItem, TEnd End)
{
using namespace boost;
enum {WINDOW_COMPONENT_TYPE=mpl::deref<TCurrentItem>::type::value};
STATIC_CHECK((mpl::has_key<TWindowComponentsTypeIdToTypeMap, mpl::int_<WINDOW_COMPONENT_TYPE> >::value!=0), WINDOW_COMPONENT_TYPE_must_be_at_TWindowComponentsTypeIdToTypeMap);
typedef mpl::at<TWindowComponentsTypeIdToTypeMap, mpl::int_<WINDOW_COMPONENT_TYPE> >::type TRealTypeOfWindowComponent;
this->checkDuplicateComponentTypeID(WINDOW_COMPONENT_TYPE, casterRepository);
struct _
{
static TCastWindowComponentTo* const casterForEachWindowComponent(PegThing* const Window)
{
ASSERT(Window!=NULL);
if(Window==NULL)
return NULL;
TRealTypeOfWindowComponent* const RealTypeComponent = static_cast<TRealTypeOfWindowComponent* const>(Window);
ASSERT(RealTypeComponent!=NULL);
TCastWindowComponentTo* const FinalCastedWindowComponent = static_cast<TCastWindowComponentTo* const >(RealTypeComponent);
ASSERT(FinalCastedWindowComponent!=NULL);
return FinalCastedWindowComponent;
}
};
TCasterForEachWindowComponent CasterForEachWindowComponentFunction=&(_::casterForEachWindowComponent);
this->checkDuplicateType(CasterForEachWindowComponentFunction, casterRepository);
casterRepository[WINDOW_COMPONENT_TYPE]=CasterForEachWindowComponentFunction;
registerVisitedComponentListItem(casterRepository, mpl::next<TCurrentItem>::type(), TEnd());
}
template<class TEnd>
void registerVisitedComponentListItem(TCasterRepository& casterRepository, TEnd CurrentItem, TEnd End)
{}
Код из того же большого проекта.
Ассерты после static_cast и "шаблонная магия" особенно доставляют.
Мне конечно boost::mpl нравиться, но я считаю, что его негоже использовать в реальных проектах.
roman-kashitsyn 29.03.2012 11:47 # +4
Говногость 29.03.2012 12:23 # 0
Поэтому тут заводятся списки типов, в каждом типе заводится идентификатор реального типа. При кастинге от одного предка до другого между ветками множественного наследования: сначала каститься объект до реального типа, указанного в const_cast<PegThing* const>(Window)->Type() идентификатором, а только потом обратно до другой ветки наследования.
Говногость 29.03.2012 12:46 # 0
TarasB 29.03.2012 12:00 # 0
Говногость 29.03.2012 12:06 # 0
Kirinyale 30.03.2012 12:35 # 0
> но я считаю, что его негоже использовать в реальных проектах
А я считаю, что использовать в реальных проектах "гоже" что угодно, если есть чёткое понимание "зачем", руки у всех использующих прямые, а извилины извилистые.
Правда, судя по описанной "рахитектуре", здесь не тот случай.
Говногость 30.03.2012 12:37 # 0
Kirinyale 30.03.2012 12:42 # 0
2. (вынуждено) Списки реакций состояния при использовании boost::statechart.
От 2 позже ушли, потому что boost::statechart дико тормозной (в плане компиляции) и, как оказалось, недостаточно функциональный. Сделали своё, "такое же, только лучше".
Других поводов к использованию пока не было, но не стал бы зарекаться, что и никогда не будет.
Говногость 30.03.2012 12:56 # 0
Знаю. :-[
Kirinyale 30.03.2012 12:59 # 0
Говногость 30.03.2012 13:29 # 0
Мы начали использовать, но пока не так долго.
Kirinyale 30.03.2012 13:37 # 0
http://sourceforge.net/projects/libfsm/
Писался в нерабочее время (в основном не мной), потому работодателю не принадлежит. Примерно соответствует стейтчарту со следующими основными отличиями:
+ параметры состояний
+ контроль порядка передачи событий между ортогональными областями
+ абстрактные состояния
+ компиляция в несколько раз быстрее, хотя на больших машинах всё равно долго
- хистори (ни разу не понадобилось)
- compile-time валидация (иногда вылазит боками, но жить можно, если осторожно; что смогли - перекрыли ассертами, ещё кое-что - доп.тулзой)
- mpl
К сожалению, собственно библиотекой заниматься времени почти нет, поэтому всё в "неприбранном" виде и без документации (только с каким-то простейшим семплом).
Чёрт, даже архив для закачки положить забыли, надо будет добавить. :) Однако уже джва проекта на этом сделали, результатами довольны.
Говногость 30.03.2012 17:45 # 0
> контроль порядка передачи событий между ортогональными областями
Это что? А именно ортогональные области.
Kirinyale 30.03.2012 18:00 # 0
Это когда внутри некоторого состояния одновременно существует несколько (больше одного) подсостояний. "Ортогональная область" - это нечто вроде "под-стейтчарта" внутри состояния, т.е. набор подсостояний, которые взаимно исключают друг друга, но могут сосуществовать с любыми состояниями других орт.областей.
Подробнее - где-то в документации самого стейтчарта (http://www.boost.org/doc/libs/1_49_0/libs/statechart/doc/index.html#Overview) и в документах, описывающих теорию, на которой он основан (ссылки там тоже есть).
Для простых вещей, конечно, можно и без этого обойтись, но у нас в последних нескольких играх на таких стейт-машинах строится вообще вся логика приложения (меню, всплывающие окошки, сама игра, HUD... в общем, всё). Когда привыкаешь правильно мыслить - оказывается очень удобно, надёжно и гибко (т.к. связи между состояниями минимальны и их почти всегда можно безболезненно перебросить в другое место на схеме, тем самым поменяв уровень вложенности, приоритеты и т.п., ничего не сломав)
Говногость 30.03.2012 18:17 # 0
Kirinyale 30.03.2012 18:45 # 0
Да, вспомнил ещё одно отличие от буста: родитель состояния у нас не обязан быть полным типом. Для перехода в другое состояние полный тип тоже не нужен (если только не нужно передать ему параметры). Достаточно просто где-то объявить и реализовать состояние, и на старте оно само корректно встроит себя в граф. Только конструкторы-деструкторы в пустых состояниях объявлять надо, чтобы компилятор не вздумал их выкинуть. Ну и в lib/dll граф не вынесешь. Но вроде и не надо.
В общем, зависимостей между модулями тоже получается минимум (в бусте, насколько помню, приходилось связывать практически всё со всем). Как раз за счёт этого уменьшается надёжность: некоторые ошибки принципиально ловятся только в рантайме. Но меньше зависимостей = меньше перекомпиляций.
Kirinyale 30.03.2012 13:53 # 0
1) порядок передачи событий в ортогональные области не фиксирован и время от времени меняется прямо в рантайме (например, при переходах)
2) состояние, имеющее несколько ортогональных областей внутри, получает каждое событие по нескольку раз (столько, сколько областей), если ему не сделан discard ни одним подсостоянием.
Второе обнаружили внезапно ближе к концу проекта, и это стало бомбой, которую срочно пришлось глушить гвоздями, т.к. из-за этого, например, в разных местах игры время шло с разной скоростью. :)
Автору как-то писал на эту тему письмо. Оказалось, что для него стала откровением сама возможность того, что кому-то понадобится событие, которое обрабатывают все, но не дискардит никто (например, банальный пофреймовый Update). Скорость компиляции обещал улучшить в следующей версии, но было это года два уже назад, а версии вроде так и нет...
niXman 04.04.2012 12:36 # 0
boost.msm - идеальный вариант.
минимум в 6 раз меньше рантайм оверхеда. в списке рассылки не раз встречал упоминания о 12-15 кратном уменьшение оверхеда.
boost.mpl как и boost.type_traits замечательные библиотеки. хз как люди в с++ могут мыслить значениями, а не типами %)
да и вообще, не скажу что какая-то из подбиблиотек boost`а кривая или ненужная. все зависит от кривизны рук использующего.
Kirinyale 04.04.2012 12:45 # 0
Хотя не, вру - недавно таки профайлер показал лёгкие тормоза, но уже в нашем велосипеде, и вызваны они были исключительно нашей собственной ленью при его разработке (что-то константное не закешировали, а вычисляли при каждом вбрасывании события). Исправили за 5 минут.
Главная тормознутость стейтчарта - всё-таки не в рантайме, а в компайл-тайме. Когда проект собирается 25 минут на сильной машине или час на билд-боте, притом 90% времени - именно стейтчарт, это печально.
Но отказались мы от него не столько из-за тормозов, сколько из-за некоторых функциональных проблем. Как уже сказал выше, автору я на эту тему письмо писал, и он признал, что просто не рассматривал подобное использование. А жаль.
guest8 09.04.2019 12:57 # −999