Showing posts with label std::tr1. Show all posts
Showing posts with label std::tr1. Show all posts

Saturday, March 19, 2011

object factory

Просто нереально простая и в тоже время для меня весьма приятная фабрика. Сие чудо было сотворенно под влиянием Александреску и ревью 3D движков. Приятного аппетита)

#ifndef __CMN_OBJ_FACTORY_H__
#define __CMN_OBJ_FACTORY_H__

#include "cmnNonCopyable.h"

#include < map >
#include < string >
#include < memory >
#include < assert.h >

namespace utils
{
    namespace factory
    {
        template < typename TIdentifierType, class TObjectType >
        struct default_err_policy
        {
            TObjectType* OnUnknownType( TIdentifierType )
            {
                assert( false );
                return NULL;
            }
        };

        template
        <
            typename TObjectType,
            typename TIdentifierType  = std::string,
            typename TCreatorCallback = TObjectType * ( * ) ( ),
            typename TCopyablePolicy  = noncopyable,
            template < typename, class > class error_policy = factory::default_err_policy
        >
        class object_factory
            : public error_policy < TIdentifierType, TObjectType >
            , TCopyablePolicy
        {
            class Constructor
            {
                Constructor();

            public:
                explicit Constructor( TCreatorCallback func ) : func_( func )
                { }

                template < typename TType >
                explicit Constructor( TType* dumb ) : func_( NULL ), type_creator_( new TypeCreator < TType > )
                { }

                TObjectType* operator( )( )
                {
                    return ( func_ ? func_( ) : ( type_creator_ ? type_creator_->Create() : NULL ) );
                }

            private:
                struct TypeCreatorBase
                {
                    virtual TObjectType* Create() = 0;
                };

                template < typename TType >
                struct TypeCreator
                    : public TypeCreatorBase
                    , TCopyablePolicy
                {
                    virtual TObjectType* Create()
                    {
                        return new TType();
                    }
                };

            private:
                TCreatorCallback func_;
                std::tr1::shared_ptr < TypeCreatorBase > type_creator_;
            };

            typedef std::map < TIdentifierType, Constructor > fmap;
            fmap factory_;

        public:
            template < typename TType >
            bool Register( const TIdentifierType& id )
            {
                return factory_.insert( fmap::value_type( id, Constructor( ( TType* ) NULL ) ) ).second != 0;
            }

            template < TCreatorCallback func >
            bool Register( const TIdentifierType& id )
            {
                return factory_.insert( fmap::value_type( id, Constructor( func ) ) ).second != 0;
            }

            bool Unregister( const TIdentifierType& id )
            {
                return factory_.erase( id ) != 0;
            }

            TObjectType* CreateObject( const TIdentifierType& id )
            {
                typename fmap::iterator iter = factory_.find( id );
                if ( iter != factory_.end() )
                {
                    return ( iter->second )( );
                }

                return OnUnknownType( id );
            }
        };
    } // factory
} // utils

#endif

Тест сего творения:

#include < iostream >
#include "cmnObjFactory.h"

using namespace std;
using namespace utils::factory;
//---------------------------------
struct IBase { virtual void Test() = 0; };

struct Test1 : IBase {
    Test1() { cout << "Create 1" << endl; }
    virtual void Test() { } };

struct Test2 : IBase {
    Test2(int i) { cout << "Create 2" << endl; }
    virtual void Test() { } };

IBase* Create() { return new Test2(2); }
//---------------------------------
object_factory < IBase > g_factory;

int _tmain(int argc, _TCHAR* argv[])
{
    g_factory.Register < Test1 >  ("test1");
    g_factory.Register < Create > ("test2");

    IBase* pObj1 = g_factory.CreateObject("test1");
    IBase* pObj2 = g_factory.CreateObject("test2");

    return 0;
}

Tuesday, October 19, 2010

Об исключениях и С++

    В С++ есть исключения и есть RAII техники. Теперь абстрактная задача:
Есть некие внешние функции Start, Stop и Task. Мы с ними работаем в таком порядке: Start вызывать нужно до вызова Task а Stop - после(причем Stop вызывать нужно обязательно, но только, если был вызван Start - строгое условие). Клиентский код мы изменить не можем.

    Теперь более подробнее - некий псевдо-код задачи:


try
{
    if (NULL != p)
    {
        p->Start(); // может бросить исключение

        // Некий опасный код бросающий исключения...

        DangerTask(); // может бросить исключение

        // Некий опасный код бросающий исключения...

        p->Stop(); // может бросить исключение
    }
    else
    {
        // Некий опасный код бросающий исключения...

        DangerTask(); // может бросить исключение, а также - повторение кода

        // Некий опасный код бросающий исключения...
    }
}
catch(const std::exception&)
{
    // И что делать? Мы должны вызывать p->Stop() или нет?
}
catch(...)
{
    // И что делать? Мы должны вызывать p->Stop() или нет?
}

    Этот псевдокод плох тем, что имеется повторение кода, неоднозначность в обработке ошибок, небезопасный код. В большинстве случаев такое прокатывает - т.к. важно чтоб заработало все побыстрее, и был низкий уровень входимости/опыта для суппорта, и да - уровень входимости ниже, но если код растет линейно - то время на его добавление экспонециально растет. Но сейчас не об этом, нужно просто написать правильный транзакционный код, и без повторений кода (см. выше - DangerTask в коде дважды вызывается).
    Это довольно типичная задача. В большинстве случаев люди на это(возможные исключения и повторения кода) не обращают внимание(т.к. они в своем коде не бросают исключения - а думают что если оно произошло - то это мол редкость и вообще уже мало что можно сделать в этом случае). Не буду говорить о вероятности и т.п. Суть в том - что нам нужно обеспечить строгую гарантию безсбойного транзакционного режима Start/Stop, и мы допускаем что чужой код и наш код весьма бросает исключения.
    Задача на самом деле не сложная. Апологеты ООП сразу вспомнят про RAII - и наплодят пару-тройку классов которые собой обернут эту семантику. В принципе - это всегда хорошее решение. Но вот мне не хочется писать каждый раз классы-обертки. Также не хочу использовать сторонние библиотеки. Для этой цели(чтоб не плодить новое) можно использовать дополнения к STL - std::tr1::shared_ptr и std::tr1::bind вот так:

try
{
    // Некий опасный код бросающий исключения...

    // Это сложная атомарная операция, которую нельзя испортить
       своим новым небезопасным кодом

    p && std::tr1::shared_ptr < void >
        ((p->Start(), LPVOID(NULL)), std::tr1::bind(&IPolicy::Stop, p)),
            DangerTask();
// может бросить исключение

    // Некий опасный код бросающий исключения...
}
catch(const std::exception&)
{
    // Делаем что что-либо, но мы уже не думаем про p->Stop()
}
catch(...)
{
    // Делаем что что-либо, но мы уже не думаем про p->Stop()
}

Также можно заюзать ninja_ptr с предыдущего поста:

try
{
    // Некий опасный код бросающий исключения...

    p && ninja_ptr < IPolicy > ((p->Start(), p), &IPolicy::Stop),
        DangerTask(); // может бросить исключение

    // Некий опасный код бросающий исключения...
}
catch(const std::exception&)
{
    // Делаем что что-либо, но мы уже не думаем про p->Stop()
}
catch(...)
{
    // Делаем что что-либо, но мы уже не думаем про p->Stop()
}

Вот такие приколы. Код я переделал с реального, но не скомпилировал, надеюсь "скобочки" все на месте.