¿Por qué permitir la concatenación de literales de cuerdas?

Recientemente fui mordido por un error sutil.

char ** int2str = { "zero", // 0 "one", // 1 "two" // 2 "three",// 3 nullptr }; assert( int2str[1] == std::string("one") ); // passes assert( int2str[2] == std::string("two") ); // fails 

Si tienes poderes de revisión de código divino, notarás que olvidé el , después de "two" .

Después del gran esfuerzo por encontrar ese error, tengo que preguntar por qué alguien querría este comportamiento.

Puedo ver cómo esto podría ser útil para la magia macro, pero entonces, ¿por qué esta es una “característica” en un lenguaje moderno como python?

¿Alguna vez ha utilizado la concatenación de cadenas literales en el código de producción?

Veo varias respuestas de C y C ++ pero ninguna de las respuestas realmente ¿por qué o realmente cuál fue la razón de esta característica? En C ++, esta característica proviene de C99 y podemos encontrar la razón de ser de esta característica yendo a Rationale for International Standard — Lenguajes de progtwigción — C sección 6.4.5 cadenas que dice ( énfasis mío ):

Una cadena puede continuar a lo largo de múltiples líneas usando la continuación de la barra invertida-nueva línea, pero esto requiere que la continuación de la cadena comience en la primera posición de la siguiente línea. Para permitir un diseño más flexible y resolver algunos problemas de preprocesamiento (ver §6.10.3), el Comité C89 introdujo la concatenación de cadenas literales. Dos literales de cadena en una fila se pegan juntos, sin ningún carácter nulo en el medio, para hacer una cadena literal combinada. Esta adición al lenguaje C le permite a un progtwigdor extender un literal de cadena más allá del final de una línea física sin tener que usar el mecanismo de barra invertida-nueva línea y, por lo tanto, destruir el esquema de sangría del progtwig. No se introdujo un operador de concatenación explícito porque la concatenación es una construcción léxica en lugar de una operación en tiempo de ejecución.

Python, que parece tener la misma razón, reduce la necesidad de que los feos \ continúen con los literales de cadena larga. Que se trata en la sección 2.4.2 Concatenación literal de cadenas de la Referencia del lenguaje Python .

Claro, es la manera fácil de hacer que tu código se vea bien:

 char *someGlobalString = "very long " "so broken " "onto multiple " "lines"; 

La mejor razón, sin embargo, es para formatos de impresión raros, como forzar el tipo:

 uint64_t num = 5; printf("Here is a number: %"PRIX64", what do you think of that?", num); 

Hay muchos de los definidos, y pueden ser útiles si tiene requisitos de tamaño de tipo. Míralos a todos en este enlace . Algunos ejemplos:

 PRIo8 PRIoLEAST16 PRIoFAST32 PRIoMAX PRIoPTR 

Es una gran característica que le permite combinar cadenas de preprocesador con sus cadenas.

 // Here we define the correct printf modifier for time_t #ifdef TIME_T_LONG #define TIME_T_MOD "l" #elif defined(TIME_T_LONG_LONG) #define TIME_T_MOD "ll" #else #define TIME_T_MOD "" #endif // And he we merge the modifier into the rest of our format string printf("time is %" TIME_T_MOD "u\n", time(0)); 

Casos donde esto puede ser útil:

  • Generar cadenas que incluyen componentes definidos por el preprocesador (este es quizás el caso de uso más grande en C, y es uno que veo muy, muy frecuentemente).
  • División de constantes de cadena en múltiples líneas

Para proporcionar un ejemplo más concreto para el primero:

 // in version.h #define MYPROG_NAME "FOO" #define MYPROG_VERSION "0.1.2" // in main.c puts("Welcome to " MYPROG_NAME " version " MYPROG_VERSION "."); 

De la referencia de análisis léxico de python, sección 2.4.2:

Esta función se puede usar para reducir la cantidad de barras invertidas necesarias, para dividir cadenas largas de manera conveniente en líneas largas, o incluso para agregar comentarios a partes de cadenas

http://docs.python.org/reference/lexical_analysis.html

No estoy seguro acerca de otros lenguajes de progtwigción, pero por ejemplo, C # no te permite hacer esto (y creo que esto es algo bueno). Por lo que puedo decir, la mayoría de los ejemplos que muestran por qué esto es útil en C ++ seguirían funcionando si pudiera usar algún operador especial para la concatenación de cadenas:

 string someGlobalString = "very long " + "so broken " + "onto multiple " + "lines"; 

Esto puede no ser tan cómodo, pero ciertamente es más seguro. En su ejemplo de motivación, el código no sería válido a menos que agregue , para separar elementos o + para concatenar cadenas …

Para que pueda dividir los literales de cadena larga en líneas.

Y sí, lo he visto en código de producción.

Por razones, ampliando y simplificando la respuesta de Shafik Yaghmour: concatenación de cadenas literales originada en C (por lo tanto, heredada por C ++), al igual que el término, por dos razones (las referencias son de la Razón para el lenguaje de progtwigción ANSI C ):

  • Para el formato: para permitir que los literales de cadena larga abarquen varias líneas con la sangría adecuada, en contraste con la continuación de la línea, lo que destruye el esquema de sangría ( 3.1.4 literales de cadena ); y
  • Para macro magia: para permitir la construcción de literales de cadena por macros (a través de stringizing) ( 3.8.3.2 El # operador ).

Se incluye en los lenguajes modernos Python y D porque lo copiaron de C, aunque en ambos se ha propuesto su desaprobación, ya que es propenso a errores (como se nota) e innecesario (ya que uno solo puede tener una concatenación). operador y plegado constante para la evaluación en tiempo de comstackción; no puede hacer esto en C porque las cadenas son punteros, y por lo tanto no puede agregarlos).

No es fácil de eliminar porque rompe la compatibilidad, y debe tener cuidado con la precedencia (la concatenación implícita ocurre durante el lexing, antes de los operadores, pero reemplazarlo con un operador significa que debe tener cuidado con la precedencia), por lo que todavía está presente. .

Sí, está en código de producción usado. Guía de estilo de Google Python : la longitud de la línea especifica:

Cuando una cadena literal no cabe en una sola línea, use paréntesis para unir líneas implícitas.

 x = ('This will build a very long long ' 'long long long long long long string') 

Consulte ” Concatenación literal de cadenas ” en Wikipedia para obtener más detalles y referencias.

Ciertamente tengo tanto en C como en C ++. A primera vista, no veo mucha relación entre su utilidad y cuán “moderno” es el lenguaje.

Mientras que la gente ha sacado las palabras de mi boca sobre los usos prácticos de la función, hasta ahora nadie ha tratado de defender la elección de la syntax.

Por lo que sé, el error tipográfico que puede deslizarse como resultado fue probablemente pasado por alto. Después de todo, parece que la robustez contra los errores tipográficos no estaba en la mente de Dennis, como lo demuestra más adelante:

 if (a = b); { printf("%d", a); } 

Además, existe la posibilidad de que no valga la pena usar un símbolo adicional para la concatenación de literales de cadenas. Después de todo, no hay mucho más que se pueda hacer con dos de ellos, y tener un símbolo allí podría crear la tentación de intente usarlo para la concatenación de cadenas en tiempo de ejecución, que está por encima del nivel de las funciones integradas de C.

Algunos lenguajes modernos de nivel superior basados ​​en la syntax C han descartado esta notación, presumiblemente porque es propenso a los errores tipográficos. Pero estos idiomas tienen un operador para la concatenación de cadenas, como + (JS, C #) . (Perl, PHP), ~ (D, aunque esto también ha mantenido la syntax de yuxtaposición de C), y el plegado constante (en lenguajes comstackdos, de todos modos) significa que no hay sobrecarga de rendimiento en tiempo de ejecución.