Ser capaz de crear y manipular cadenas durante el tiempo de compilación en C ++ tiene varias aplicaciones útiles. Aunque es posible crear cadenas de tiempo de compilación en C ++, el proceso es muy engorroso, ya que la cadena debe declararse como una secuencia de caracteres variada, p. Ej.
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Las operaciones como la concatenación de cadenas, la extracción de subcadenas y muchas otras se pueden implementar fácilmente como operaciones en secuencias de caracteres. ¿Es posible declarar cadenas en tiempo de compilación más convenientemente? Si no, ¿hay una propuesta en los trabajos que permitiría una conveniente declaración de cadenas en tiempo de compilación?
Por qué fallan los enfoques existentes
Idealmente, nos gustaría poder declarar cadenas de tiempo de compilación de la siguiente manera:
// Approach 1
using str1 = sequence<"Hello, world!">;
o, usando literales definidos por el usuario,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
donde decltype(str2)
tendria un constexpr
constructor. Es posible implementar una versión más desordenada del enfoque 1, aprovechando el hecho de que puede hacer lo siguiente:
template <unsigned Size, const char Array[Size]>
struct foo;
Sin embargo, la matriz necesitaría tener un enlace externo, por lo que para que el enfoque 1 funcione, tendríamos que escribir algo como esto:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
No hace falta decir que esto es muy inconveniente. Enfoque 2 en realidad no es posible implementar. Si declaramos un constexpr
operador literal ( ), ¿cómo especificaríamos el tipo de retorno? Como necesitamos que el operador devuelva una secuencia de caracteres variada, deberíamos usar el const char*
parámetro para especificar el tipo de retorno:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Esto da como resultado un error de compilación, porque s
no es un constexpr
. Intentar solucionar esto haciendo lo siguiente no ayuda mucho.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
El estándar dicta que este formulario de operador literal específico está reservado para los tipos enteros y de coma flotante. Mientras 123_s
funcionaría, abc_s
no lo haría. ¿Qué pasa si abandonamos por completo los literales definidos por el usuario y solo usamos una constexpr
función regular ?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Como antes, nos encontramos con el problema de que la matriz, ahora un parámetro para la constexpr
función, ya no es un constexpr
tipo.
Creo que debería ser posible definir una macro de preprocesador en C que tome una cadena y el tamaño de la cadena como argumentos, y devuelva una secuencia que consta de los caracteres en la cadena (usando BOOST_PP_FOR
, stringificación, subíndices de matriz y similares). Sin embargo, no tengo el tiempo (o suficiente interés) para implementar tal macro =)
fuente
constexpr
funciones e inicializar matrices (por lo tanto, concat, substr, etc.).constexpr
cadenas se pueden analizar durante el tiempo de compilación, de modo que puede tomar diferentes rutas de código según los resultados. Esencialmente, puede crear EDL dentro de C ++; Las aplicaciones son bastante ilimitadas.Respuestas:
No he visto nada que coincida con la elegancia de Scott Schurr
str_const
presentado en C ++ Now 2012 . Sin embargo, requiereconstexpr
.Aquí le mostramos cómo puede usarlo y qué puede hacer:
¡No se vuelve mucho más genial que la comprobación del rango de tiempo de compilación!
Tanto el uso como la implementación están libres de macros. Y no hay límite artificial en el tamaño de la cadena. Publicaría la implementación aquí, pero estoy respetando los derechos de autor implícitos de Scott. La implementación está en una sola diapositiva de su presentación vinculada a lo anterior.
fuente
str_const
y la otra basada ensequence
), esto puede ser posible. El usuario usaríastr_const
para inicializar la cadena, pero las operaciones posteriores que crean nuevas cadenas devolveríansequence
objetos.template<char... cs>
. En teoría, podría construir algo que tome una cadena literal y compile el contenido en una función. Ver la respuesta por dyp. Una biblioteca de aspecto muy completo es metaparse . Esencialmente, puede definir cualquier asignación de cadenas literales a tipos e implementarla con este tipo de tecnología.constexpr operator==
. Lo siento. La presentación de Scott debería ayudarlo a comenzar a hacerlo. Es mucho más fácil en C ++ 14 que en C ++ 11. Ni siquiera me molestaría en probar en C ++ 11. Vea las últimasconstexpr
charlas de Scott aquí: youtube.com/user/CppCones posible implementar esto sin depender de boost, usando macro muy simple y algunas de las características de C ++ 11:
(los dos últimos no son estrictamente necesarios aquí)
debemos ser capaces de crear una instancia de una plantilla variadic con indicadores proporcionados por el usuario de 0 a N, una herramienta también útil, por ejemplo, para expandir la tupla en el argumento de la función de plantilla variable (ver preguntas: ¿Cómo puedo expandir una tupla en los argumentos de la función de plantilla variable?
" desempaquetar "una tupla para llamar a un puntero de función coincidente )
luego defina una plantilla variadic llamada cadena con parámetro de tipo no char:
ahora la parte más interesante: pasar literales de caracteres a la plantilla de cadena:
Una simple demostración de concatenación muestra el uso:
https://ideone.com/8Ft2xu
fuente
operator+
lugar deoperator*
?(str_hello + str_world)
CSTRING
macro. De lo contrario, no puede crear unaCSTRING
llamada interna a un[]
operador, ya que el doble[[
está reservado para los atributos.Editar: como señaló Howard Hinnant (y yo en mi comentario al OP), es posible que no necesite un tipo con cada carácter de la cadena como un argumento de plantilla única. Si necesita esto, hay una solución libre de macros a continuación.
Hay un truco que encontré al intentar trabajar con cadenas en tiempo de compilación. Requiere introducir otro tipo además de la "cadena de plantilla", pero dentro de las funciones, puede limitar el alcance de este tipo.
No utiliza macros, sino algunas características de C ++ 11.
fuente
pair<int,pair<char,double>>
. ¡Estaba orgulloso de mí mismo y descubrí esta respuesta y la biblioteca de metaparse hoy! Realmente debería buscar SO más a fondo antes de comenzar proyectos tontos como este :-) Supongo que, en teoría, un compilador completamente C ++ podría construirse a partir de este tipo de tecnología. ¿Qué es lo más loco que se ha construido con esto?char[]
.my_str.print();
lugar destr.print();
?Si no desea utilizar la solución Boost, puede crear macros simples que harán algo similar:
El único problema es el tamaño fijo de 64 caracteres (más cero adicional). Pero se puede cambiar fácilmente según sus necesidades.
fuente
sizeof(str) > i
(en lugar de agregar los0,
tokens adicionales )? Es fácil definir unatrim
metafunción que haga esto después de que la macro ya haya sido llamada, pero sería bueno si la macro en sí misma pudiera modificarse.sizeof(str)
. Es posible agregar manualmente el tamaño de cadena,MACRO_GET_STR(6, "Hello")
pero esto requiere que las macros Boost funcionen porque escribirlo manualmente requiere 100 veces más código (necesita implementos simples1+1
).Hay un artículo: Uso de cadenas en metaprogramas de plantilla C ++ por Abel Sinkovics y Dave Abrahams.
Tiene alguna mejora sobre su idea de usar macro + BOOST_PP_REPEAT : no requiere pasar tamaño explícito a macro. En resumen, se basa en el límite superior fijo para el tamaño de la cadena y la "protección contra el exceso de cadenas":
más impulso condicional :: mpl :: push_back .
Si acepta ceros finales, bucles de macro escritos a mano, 2x repetición de cadena en macro expandida y no tiene Boost, entonces estoy de acuerdo, es mejor. Sin embargo, con Boost serían solo tres líneas:
DEMO EN VIVO
fuente
A nadie parece gustarle mi otra respuesta: - <. Entonces, aquí muestro cómo convertir un str_const a un tipo real:
Compila con clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)
fuente
Aquí hay una solución sucinta de C ++ 14 para crear std :: tuple <char ...> para cada cadena de tiempo de compilación que se pasa.
Y aquí hay uno para crear un tipo de tiempo de compilación único, recortado de la otra publicación de macro.
Es realmente una lástima que los literales definidos por el usuario no se puedan usar para esto todavía.
fuente
Un colega me retó a concatenar cadenas en la memoria en tiempo de compilación. Incluye la creación de instancias de cadenas individuales en tiempo de compilación también. El listado completo de códigos está aquí:
fuente
objdump -t a.out |grep my
no encuentra nada. Cuando comencé a escribir este código, seguí experimentando con la eliminaciónconstexpr
de las funciones yobjdump
les mostré cuándoconstexpr
se omitió. Estoy 99.9% seguro de que sucede en tiempo de compilación.-S
), notará que gcc (4.7.2) resuelve lasconstexpr
funciones en tiempo de compilación. Sin embargo, las cadenas no se ensamblan en tiempo de compilación. Por el contrario, (si lo interpreto correctamente) para cada carácter de esas cadenas "ensambladas", hay unamovb
operación propia , que posiblemente sea la optimización que estaba buscando.basado en la idea de Howard Hinnant , puede crear una clase literal que agregará dos literales juntos.
fuente
str_at
vienestr_at<int I>(const char* a) { return a[i]; }
Su enfoque # 1 es el correcto.
No, no es correcto Esto compila con clang y gcc. Espero que sea c ++ 11 estándar, pero no soy un experto en lenguaje.
Lo que realmente me encantaría de c ++ 17 sería que lo siguiente fuera equivalente (para completar el enfoque n. ° 1)
Ya existe algo muy similar en el estándar para literales definidos por el usuario con plantilla, como también menciona void-pointer, pero solo para dígitos. Hasta entonces, otro pequeño truco es usar el modo de edición de anulación + copiar y pegar de
Si no te importa la macro, entonces esto funciona (ligeramente modificado de la respuesta de Yankes):
fuente
La solución de kacey para crear un tipo de tiempo de compilación único, con modificaciones menores, también se puede usar con C ++ 11:
Utilizar:
fuente
Mientras jugaba con el mapa hana de impulso, me encontré con este hilo. Como ninguna de las respuestas resolvió mi problema, encontré una solución diferente que quiero agregar aquí, ya que podría ser potencialmente útil para otros.
Mi problema era que cuando usaba el mapa hana de impulso con cadenas hana, el compilador aún generaba un código de tiempo de ejecución (ver más abajo). Obviamente, la razón era que para consultar el mapa en tiempo de compilación debe ser
constexpr
. Esto no es posible ya que laBOOST_HANA_STRING
macro genera una lambda, que no se puede usar enconstexpr
contexto. Por otro lado, el mapa necesita cadenas con diferentes contenidos para ser de diferentes tipos.Como las soluciones en este hilo están utilizando una lambda o no proporcionan diferentes tipos para diferentes contenidos, encontré útil el siguiente enfoque. También evita el hacky
str<'a', 'b', 'c'>
sintaxis .La idea básica es tener una versión de Scott Schurr basada
str_const
en el hash de los personajes. Lo esc++14
, peroc++11
debería ser posible con una implementación recursiva de lacrc32
función (ver aquí ).Uso:
El código de ensamblador resultante con
clang-cl
5.0 es:fuente
Me gustaría agregar dos mejoras muy pequeñas a la respuesta de @ user1115339. Los mencioné en los comentarios a la respuesta, pero por conveniencia pondré una solución de copiar y pegar aquí.
La única diferencia es la
FIXED_CSTRING
macro, que permite usar las cadenas dentro de las plantillas de clase y como argumentos para el operador de índice (útil si tiene, por ejemplo, un mapa de tiempo de compilación).Ejemplo en vivo .
fuente
Mi propia implementación se basa en el enfoque de la
Boost.Hana
cadena (clase de plantilla con caracteres variados), pero utiliza solo elC++11
estándar y lasconstexpr
funciones con un control estricto de tiempo de compilación (sería un error de tiempo de compilación si no es una expresión de tiempo de compilación). Se puede construir a partir de la cadena C sin procesar habitual en lugar de elegante{'a', 'b', 'c' }
(a través de una macro).Implementación: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_string.hpp
Pruebas: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp
Ejemplos de uso:
Los detalles sobre un
constexpr
límite de tiempo de compilación de funciones: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexprPara otros detalles de uso ver las pruebas.
Todo el proyecto actualmente es experimental.
fuente
En C ++ 17 con una función de macro auxiliar, es fácil crear cadenas de tiempo de compilación:
Y este es un ejemplo de uso:
fuente