пятница, 10 сентября 2010 г.

Автоматическая регистрация классов

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

  1. class WindowClassCreator
  2. {
  3.     HINSTANCE       mHInst;
  4.     const TCHAR*    mClassName;
  5. public:
  6.     WindowClassCreator( HINSTANCE hinst, const TCHAR* class_name, WNDPROC window_proc ) : mHInst(hinst), mClassName(class_name)
  7.     {
  8.         WNDCLASSEX wcl = {0};
  9.  
  10.         wcl.cbSize = sizeof(wcl);
  11.         wcl.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
  12.         wcl.lpfnWndProc = window_proc;
  13.         wcl.cbClsExtra = 0;
  14.         wcl.cbWndExtra = 0;
  15.         wcl.hInstance = hInst;
  16.         wcl.hIcon = LoadIcon(0, IDI_APPLICATION);
  17.         wcl.hCursor = LoadCursor(0, IDC_ARROW);
  18.         wcl.hbrBackground = 0;
  19.         wcl.lpszMenuName = 0;
  20.         wcl.lpszClassName = class_name;
  21.         wcl.hIconSm = 0;
  22.  
  23.         ::RegisterClassEx(&wcl);
  24.     }
  25.  
  26.     ~WindowClassCreator()
  27.     {
  28.         ::UnregisterClass( mClassName, mHInst );
  29.     }
  30. };

Если создать объект этого класса в глобальном пространстве, то есть приблизительно так:

  1. const TCHAR* WINDOW_CLASS_NAME = TEXT("ENGINE");
  2. static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
  3. static WindowClassCreator window_class_creator( (HINSTANCE)GetModuleHandle(0), WINDOW_CLASS_NAME, WindowProc );

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

* Подсветка синтаксиса http://quickhighlighter.com

понедельник, 9 августа 2010 г.

Аналог dynamic_cast и RTTI

Решил перепостить в свой блог подсказку, которую я писал на сайте Gamedev.ru (здесь), и заодно немного подправить

