Obviamente podemos concatenar dos literales de cadena en una constexpr
función, pero ¿qué pasa con la concatenación de un literal de cadena con una cadena devuelta por otra constexpr
función como en el código a continuación?
template <class T>
constexpr const char * get_arithmetic_size()
{
switch (sizeof(T))
{
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
default: static_assert(dependent_false_v<T>);
}
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic_v<T>, const char *> make_type_name()
{
const char * prefix = std::is_signed_v<T> ? "int" : "uint";
return prefix; // how to concatenate prefix with get_arithmetic_size<T>() ?
}
static_assert(strings_equal(make_type_name<int>, make_type_name<int32_t>);
El código crea un identificador de cadena independiente del compilador de un tipo aritmético.
EDITAR1:
Un ejemplo un poco más complicado es:
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
template <class T>
constexpr std::enable_if_t<is_specialization<T, std::vector>::value || is_specialization<T, std::list>::value, const char *> make_type_name()
{
return "sequence"; // + make_type_name<typename T::value_type>;
}
static_assert(strings_equal(make_type_name<std::vector<int>>(), make_type_name<std::list<int>>()));
std::array
(y probablemente + plantillas variadas)typeid
operador. Parte de la razóntypeid
es parte del lenguaje (por ejemplo, respaldado por una palabra clave de idioma dedicada) en lugar de una función de biblioteca es que implementarlo se basa en la "magia del compilador": no es posible implementar en el lenguaje sin algún apoyo dedicado de la implementación .Respuestas:
Aquí hay una clase de cadena de tiempo de compilación rápida:
puedes usarlo así:
lo que lleva a declaraciones como:
paso.
Ejemplo en vivo .
Ahora, una cosa molesta es que la longitud del búfer está en el sistema de tipos. Puede agregar un
length
campo y hacer queN
sea "tamaño de búfer", y modificarct_str
para copiar sololength
y dejar los bytes finales como0
. Luego anulecommon_type
para devolver el máximoN
de ambos lados.Eso le permitiría pasar
ct_str{"uint"}
yct_str{"int"}
en el mismo tipo de valor y hacer que el código de implementación sea un poco menos molesto.Las implementaciones de funciones ahora se convierten en:
lo cual es mucho más natural de escribir.
Ejemplo en vivo .
fuente
else
enget_arithmetic_size
conif constexpr
incluso si lo hacereturn
, porque sinelse
la afirmacióndependent_false_v<T>
fallará.No es imposible. Puede implementar algo como a continuación (es C ++ 14).
https://ideone.com/BaADaM
Si no le gusta usar
<cmath>
, puede reemplazarstd::log
:fuente
std::log
es demasiado complicado para mí, necesito una técnica genérica para concatenar cadenasconstexpr
, no te preocupesstd::log()
. Puede reemplazarlo, pero el código se ampliará,std::log
nistd::strcmp
se garantiza que ni lo seaconstexpr
. De hecho, el estándar específicamente les prohíbe serconstexpr
desde C ++ 14. Por lo tanto, su código en realidad hace uso de extensiones no estándar.