Небольшой
От: NiJazz Австралия  
Дата: 21.12.10 18:01
Оценка:
Предыстория.
Был найден такой кусочек кода:
DWORD info_length = 0;

if ( !::GetTokenInformation( token.handle(), TokenGroups, 0, 0, &info_length ) && 
                            ERROR_INSUFFICIENT_BUFFER != ::GetLastError() )
    throw an_exception( ::GetLastError() );

std::vector<BYTE> buffer( info_length );   

if ( !::GetTokenInformation( token.handle(), TokenGroups, &buffer[0], info_length, &info_length ) )
    throw an_exception( ::GetLastError() );

Это далеко не единственная winapi, которая возвращает размер буфера для своей работы. Но данный случай интересен тем, что далее:
PTOKEN_GROUPS  pGroups = reinterpret_cast<PTOKEN_GROUPS>( &buffer[0] ); 

for ( DWORD i = 0; i < pGroups->GroupCount && !m_admin; ++ i )
{
    if ( ::EqualSid( psid, pGroups->Groups[i].Sid ) )
        m_admin = true;
}


То есть помимо часто повторяемой процедуры запроса размера буфера, выделения и использования здесь присутствует кастинг (плата за вектор).
Был написан небольшой класс, инкапсулирующий действия с ресайзами и кастингом.
Сперва покажу его в действии, а затем приведу листинг.

smart_buffer<PTOKEN_GROUPS,DWORD> buffer;

if ( !::GetTokenInformation( token.handle(), TokenGroups, 0, 0, buffer.psize() ) )
{
    DWORD err_code = ::GetLastError();

    if ( ERROR_INSUFFICIENT_BUFFER != err_code )
        throw an_exception( err_code );

    if ( !::GetTokenInformation( token.handle(), TokenGroups, buffer, buffer.size(), buffer.psize() ) )
        throw an_exception( ::GetLastError() );
}

Конечно же, кастинг никуда не делся. По крайней мере, он не мозолит глаз в клиентском коде.
for ( DWORD i = 0; i < buffer->GroupCount && !m_admin; ++ i )
{
    if ( ::EqualSid( psid, buffer->Groups[i].Sid ) )
        m_admin = true;
}


Сам класс:

typedef std::vector<BYTE> buffer; 

template <typename T, typename sizeT = buffer::size_type>
class smart_buffer : buffer
{
    friend class buffer_size_proxy;

public:

    typedef T        value_type;
    typedef sizeT    size_type;

    using buffer::size;

    template <typename BufferT>
    class buffer_size_proxy
    {
        BufferT& buffer_;
        typename BufferT::size_type size_; 
        typename const BufferT::size_type initial_size_; 
        size_t ref_cnt_;

    public:
        buffer_size_proxy( BufferT& buffer )
            :    buffer_( buffer ),
                size_( buffer.size() ),
                initial_size_( size_ ),
                ref_cnt_( 1 )
        {
        }

        ~buffer_size_proxy()
        {
            if ( 0 == -- ref_cnt_ && initial_size_ < size_ )
                buffer_.resize( size_ );
        }
        
        buffer_size_proxy( const buffer_size_proxy& rhs )
            :    buffer_( rhs.buffer_ ),
                size_( rhs.size_ ),
                ref_cnt_( rhs.ref_cnt_ + 1 )
        {
        }

        operator typename BufferT::size_type * ()
        {
            return &size_;
        }
    };

public:

    smart_buffer() 
    {
    }

    smart_buffer( size_type initial_size )
        :    buffer( initial_size )
    {
    }

    buffer_size_proxy<smart_buffer<T,sizeT>> psize()
    {
        return buffer_size_proxy<smart_buffer>( *this );
    }

    operator T()
    {
        return reinterpret_cast<T>( &operator[]( 0 ) );
    }

    const T operator -> ()
    {
        return reinterpret_cast<const T>( &at( 0 ) );
    }
};


Идея заключается в использовании временного прокси-объекта, неявно управляющего выделением памяти. Нет надобности заводить вспомогательные переменные и делать кастинг. Да и вообще
Написано в некоторой спешке, критика горячо приветствуется.
Спасибо за внимание.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.