Простой аналог dynamic_cast и RTTI для тех, кто не хочет, по каким-либо религиозным причинам, использовать встроенный...

  1. class CType
  2. {
  3.   const CType*  mSuper;
  4.   const char*    mName;
  5. public:
  6.   CType( const CType* parent, const char* name ): mSuper(parent), mName(name) {}
  7.  
  8.   inline bool Is( const CType* objtype ) const
  9.   {
  10.     while( objtype && objtype != this )
  11.     {
  12.       objtype = objtype->mSuper;
  13.     }
  14.     return objtype == this;
  15.   }
  16.  
  17.   inline const char* GetName() const { return mName; }
  18. };
  19.  
  20. #define DECLARE_GET_TYPE(cls)      \
  21.     typedef cls  Self;    \
  22.     virtual const CType*  GetType() const { return &Type(); }
  23.  
  24. #define DECLARE_OBJECT(cls)    \
  25.     typedef Self  Super;    \
  26.     static inline const CType&  Type() { static const CType my_type( &cls::Super::Type(), #cls ); return my_type; }  \
  27.     DECLARE_GET_TYPE(cls)
  28.          
  29. #define DECLARE_ROOT_OBJECT(cls)            \
  30.     static inline const CType&  Type() { static const CType my_type( 0, #cls ); return my_type; } \
  31.     DECLARE_GET_TYPE(cls)             \
  32.     template <class T> static inline T* Cast( cls * obj )      \
  33.     {                \
  34.       return GET_CLASS_TYPE(T)->Is( GET_OBJECT_TYPE(obj) ) ? (T*)obj : NULL;  \
  35.     }                \
  36.     \
  37.     template <class T> static inline const T* Cast( const cls * obj )  \
  38.     {                \
  39.       return GET_CLASS_TYPE(T)->Is( GET_OBJECT_TYPE(obj) ) ? (T*)obj : NULL;  \
  40.     }
  41.  
  42. /////////////////////////////////////////////////////////////////////////////////
  43. #define GET_CLASS_TYPE(cls) (&cls::Type())
  44. #define GET_CLASS_TYPE_NAME(cls) (GET_CLASS_TYPE(cls)->GetName())
  45.  
  46. #define GET_OBJECT_TYPE(obj) (obj->GetType())
  47. #define GET_OBJECT_TYPE_NAME(obj) (GET_OBJECT_TYPE(obj)->GetName())
  48. /////////////////////////////////////////////////////////////////////////////////

пример использования

  1. #include <iostream>
  2.  
  3. class Object
  4. {
  5. public:
  6.   DECLARE_ROOT_OBJECT(Object)
  7.  
  8. };
  9.  
  10. class ChildObject : public Object
  11. {
  12. public:
  13.   DECLARE_OBJECT(ChildObject)
  14. };
  15.  
  16. class SecondChildObject : public ChildObject
  17. {
  18. public:
  19.   DECLARE_OBJECT(SecondChildObject)
  20. };
  21.  
  22. int main()
  23. {
  24.   const Object *o1 = new Object(), *o2 = new ChildObject(), *o3 = new SecondChildObject();
  25.   const ChildObject* derived = 0;
  26.   const SecondChildObject* secondderived = 0;
  27.  
  28.   std::cout<< "o1 is " << GET_OBJECT_TYPE_NAME( o1 ) << std::endl;
  29.   std::cout<< "o2 is " << GET_OBJECT_TYPE_NAME( o2 ) << std::endl;
  30.   std::cout<< "o3 is " << GET_OBJECT_TYPE_NAME( o3 ) << std::endl << std::endl;
  31.  
  32.   derived = Object::Cast<ChildObject>(o1);
  33.   std::cout<< "o1 is " << (derived == 0 ? "not ": "") << GET_CLASS_TYPE_NAME(ChildObject) << std::endl;
  34.   derived = Object::Cast<ChildObject>(o2);
  35.   std::cout<< "o2 is " << (derived == 0 ? "not ": "") << GET_CLASS_TYPE_NAME(ChildObject) << std::endl;
  36.   derived = Object::Cast<ChildObject>(o3);
  37.   std::cout<< "o3 is " << (derived == 0 ? "not ": "") << GET_CLASS_TYPE_NAME(ChildObject) << std::endl << std::endl;
  38.  
  39.  
  40.   secondderived = Object::Cast<SecondChildObject>(o1);
  41.   std::cout<< "o1 is " << (secondderived == 0 ? "not ": "") << GET_CLASS_TYPE_NAME(SecondChildObject) << std::endl;
  42.   secondderived = Object::Cast<SecondChildObject>(o2);
  43.   std::cout<< "o2 is " << (secondderived == 0 ? "not ": "") << GET_CLASS_TYPE_NAME(SecondChildObject) << std::endl;
  44.   secondderived = Object::Cast<SecondChildObject>(o3);
  45.   std::cout<< "o3 is " << (secondderived == 0 ? "not ": "") << GET_CLASS_TYPE_NAME(SecondChildObject) << std::endl << std::endl;
  46.  
  47.   return 0;
  48. }
  49.  

output:

o1 is Object
o2 is ChildObject
o3 is SecondChildObject

o1 is not ChildObject
o2 is ChildObject
o3 is ChildObject

o1 is not SecondChildObject
o2 is not SecondChildObject
o3 is SecondChildObject


Замечание: Данный метод не работает при наследовании от нескольких базовых классов...



* Подсветка синтаксиса http://quickhighlighter.com

суббота, 7 августа 2010 г.

Удобная работа с родительскими классами

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

  1. #define DECLARE_ROOT_OBJECT(cls)    \
  2.     typedef cls  Self;    \
  3.  
  4. #define DECLARE_OBJECT(cls)    \
  5.     typedef Self  Super;    \
  6.     typedef cls Self;

Использовать так:

  1. class Base
  2. {
  3. public:
  4.     DECLARE_ROOT_OBJECT(Base)
  5.  
  6.     virtual void Foo();
  7. };
  8.  
  9. class Derived : public Base
  10. {
  11. public:
  12.     DECLARE_OBJECT(Derived)
  13.  
  14.     void Foo()
  15.     {
  16.         Super::Foo();
  17.     }
  18. };
Метод взят из обсуждения подсказки на сайте Gamedev.ru 

* Подсветка синтаксиса http://quickhighlighter.com

суббота, 3 июля 2010 г.

Первая запись

Здравствуйте

Давно хотел начать писать свой блог, но никак не мог придумать с чего начать.

Проблема о которой я напишу - не надуманная, а вполне реальная. Выглядит она так:

  1. class SomeMenu : public Menu
  2. {
  3. ...
  4.     virtual void Command( const char* command, const char* arg )
  5.     {
  6.         if ( strcmp( command, "Command1" ) == 0 )
  7.         {
  8.             // some commands
  9.         }
  10.         else if ( strcmp( command, "Command2" ) == 0 )
  11.         {
  12.             // ...
  13.         }
  14.         else if ( ... )
  15.         {
  16.             ...
  17.         }
  18.         ......
  19.         else
  20.         {
  21.             Menu::Command( command, arg );
  22.         }
  23.     }
  24. };

Уверен, что многие читающие не видят в этом проблему и отчасти они правы. Если условий не больше 10 и в каждом по 2-3 команды, то такой код, действительно, очень прост и довольно легко поддерживается. А теперь представьте, что условий стало больше 50-100 и в половине из них больше десятка строк, а код подлежит дальнейшей модификации и поиску ошибок...

Сразу становится понятно, что нужно что-то делать...


Для решения этой проблемы, я написал такой шаблон:

  1. template<typename T>
  2. class DefaultCompareFn
  3. {
  4.     public:
  5.     bool operator ()(T op1, T op2) const
  6.     {
  7.         return op1 == op2;
  8.     }
  9. };
  10.  
  11. template<typename Key, typename Value, class KeyCompare = DefaultCompareFn<Key> >
  12. class Factory
  13. {
  14.     Value mResult;
  15.     KeyCompare mCompFn;
  16. public:
  17.     struct Item
  18.     {
  19.         Key key;
  20.         Value value;
  21.     };
  22.  
  23.     Factory( const Item* items, size_t size, Key key, Value defaultValue ) :
  24.         mResult(defaultValue), mCompFn()
  25.     {
  26.         for( size_t i = 0; i < size; i++ )
  27.         {
  28.             if (mCompFn(items[i].key, key))
  29.             {
  30.                 mResult = items[i].value;
  31.                 break;
  32.             }
  33.         }
  34.     }
  35.  
  36.     operator Value()
  37.     {
  38.         return mResult;
  39.     }
  40. };

Применяется это так:

  1. struct strcomparer
  2. {
  3.     bool operator()( const char* str1, const char* str2 ) const
  4.     {
  5.         return strcmp( str1, str2 ) == 0;
  6.     }
  7. };
  8.  
  9. class SomeMenu : public Menu
  10. {
  11.     ...
  12.     typedef void (SomeMenu::*CommandFn)(const char*);
  13.     typedef Factory<const char*, SomeMenu::CommandFn, strcomparer> SomeMenuFactory;
  14.     static const SomeMenuFactory::Item sCommandHandlers[];
  15.     static const size_t sCommandHandlersSize;
  16.  
  17.     void Command1(const char* arg)
  18.     {
  19.         ...
  20.     }
  21.  
  22.     void Command2(const char* arg)
  23.     {
  24.         ...
  25.     }
  26.  
  27.     void Command(const char* command, const char* arg)
  28.     {
  29.         CommandFn fn = SomeMenuFactory( sCommandHandlers, sCommandHandlersSize, command, 0 );
  30.         if (fn != 0)
  31.         {
  32.             (this->*fn)(arg);
  33.         }
  34.         else
  35.         {
  36.              Menu::Command( command, arg );
  37.         }
  38.     }
  39. };
  40.  
  41. const SomeMenu::SomeMenuFactory::Item SomeMenu::sCommandHandlers[] =
  42. {
  43.     { "Command1", &SomeMenu::Command1 },
  44.     { "Command2", &SomeMenu::Command2 },
  45. };
  46.  
  47. const size_t SomeMenu::sCommandHandlersSize = sizeof(SomeMenu::sCommandHandlers)/sizeof(SomeMenu::sCommandHandlers[0]);

Но у этого кода есть недостаток - функции CommandX не могут быть виртуальными или расположенными вне класса. Надеюсь в будущем устранить этот недостаток...