¿Opciones de formato de cadena alternativas en C ++?

7

Estoy buscando la optimización de un código de formato de cadena que se ha golpeado mucho en nuestro código. Habíamos estado usando ostringstream, y convertí el código para usar sprintf (en realidad, los sprintfs más seguros de Microsoft). He cambiado el tipo de seguridad para el rendimiento en tiempo de ejecución. Pero he depurado suficientes accidentes extraños relacionados con el sprintf para saber que el sprintf tiene sus propios defectos graves que la comprobación del tiempo de compilación de las capturas de ostringstream. Pero ostringstream es más que un orden de magnitud más lento que mi velocidad, y eso no es tolerable en este código. Tampoco estoy entusiasmado con la legibilidad del formato de cadena de C ++, pero esto puede ser completamente subjetivo.

Desafortunadamente, al formatear cadenas en C ++, tengo varias opciones "estándar" que son subóptimas:

  1. sprintf: rápido, pero no es seguro. Puede tener errores insidiosos cuando no se utiliza la cadena de formato incorrecta.
  2. ostringstream: lento, pero seguro. IMO Feo, demasiado verboso y difícil de leer.
  3. boost :: format - un poco más legible que la OMI de ostringstream, pero en mis evaluaciones de rendimiento parece ser incluso más lento y luego de ostringstream, así que esto está fuera.

Para resumir, no estoy realmente satisfecho con las opciones "estándar". Me gustaría algo que tome en serio el rendimiento y la seguridad del tipo. ¿Qué otras opciones de formato de cadena existen para C ++?

    
pregunta Doug T. 21.12.2011 - 17:28

5 respuestas

4

Recomiendo esta biblioteca de formato que escribí recientemente. Aquí hay una lista incompleta de sus características:

Es una biblioteca nueva, pero ya es compatible con casi todas las opciones de formato de printf (con una sintaxis diferente), además de que admite argumentos posicionales, alineación central, carácter de relleno personalizado y tipos definidos por el usuario.

Actualización: ahora también proporciona una implementación printf segura

    
respondido por el vitaut 30.12.2012 - 06:00
3

Sin embargo, supongo que esta no es una respuesta completa, dado que el rendimiento es importante: la ampliación de sprintf podría ser un mejor punto de partida.

Un área en la que debe mirar es cómo funciona GCC. Básicamente, a pesar de que C (y C ++) no se molesta como lenguaje para realizar una comprobación de tipo de% d en lugar de tener un número entero en los argumentos respectivos, normalmente GCC siempre captura eso y da Advertencias . En realidad, puede tratar la advertencia como error con las opciones -Werror . El punto es que, esencialmente, GCC ya realiza la verificación de "seguridad de tipo" en el momento de la compilación. Eso es exactamente lo que necesitas.

    
respondido por el Dipan Mehta 21.12.2011 - 18:30
1

Las secuencias son muy lentas porque tienen una implementación relativamente ineficiente, y también porque tienen un montón de alarmas y silbidos de overhead que realmente no necesitas. Fundamentalmente, sirven el propósito exacto que necesita.

La pregunta, en última instancia, es sobre exactamente cuánto esfuerzo estás dispuesto a realizar y cuántos tipos estás generando. sprintf no es simplemente inseguro, también es completamente inaccesible, algo que no es cierto para ostringstream . También puede utilizar plantillas de expresión para mejorar su rendimiento. El problema de rendimiento de ostringstream no es en absoluto endémico para su diseño.

    
respondido por el DeadMG 21.12.2011 - 18:55
1

Me sacaron aquí después de escribir mi propia printf-like ayudante de formato de tipo seguro actualmente para revisión en CR [enlace] . Me preguntaba acerca de las mejores alternativas y me encontré pensando de una manera demasiado conceptual que parece ser más adecuada aquí que en CR.

Problema de seguridad de tipo de snprintf

El sufijo n o el sufijo _s de Microsoft pueden proteger contra el desbordamiento del búfer y muchos compiladores-compilados en el compás-check pueden proteger contra otros errores (como argumentos insuficientes), pero aún así, hay algunos esquinas casos. El problema está en va_list , donde todos los tipos se borran y la conversión se realiza en función de los especificadores de tipo dentro de la cadena de formato. Otro problema es que no puede pasar objetos (las clases que no se pueden copiar de forma trivial e incluso las envolturas struct / POD son un problema). No se trata de la seguridad de tipos, sino de la facilidad de uso.

Alternativas a va_list

Las plantillas Variadic son la solución y se pueden utilizar para producir formateadores realmente rápidos. Otra cosa que actualmente tengo en mente es usar un nuevo C ++ 11 literals , por ejemplo. operator "" _fmt(const char *str, size_t len) que podría verificar opciones de formato específicas y seleccionar la implementación más rápida (especialmente cuando no se usan argumentos posicionales). C ++ 14 eliminó aún más la necesidad de funciones constexpr de declaración de retorno, ya que esto se puede hacer de una manera más elegante.

Este buf << "hello %s! pi = %g :)"_fmt("world", 3.14159265) podría ser la forma final de una alternativa rápida y segura para el tipo que tengo en mente. Se podría hacer realmente muy rápido con pocas especializaciones de plantillas.

Boost :: format and ostream

Ambos son monstruosos por dentro, aumentan el formato aún más, pero son de tipo seguro, extensibles y con algunos trucos más que involucran variadas plantillas y especializaciones, podrían hacerse más rápido. ¿Cómo los evaluó? snprintf es para búferes, ostream es para flujos, ostringstream para cadenas, que es más complejo. La idea subyacente es probablemente hacer algo universal y funcional, que sirva para ese propósito. La velocidad generalmente no es tan crítica en estos días, la selección del algoritmo (por ejemplo, O (n ^ 2) frente a O (n log n)) a menudo es más importante que el tiempo que O (1) realmente tomará.

Rápido vs. universal

La solución más rápida siempre sería escribir su propia versión especializada para cada cadena de formato (tipo de). Nada puede vencer eso. Entonces, la pregunta es, ¿qué tan cerca / lejos quieres ir?

Soy un programador de firmware y IAR EWARM ofrece opciones sobre la implementación de prinf y scanf , generalmente algo básico (por ejemplo, sin flotadores hexadecimales) vs. Esto claramente se trata de elegir la potencia en función de la velocidad (y el tamaño).

    
respondido por el firda 05.10.2014 - 15:40
0

Sospecho que estás buscando una bala de plata. Realmente es un triángulo, (rápido, poderoso, seguro). Lo necesita rápido, por lo que su única opción es poderosa o segura, no puede tener ambos. Sprintf es rápido y potente, a expensas de la seguridad.

Debería poder envolver a sprintf de manera que restrinja su uso (haciéndolo menos poderoso y más seguro) y, a pesar de las verificaciones en tiempo de ejecución y la revisión rigurosa del código, asegúrese de que se cumplan sus objetivos de rendimiento y seguridad. Si esto no se puede lograr, piense cuidadosamente por qué no antes de elegir otra opción.

    
respondido por el mattnz 21.12.2011 - 22:52

Lea otras preguntas en las etiquetas