2009-10-19 14 views
6

Estoy trabajando con una API de bajo nivel que acepta un char* y un valor numérico para representar una cadena y su longitud, respectivamente. Mi código usa std::basic_string y llama a estos métodos con la traducción adecuada. Desafortunadamente, muchos de estos métodos aceptan longitudes de cadena de tamaño variable (es decir, max (unsigned char), máximo (short), etc.) y estoy atascado escribiendo código para asegurarme de que las instancias de cadenas no exceden la longitud máxima prescrita. por la API de bajo nivel.¿Se puede aprovechar std :: basic_string para implementar una cadena que tenga una limitación de longitud?

Por defecto, la longitud máxima de una instancia std::basic_string está obligado por el valor máximo de size_t (ya sea max (unsigned int) o max (__int64)). ¿Hay alguna forma de manipular los rasgos y las implementaciones de asignadores de una implementación de std::basic_string para que pueda especificar mi propio tipo para usar en lugar de size_t? Al hacerlo, espero aprovechar las comprobaciones de límites existentes dentro de la implementación std::basic_string, por lo que no tengo que hacerlo al realizar la traducción.

Mi investigación inicial sugiere que esto no es posible sin necesidad de escribir mi propia clase string, pero espero que me daba algo :)

Respuesta

5

puede pasar un asignador personalizado a std::basic_string que tiene un tamaño máximo de lo que desee. Esto debería ser suficiente. Tal vez algo como esto:

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

, entonces probablemente puede hacer esto:

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

EDIT: que acabo de hacer una prueba para asegurarse de que esto funciona como se esperaba. El siguiente código lo prueba.

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

Esa última s += s se puso sobre la parte superior y causar una excepción std::bad_alloc, (ya que mi límite es de poco menos de 64k). Desafortunadamente, la implementación de std::basic_string::max_size() de gcc no basa su resultado en el asignador que utiliza, por lo que todavía afirmará que puede asignar más. (No estoy seguro de si esto es un error o no ...).

Pero esto definitivamente le permitirá imponer límites duros en los tamaños de las cuerdas de una manera simple. Incluso podría hacer que el tamaño máximo sea un parámetro de plantilla, por lo que solo debe escribir el código para el asignador una vez.

+1

Hubiera agregado un parámetro de plantilla int y tenía max_size() return N; luego puede hacer typedef std :: basic_string , my_allocator > my256string; – jmucchiello

+0

Creo que exponer 'size_type' como un argumento de plantilla sería beneficioso, ya que entonces el usuario es libre de elegir qué' size_type' es más adecuado para una instancia de cadena dada. Entonces, la especialización de plantilla parcial ayudará a hacer que este tipo juegue bien con 'basic_string'. –

0

no se puede crear una clase con std :: string como padre y anular c_str()? ¿O define su propio c_str16(), c_str32(), etc. e implementa la traducción allí?

+2

La mayor parte de la biblioteca estándar no pretende ser heredado de (no tienen métodos virtuales). Entonces esto no es aconsejable. –

4

Estoy de acuerdo con Evan Teran sobre su solución. Esto es sólo una modificación de su solución no más:

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

ten en cuenta que no debe utilizar el polimorfismo en absoluto con myalloc. Así que esto es desastroso:

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

sólo lo utilizan como si fuera un tipo separado, es seguro en caso siguiente:

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

Sí, pensé en hacer del tamaño máximo un parámetro de plantilla. Desafortunadamente, no creo que tu solución funcione como está. Debido a que la implementación de cadena (al menos de gcc) no basa su tamaño máximo en el tamaño máximo del asignador. Así que tuve que hacer 'allocate (...)' throw si pedía más de 'max_size' bytes. –

+0

@Evan Su solución es agradable, usted obtuvo mi +1 por cierto. Agregaré más código para 'asignar 'como lo aclaró :) – AraK

Cuestiones relacionadas