2011-03-30 15 views
33

Tengo una pregunta muy similar astd :: string con ninguna tienda libre de asignación de memoria

How do I allocate a std::string on the stack using glibc's string implementation?

pero creo que vale la pena preguntar de nuevo.

Quiero un std::string con almacenamiento local que se desborda en la tienda gratuita. std::basic_string proporciona un asignador como un parámetro de plantilla, así que parece que lo que hay que hacer es escribir un asignador de almacenamiento local y lo utilizan para parametrizar el basic_string, así:

std::basic_string< 
char, 
std::char_traits<char>, 
inline_allocator<char, 10> 
> 
x("test"); 

Traté de escribir la clase inline_allocator eso funcionaría de la manera esperada: reserva 10 bytes para el almacenamiento, y si el basic_string necesita más de 10 bytes, llama al ::operator new(). No pude hacer que funcione. En el curso de la ejecución de la línea de código anterior, mi biblioteca de cadenas estándar GCC 4.5 llama al constructor de copia para inline_allocator 4 veces. No es claro para mí que haya una forma sensata de escribir el constructor de copia para inline_allocator.

En el otro hilo StackOverflow, Eric melski proporcionan este enlace a una clase de Cromo:

http://src.chromium.org/svn/trunk/src/base/stack_container.h

lo cual es interesante, pero no es una gota en el reemplazo para std::string, ya que envuelve el std::basic_string en un contenedor para que tenga que llamar a un operator->() sobrecargado para obtener el std::basic_string.

No puedo encontrar ninguna otra solución a este problema. ¿Podría ser que no hay una buena solución? Y si eso es cierto, ¿los conceptos std::basic_string y std::allocator son muy defectuosos? Quiero decir, parece que este debería ser un caso de uso muy simple y básico para std::basic_string y std::allocator. Supongo que el concepto std::allocator está diseñado principalmente para piscinas, pero creo que debería cubrir esto también.

Parece que la semántica movimiento rvalue de referencia en C++ 0x podría hacer posible la escritura de inline_allocator, si la biblioteca de cadena se re-escrito para que basic_string utiliza el constructor de su movimiento asignador en lugar del constructor de copia. ¿Alguien sabe cuál es la perspectiva para ese resultado?

Mi aplicación necesita construir un millón de pequeñas cadenas ASCII por segundo, así que terminé escribiendo mi propia clase de cadena de longitud fija basada en Boost.Array, que funciona bien, pero esto todavía me molesta.

+0

Interesante pregunta, +1. –

+4

Como alguien que ha luchado con asignadores personalizados en el pasado, estoy muy interesado en escuchar a una autoridad hablar sobre este tema. –

+0

+1 para "tienda gratis". Ah, y porque es una buena pregunta. –

Respuesta

16

Andrei Alexandrescu, extraordinario programador de C++ que escribió "Modern C++ Design" escribió una vez un excelente artículo sobre la construcción de diferentes implementaciones de cadenas con sistemas de almacenamiento personalizables. Su artículo (linked here) describe cómo puede hacer lo que describió anteriormente como un caso especial de un sistema mucho más general que puede manejar todo tipo de requisitos inteligentes de asignación de memoria. Esto no habla mucho sobre std::string y se centra más en una clase de cadena completamente personalizada, pero es posible que desee examinarlo ya que hay algunas gemas reales en la implementación.

+0

Ese es un gran artículo de Alexandrescu, gracias. Esta es una muy buena respuesta a la pregunta de cómo personalizar el almacenamiento de basic_string. –

2

Creo que el código de Chromium simplemente envuelve las cosas en un bonito caparazón. Pero puede obtener el mismo efecto sin usar el contenedor de envoltura de Chromium.

Como el objeto asignador se copia con tanta frecuencia, debe contener una referencia o un puntero a la memoria. Entonces, lo que tendría que hacer es crear el búfer de almacenamiento, crear el objeto asignador y luego llamar al constructor std :: string con el asignador.

Va a ser mucho más prolijo que usar la clase contenedora, pero debería tener el mismo efecto.

You can see an example of the verbose method (still using the chromium stuff) in my question about stack vectors.

6

Esto generalmente no es necesario.Se llama "optimización de cadena corta", y la mayoría de las implementaciones de std::string ya lo incluyen. Puede ser difícil de encontrar, pero por lo general está allí de todos modos.

Así por ejemplo, aquí está la pieza correspondiente de sso_string_base.h que es parte de MinGW:

enum { _S_local_capacity = 15 }; 

    union 
    { 
_CharT   _M_local_data[_S_local_capacity + 1]; 
size_type  _M_allocated_capacity; 
    }; 

El miembro _M_local_data es el correspondiente uno - espacio para él para almacenar (hasta) 15 caracteres (más un NUL terminador) sin asignar ningún espacio en el montón.

Si la memoria sirve, la biblioteca Dinkumware incluida con VC++ asigna espacio para 20 caracteres, aunque ha pasado un tiempo desde que lo busqué, así que no puedo jurar eso (y buscar mucho de todo en sus encabezados tiende a ser un dolor, así que prefiero evitar mirar si puedo).

En cualquier caso, me gustaría tener buenas probabilidades de que haya estado involucrado en ese demasiado popular tiempo de pasada conocido como optimización prematura.

+1

VC++ asigna 16 bytes para el SSO, que en mi opinión es demasiado pequeño ser generalmente útil como 'std :: wstring' es más comúnmente usado en el código de Windows que' std :: string' y eso solo permite 8 caracteres en un 'std :: wstring' (o 7 si' .c_str() ' se usa con frecuencia). – ildjarn

+0

¿El encabezado no pertenece a las extensiones de la biblioteca MinGW (no es parte de std :: string)? – UncleBens

+0

@UncleBens: No lo creo, pero podría estar equivocado. También hay alternativas (por ejemplo, STLPort, libC++) que definitivamente usan SSO en sus implementaciones de 'std :: string'. –

10

C++ 2011 es realmente va a ayudar aquí :)

El hecho es que el concepto allocator en C++ 03 fue paralizado. Uno de los requisitos era que un asignador de tipo A debería poder desasignar la memoria de cualquier otro asignador del tipo A ... Desafortunadamente, este requisito también está en desacuerdo con los asignadores con estado, cada uno enganchado a su propio grupo.

Howard Hinnant (que gestiona el subgrupo STL del comité C++ y está implementando un nuevo STL desde cero para C++ 0x) ha explorado stack-based allocators on his website, del que puede obtener inspiración.

+1

Eso es muy interesante. Miré la clase 'stack_alloc' de Hinnant y funciona espléndidamente para' std :: vector' pero, lamentablemente, no para 'std :: basic_string'. El problema es nuevamente, creo, en la forma en que 'std :: basic_string' usa el constructor de copias' stack_alloc'. –

+0

Y creo que está permitido usar el constructor de copia de esa manera debido al requisito que describió. –

+0

@James: sí, lamentablemente dado que el estándar casi implica que los asignadores son apátridas (o al menos que todos los asignadores del mismo tipo comparten más o menos el mismo estado, de alguna manera), las copias están perfectamente permitidas. –

Cuestiones relacionadas