Può una leva std :: basic_string per implementare una stringa con una limitazione di lunghezza?

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

Domanda

sto lavorando con un basso livello API che accetta un char* e valore numerico per rappresentare una stringa e la sua lunghezza, rispettivamente. Il mio codice usa std::basic_string e chiama in questi metodi, con la traduzione appropriata. Purtroppo, molti di questi metodi accettano lunghezze di stringa di varie dimensioni (per esempio max (unsigned char), max (short), ecc ...) e io sono la scrittura di codice bloccato per assicurarsi che i miei casi di stringa non superino la lunghezza massima prescritta dall'API di basso livello.

Per default, la lunghezza massima di un'istanza std::basic_string è vincolata dal valore massimo di size_t (sia max (unsigned int) o max (__int64)). C'è un modo per manipolare i tratti e le implementazioni allocatore di un'implementazione std::basic_string in modo che io possa indicare il mio tipo da utilizzare al posto di size_t? In questo modo, spero di sfruttare tutte le verifiche Bounds esistenti all'interno della realizzazione std::basic_string in modo da non devo farlo quando si esegue la traduzione.

La mia prima indagine suggerisce che questo non è possibile senza scrivere il mio classe string, ma spero che ho trascurato qualcosa:)

È stato utile?

Soluzione

è possibile passare un allocatore personalizzato per std::basic_string che ha una dimensione massima di quello che vuoi. Questo dovrebbe essere sufficiente. Forse qualcosa di simile:

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;
    }
};

Poi probabilmente si può fare in questo modo:

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

Modifica Ho appena fatto un test per assicurarsi che questo funziona come previsto. Il codice seguente prova di esso.

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;
}

L'ultima s += s metterà sopra la parte superiore e causare un'eccezione std::bad_alloc, (dato che il mio limite è poco meno di 64k). Purtroppo implementazione std::basic_string::max_size() del GCC non basa il suo risultato sul allocatore si utilizza, quindi sarà ancora affermare di essere in grado di allocare più. (Non sono sicuro se questo è un bug o no ...).

Ma questo sarà sicuramente permetterà impongono limiti rigidi sulle dimensioni delle stringhe in modo semplice. Si potrebbe anche fare la dimensione massima di un parametro di template in modo da avere solo scrivere il codice per l'allocatore di una volta.

Altri suggerimenti

Sono d'accordo con Evan Teran sulla sua soluzione. Questa è solo una modifica della sua soluzione non più:

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)));
    }
};

Essere consapevoli non si dovrebbe usare il polimorfismo a tutti con myalloc. Quindi questo è disastrosa:

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

Basta usare come se si tratta di un tipo distinto, è sicuro in caso seguente:

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

Non puoi creare una classe con std :: string come genitore e sovrascrivere c_str ()? O definire la propria c_str16 (), c_str32 (), ecc e implementare traduzione lì?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top