Pode -se aproveitar o std :: Basic_String para implementar uma string com uma limitação de comprimento?

StackOverflow https://stackoverflow.com/questions/1591591

Pergunta

Estou trabalhando com uma API de baixo nível que aceita um char* e valor numérico para representar uma string e seu comprimento, respectivamente. Meu código usa std::basic_string e chama esses métodos com a tradução apropriada. Infelizmente, muitos desses métodos aceitam comprimentos de string de tamanho variável (ou seja, max (unsigned char), Max (short), etc ...) e estou preso ao escrever um código para garantir que minhas instâncias de string não excedam o comprimento máximo prescrito pela API de baixo nível.

Por padrão, o comprimento máximo de um std::basic_string a instância está vinculada pelo valor máximo de size_t (Max (unsigned int) ou max (__int64)). Existe uma maneira de manipular as características e implementações de alocadores de um std::basic_string implementação para que eu possa especificar meu próprio tipo para usar no lugar de size_t? Ao fazer isso, espero alavancar qualquer cheques de limites existentes dentro do std::basic_string Implementação para que eu não precise fazer isso ao executar a tradução.

Minha investigação inicial sugere que isso não é possível sem escrever minha própria aula de cordas, mas espero ter esquecido de algo :)

Foi útil?

Solução

você pode passar um alocador personalizado para std::basic_string que tem um tamanho máximo do que você quiser. Isso deve ser suficiente. Talvez algo assim:

template <class T>
class my_allocator {
public:
    typedef T              value_type;

    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;

    pointer address(reference r) const             { return &r; }
    const_pointer address(const_reference r) const { return &r; }

    my_allocator() throw() {}

    template <class U>
    my_allocator(const my_allocator<U>&) throw() {}

    ~my_allocator() throw() {}

    pointer allocate(size_type n, void * = 0) {
        // fail if we try to allocate too much
        if((n * sizeof(T))> max_size()) { throw std::bad_alloc(); }
        return static_cast<T *>(::operator new(n * sizeof(T)));
    }

    void deallocate(pointer p, size_type) {
        return ::operator delete(p);
    }

    void construct(pointer p, const T& val) { new(p) T(val); }
    void destroy(pointer p)                 { p->~T(); }

    // max out at about 64k
    size_type max_size() const throw() { return 0xffff; }

    template <class U>
    struct rebind { typedef my_allocator<U> other; };

    template <class U>
    my_allocator& operator=(const my_allocator<U> &rhs) {
        (void)rhs;
        return *this;
    }
};

Então você provavelmente pode fazer isso:

typedef std::basic_string<char, std::char_traits<char>, my_allocator<char> > limited_string;

EDITAR: Acabei de fazer um teste para garantir que isso funcione conforme o esperado. O código a seguir testa.

int main() {
    limited_string s;
    s = "AAAA";
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s; // 512 chars...
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s; // 32768 chars...
    s += s; // this will throw std::bad_alloc

    std::cout << s.max_size() << std::endl;
    std::cout << s.size() << std::endl;
}

Aquele último s += s vai colocá -lo por cima e causar um std::bad_alloc exceção, (como meu limite está a menos de 64k). Infelizmente, GCC's std::basic_string::max_size() A implementação não baseia seu resultado no alocador que você usa, por isso ainda afirma poder alocar mais. (Não tenho certeza se isso é um bug ou não ...).

Mas isso definitivamente permitirá que você impor limites rígidos aos tamanhos das cordas de uma maneira simples. Você pode até fazer com que o tamanho máximo seja um parâmetro de modelo, portanto, você só precisa escrever o código para o alocador uma vez.

Outras dicas

Eu concordo com Evan Teran sobre sua solução. Esta é apenas uma modificação de sua solução não mais:

template <typename Type, typename std::allocator<Type>::size_type maxSize>
struct myalloc : std::allocator<Type>
{
    // hide std::allocator[ max_size() & allocate(...) ]

    std::allocator<Type>::size_type max_size() const throw()
    {
        return maxSize;
    }
    std::allocator<Type>::pointer allocate
        (std::allocator<Type>::size_type n, void * = 0)
    {
        // fail if we try to allocate too much
        if((n * sizeof(Type))> max_size()) { throw std::bad_alloc(); }
        return static_cast<Type *>(::operator new(n * sizeof(Type)));
    }
};

Esteja ciente de que você não deve usar o polimorfismo com myalloc. Então isso é desastroso:

// std::allocator doesn't have a virtual destructor
std::allocator<char>* alloc = new myalloc<char>;

Você apenas usa como se fosse um tipo separado, é seguro no seguinte caso:

myalloc<char, 1024> alloc; // max size == 1024

Você não pode criar uma classe com std :: string como pai e substituir c_str ()? Ou definir seu próprio c_str16 (), c_str32 (), etc e implementar a tradução lá?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top