1. C++ / Говнокод #9801

    +999

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 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 нравиться, но я считаю, что его негоже использовать в реальных проектах.

    Запостил: Говногость, 29 Марта 2012

    Комментарии (20) RSS

    • 3.14здец простыня...
      Ответить
      • На самом деле, эта простыня ответственна за dynamic_cast. Это всё из-за того, что dynamic_cast не работает из-за межмодульной проблемы С++, а рахитектура проекта разработана так, что без dynamic_cast нельзя. Также без dynamic_cast тут нельзя, тк в проекте используется множественное наследование, а из-за рахитектуры приходиться кастить от одного предка класса к другому, но расположенным в разных ветках множественного наследования. А static_cast в C++ не может кастовать от одного предка до другого, если они расположены в разных ветках множественного наследования.

        Поэтому тут заводятся списки типов, в каждом типе заводится идентификатор реального типа. При кастинге от одного предка до другого между ветками множественного наследования: сначала каститься объект до реального типа, указанного в const_cast<PegThing* const>(Window)->Type() идентификатором, а только потом обратно до другой ветки наследования.
        Ответить
    • > нравиться
      Ответить
    • > Мне конечно boost::mpl нравиться,
      > но я считаю, что его негоже использовать в реальных проектах

      А я считаю, что использовать в реальных проектах "гоже" что угодно, если есть чёткое понимание "зачем", руки у всех использующих прямые, а извилины извилистые.

      Правда, судя по описанной "рахитектуре", здесь не тот случай.
      Ответить
      • Пример успешного применения boost::mpl лично вами?
        Ответить
        • 1. Один очень важный статический ассерт в нашем сериализаторе.

          2. (вынуждено) Списки реакций состояния при использовании boost::statechart.

          От 2 позже ушли, потому что boost::statechart дико тормозной (в плане компиляции) и, как оказалось, недостаточно функциональный. Сделали своё, "такое же, только лучше".

          Других поводов к использованию пока не было, но не стал бы зарекаться, что и никогда не будет.
          Ответить
          • >(вынуждено)
            Знаю. :-[
            Ответить
            • Тоже стейтчарт, ещё что-то, или всё та же рахитектура?
              Ответить
              • Стейчарт. Думаете, от него стоит отказаться? Что посоветуете? Или только свой велосипед?

                Мы начали использовать, но пока не так долго.
                Ответить
                • Наш велосипед тут:
                  http://sourceforge.net/projects/libfsm/

                  Писался в нерабочее время (в основном не мной), потому работодателю не принадлежит. Примерно соответствует стейтчарту со следующими основными отличиями:
                  + параметры состояний
                  + контроль порядка передачи событий между ортогональными областями
                  + абстрактные состояния
                  + компиляция в несколько раз быстрее, хотя на больших машинах всё равно долго
                  - хистори (ни разу не понадобилось)
                  - compile-time валидация (иногда вылазит боками, но жить можно, если осторожно; что смогли - перекрыли ассертами, ещё кое-что - доп.тулзой)
                  - mpl

                  К сожалению, собственно библиотекой заниматься времени почти нет, поэтому всё в "неприбранном" виде и без документации (только с каким-то простейшим семплом).

                  Чёрт, даже архив для закачки положить забыли, надо будет добавить. :) Однако уже джва проекта на этом сделали, результатами довольны.
                  Ответить
                  • Спасибо. :)

                    > контроль порядка передачи событий между ортогональными областями
                    Это что? А именно ортогональные области.
                    Ответить
                    • > Это что? А именно ортогональные области.

                      Это когда внутри некоторого состояния одновременно существует несколько (больше одного) подсостояний. "Ортогональная область" - это нечто вроде "под-стейтчарта" внутри состояния, т.е. набор подсостояний, которые взаимно исключают друг друга, но могут сосуществовать с любыми состояниями других орт.областей.

                      Подробнее - где-то в документации самого стейтчарта (http://www.boost.org/doc/libs/1_49_0/libs/statechart/doc/index.html#Overview) и в документах, описывающих теорию, на которой он основан (ссылки там тоже есть).

                      Для простых вещей, конечно, можно и без этого обойтись, но у нас в последних нескольких играх на таких стейт-машинах строится вообще вся логика приложения (меню, всплывающие окошки, сама игра, HUD... в общем, всё). Когда привыкаешь правильно мыслить - оказывается очень удобно, надёжно и гибко (т.к. связи между состояниями минимальны и их почти всегда можно безболезненно перебросить в другое место на схеме, тем самым поменяв уровень вложенности, приоритеты и т.п., ничего не сломав)
                      Ответить
                      • А графическими редакторами графа состояний пользуетесь или вручную код набиваете?
                        Ответить
                        • Вручную. Там его не так много. А для того, чтобы увидеть получившуюся схему в целом, в нашем велосипеде есть функция, выбрасывающая эту схему в stdout. Что и делается первым делом на старте, т.е. в логе сразу видно всё дерево состояний.

                          Да, вспомнил ещё одно отличие от буста: родитель состояния у нас не обязан быть полным типом. Для перехода в другое состояние полный тип тоже не нужен (если только не нужно передать ему параметры). Достаточно просто где-то объявить и реализовать состояние, и на старте оно само корректно встроит себя в граф. Только конструкторы-деструкторы в пустых состояниях объявлять надо, чтобы компилятор не вздумал их выкинуть. Ну и в lib/dll граф не вынесешь. Но вроде и не надо.

                          В общем, зависимостей между модулями тоже получается минимум (в бусте, насколько помню, приходилось связывать практически всё со всем). Как раз за счёт этого уменьшается надёжность: некоторые ошибки принципиально ловятся только в рантайме. Но меньше зависимостей = меньше перекомпиляций.
                          Ответить
                • По моему опыту, кроме скорости, самые серьёзные проблемы стейтчарта в следующем:

                  1) порядок передачи событий в ортогональные области не фиксирован и время от времени меняется прямо в рантайме (например, при переходах)

                  2) состояние, имеющее несколько ортогональных областей внутри, получает каждое событие по нескольку раз (столько, сколько областей), если ему не сделан discard ни одним подсостоянием.

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

                  Автору как-то писал на эту тему письмо. Оказалось, что для него стала откровением сама возможность того, что кому-то понадобится событие, которое обрабатывают все, но не дискардит никто (например, банальный пофреймовый Update). Скорость компиляции обещал улучшить в следующей версии, но было это года два уже назад, а версии вроде так и нет...
                  Ответить
              • statechart неплохая машина, но рантаймовская, из-за чего - дико тормозная.
                boost.msm - идеальный вариант.
                минимум в 6 раз меньше рантайм оверхеда. в списке рассылки не раз встречал упоминания о 12-15 кратном уменьшение оверхеда.

                boost.mpl как и boost.type_traits замечательные библиотеки. хз как люди в с++ могут мыслить значениями, а не типами %)

                да и вообще, не скажу что какая-то из подбиблиотек boost`а кривая или ненужная. все зависит от кривизны рук использующего.
                Ответить
                • Для каких именно задач тормозная? Мы свои игрушки сколько ни профайлили - ни разу не замечали, чтоб тормозила стейт-машина. Это ещё очень постараться нужно, чтобы она в таком приложении узким местом стала.

                  Хотя не, вру - недавно таки профайлер показал лёгкие тормоза, но уже в нашем велосипеде, и вызваны они были исключительно нашей собственной ленью при его разработке (что-то константное не закешировали, а вычисляли при каждом вбрасывании события). Исправили за 5 минут.

                  Главная тормознутость стейтчарта - всё-таки не в рантайме, а в компайл-тайме. Когда проект собирается 25 минут на сильной машине или час на билд-боте, притом 90% времени - именно стейтчарт, это печально.

                  Но отказались мы от него не столько из-за тормозов, сколько из-за некоторых функциональных проблем. Как уже сказал выше, автору я на эту тему письмо писал, и он признал, что просто не рассматривал подобное использование. А жаль.
                  Ответить
    • показать все, что скрытоvanished
      Ответить

    Добавить комментарий