Предыстория.
Был найден такой кусочек кода:
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 ) );
}
};
Идея заключается в использовании временного прокси-объекта, неявно управляющего выделением памяти. Нет надобности заводить вспомогательные переменные и делать кастинг. Да и вообще
Написано в некоторой спешке, критика горячо приветствуется.
Спасибо за внимание.