Как создать функцию, возвращающую вектор, которая использует распределитель пользовательских пулов, если он предусмотрен, но по умолчанию используется std :: allocator? - PullRequest
3 голосов
/ 23 февраля 2012

В приведенном ниже коде есть функция make_vector ().Он создает вектор и возвращает его вызывающей стороне.Я хочу иметь возможность указать распределитель для вектора, но использовать по умолчанию std :: allocator по умолчанию.Это связано с тем, что при некоторых обстоятельствах все, что мне нужно, это распределитель по умолчанию, но в других случаях мне нужно выделять его из некоторых предопределенных пулов памяти.

Самое близкое, что я получил, - это шаблон функции make_vector2 ().Он работает с std :: allocator, но я не знаю, как передать аргумент 'arena' в мой пользовательский распределитель.

Надеюсь, этот рабочий пример c ++ 11 объяснит это лучше:

#include <malloc.h>
#include <cinttypes>
#include <cstddef>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <vector>

namespace mem
{
    // Memory arena to allocate from.
    enum class ARENA
    {
        TEXTURES,
        FONTS,
        SCRIPTS
    };

    // Allocate block from specific arena.
    void *malloc( const std::size_t size, const ARENA arena )
    {
        return std::malloc( size /*, arena */ );
    }

    // Free block from specific arena.
    void free( void *ptr, const ARENA arena )
    {
        std::free( ptr /*, arena */ );
    }


    // The allocator - forward declaration.
    // Not derived from std::allocator - should it be?
    // Based on code from here:
    // http://drdobbs.com/184403759?pgno=2
    template<typename T> class allocator;

    // Specialised for void.
    template<> class allocator<void>
    {
        public:
            typedef std::size_t size_type;
            typedef ptrdiff_t difference_type;
            typedef void* pointer;
            typedef const void* const_pointer;
            typedef void value_type;

            template<typename U> struct rebind
            {
                typedef allocator<U> other;
            };
    };


    template<typename T> class allocator
    {
        public:
            typedef std::size_t size_type;
            typedef std::ptrdiff_t difference_type;
            typedef T* pointer;
            typedef const T* const_pointer;
            typedef T& reference;
            typedef const T& const_reference;
            typedef T value_type;

            template<typename U> struct rebind
            {
                 typedef allocator<U> other;
            };

            allocator( ARENA arena ) noexcept :
                arena_( arena )
            {}

            ~allocator() noexcept
            {}

            pointer address( reference x ) const
            {
                    return &x;
            }

            const_pointer address( const_reference x ) const
            {
                return &x;
            }

            pointer allocate( size_type n, allocator<void>::const_pointer hint = 0 )
            {
                void *p = mem::malloc( n * sizeof( T ), arena_ );
                if ( p == nullptr )
                {
                        throw std::bad_alloc();
                }
                return static_cast<pointer>( p );
            }

            void deallocate( pointer p, size_type n )
            {
                mem::free( p, arena_ );
            }

            size_type max_size() const noexcept
            {
                return std::numeric_limits<std::size_t>::max() / sizeof( T );
            }

            void construct( pointer p, const T& val )
            {
                new (p) T(val);
            }

            void destroy( pointer p )
            {
                p->~T();
            }

            allocator( const allocator& src ) noexcept
            {
                arena_ = src.arena_;
            }

            ARENA arena_;
    };
} // namespace mem

template<class T1, class T2> bool operator==( const mem::allocator<T1> &alloc1, const mem::allocator<T2> &alloc2 ) noexcept
{
    return alloc1.arena_ == alloc2.arena_;
}

template<class T1, class T2> bool operator!=( const mem::allocator<T1> &alloc1, const mem::allocator<T2> &alloc2 ) noexcept
{
    if alloc1.arena_ != alloc2.arena_;
}

// How do I allow the custom allocator to be passed? Function parameter? Template?
std::vector<uint8_t> make_vector()
{
    std::vector<uint8_t> vec;
    // Do stuff with the vector
    return vec;
}

// This template function seems to work with std::allocator
template< typename T > std::vector<uint8_t,T> make_vector2()
{
    std::vector<uint8_t,T> vec;
    // Do stuff with the vector.
    return vec;
}

int main( int argc, char **argv )
{
    // vec1 - Allocates from TEXTURES arena
    // See the C++11 FAQ by  Bjarne Stroustrup here:
    // http://www2.research.att.com/~bs/C++0xFAQ.html#scoped-allocator
    std::vector<uint8_t, mem::allocator<uint8_t>> vec1( mem::allocator<uint8_t>{mem::ARENA::TEXTURES} );

    // vec2 - Make the vector using the default allocator.
    auto vec2 = make_vector2< std::allocator<uint8_t> >();

    return 0;
}

В main () vec1 создан для использования арены TEXTURES для выделения.Используемая арена передается в конструктор распределителя.Vec2 создается шаблонной функцией make_vector2 () и использует std :: allocator.

Q: Как определить функцию make_vector (), чтобы она могла создавать вектор, использующийstd :: allocator или пользовательский распределитель пулов выше?

Ответы [ 3 ]

6 голосов
/ 23 февраля 2012

В C ++ 11 шаблоны функций могут иметь аргументы шаблона по умолчанию:

template<class T, class Alloc = std::allocator<T>>
std::vector<T, Alloc> make_vector(Alloc const& al = Alloc()){
  std::vector<T, Alloc> v(al);
  // ...
  return v;
}

Живой пример на Ideone.

В C ++ 03 (или с компиляторами, которые не поддерживают эту функцию), это немного более громоздко, но вы можете перегружать на основе параметров шаблона:

template<class T>
std::vector<T> make_vector(){
  std::vector<T> v;
  // ...
  return v;
}

template<class T, class Alloc>
std::vector<T, Alloc> make_vector(Alloc const& al = Alloc()){
  std::vector<T, Alloc> v(al);
  // ...
  return v;
}

Живой пример на Ideone.

3 голосов
/ 23 февраля 2012

Вы можете предоставить две различные функции перегрузки:

template<typename T, typename Alloc>
std::vector<T, Alloc> func(Alloc a)
{
    return std::vector<T, Alloc>();
}

template<typename T>
std::vector<T, std::allocator<T> > func()
{
    return func<T>(std::allocator<T>());
}
0 голосов
/ 23 февраля 2012

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

...