2010-11-04 18 views
6
template < unsigned int i > 
struct t { 
    static const char *s; 
}; 
template < unsigned int i > 
const char* t<i>::s = ...; 

donde ... es "0 1 2 ... i-1", por ejemplo "0 1 2 3 4" para i == 5.C++: generar literales de cadena de plantilla de parámetros de

¿Esto es posible? (No hay soluciones de hacer esto en tiempo de ejecución, por favor!)

  • Pregunta hecha por curiosidad (hacerlo con macros del preprocesador/constantes sería fácil, pero ¿qué hay de los parámetros de plantilla)?
  • El significado es: literal de cadena generada en tiempo de compilación. Veo ahora que const no lo fuerza, pero podría tomar cualquier función evaluada en tiempo de ejecución para la generación de cadenas.
+1

Esto simplemente no es posible. ¿Qué es lo que estás tratando de lograr con esto? –

+1

@Moo: curiosidad, todavía no hay uso. Si 'i' fuera una constante de preprocesador, sería fácil, pero con las plantillas no se me ocurre una manera de resolver esto. – Thomas

+0

@Reno: Plantilla, porque puede haber múltiples instancias de varios 'i'; ¿Cómo podría reescribir esto sin una estructura? No creo que la 'plantilla < int i > const char * s = ...;' compile. – Thomas

Respuesta

3

No, pero esto es posible:

template < unsigned int i > 
struct t { 
    static std::string s; 

    static std::string ConvertIntToString() 
    { 
    std::stringstream ss; 
    ss << i; 
    return ss.str(); 
    } 
}; 

template< unsigned int i > 
std::string t<i>::s = t<i>::ConvertIntToStr(); 

por cierto ¿Por qué utiliza cadenas de C? C++ tiene la clase std :: string que es superior.

EDITAR

Creo que se puede utilizar especialización de plantilla:

template < unsigned int i > 
struct t; 

template <> 
struct t<0> 
{ 
    static const char * const s; 
}; 
const char* const t<0>::s = "abc"; 

template <> 
struct t<1> 
{ 
    static const char * const s; 
}; 
const char* const t<1>::s = "123"; 
+0

Una solución en tiempo de ejecución es, por supuesto, fácil de encontrar, pero esa no era mi pregunta; el punto era el 'const'. – Thomas

+0

Y, por supuesto, podría inicializar un 'std :: string' con la cadena constante, ¡pero primero tiene que generarse de alguna manera! – Thomas

+0

La especialización de plantillas no proporcionaría una solución general a menos que sea recursiva (pero, aún no veo una forma de hacer la recursión) – Thomas

1

imposible.

Porque la expansión de la plantilla se realiza en tiempo de compilación cuando el compilador solo puede tratar el valor constante que conoce. Cualquier operación que implique asignación de memoria (por ejemplo, inicialización de una cadena) no es posible en este momento, sino solo en tiempo de ejecución.

+0

Pero el compilador conoce el valor de plantilla constante y no es necesaria ninguna asignación de memoria (dinámica). – Thomas

+0

pero no conoce la longitud char * s, que debe ser * generada * en la memoria. es por eso que la cadena no está permitida como parámetro de plantilla. –

0

Esto no es posible con la plantilla. Pero usar stringstream, crear tal string es trivial. Aquí está el código de pseudo:

string makeit(int i) 
{ 
    stringstream sstr; 

    for (int x = 0; x < i-1; x++) 
     put x and ' ' in sstr; 
    put i in sstr; 
    return sstr contents converted to string 
} 

Más información sobre stringstreamcan be found here.

1

El código que está presentando, ...

template < unsigned int i > 
struct t { 
    static const char *s; 
}; 
static const char* t::s = ...; 

... es inválida . t::s debe tener un enlace externo. Además, la definición debe ser templada.

La fijación de los problemas directos con el código, como ...

template < unsigned int i > 
struct T 
{ 
    static const char * const s; 
}; 

template< unsigned i > 
const char* const T<i>::s = ...; 

... entonces inicializar el T<i>::s con cualquier cadena deseada es trivial.

Por lo tanto, modulo los errores en su código, la respuesta es "sí, no solo es posible, es trivial".

¿Pero por qué quieres que este esquema de Rube Goldberg sea algo trivial?

+0

Tienes razón, el código solo debería demostrar la idea, pero no era válido (con C++ 0x podría haber escrito directamente 'static const char * s = ...;' dentro de la estructura). Corregiré el código de arriba. Aún así, no veo cómo inicializar la cadena (¡y responder a mi pregunta!) – Thomas

+0

@Thomas: después del '=' puede escribir cualquier llamada a la función. Eso responde su pregunta original. La pregunta adicional que ha planteado más adelante en los comentarios, cómo hacerlo en tiempo de compilación, es un poco más complicado (si es posible), pero luego su motivación para hacerlo se basa en una suposición no válida sobre 'const' requiriendo una evaluación en tiempo de compilación. Por lo tanto, ayuda concentrarse en el problema real que se debe resolver (que ni siquiera ha mencionado), no en los problemas percibidos de forma vaga e incorrecta de una solución intentada vagamente percibida. Cheers & hth., –

+0

@Alf: ¿dónde esperas que la función asigne la cadena de resultados? En montón o en la pila? – Vlad

5

Esto es técnicamente posible, es muy muy feo. Aquí hay un ejemplo que genera un literal de cadena para un int sin firmar. No (todavía) crea una cadena de la forma "1 2 3 ... i-1", sin embargo, estoy seguro de que es posible si está dispuesto a gastar el esfuerzo.

#include <iostream> 
#include <string> 
#include <limits> 

/////////////////////////////////////////////////////////////////////////////// 
// exponentiation calculations 
template <int accum, int base, int exp> struct POWER_CORE : POWER_CORE<accum * base, base, exp - 1>{}; 

template <int accum, int base> 
struct POWER_CORE<accum, base, 0> 
{ 
    enum : int { val = accum }; 
}; 

template <int base, int exp> struct POWER : POWER_CORE<1, base, exp>{}; 

/////////////////////////////////////////////////////////////////////////////// 
// # of digit calculations 
template <int depth, unsigned int i> struct NUM_DIGITS_CORE : NUM_DIGITS_CORE<depth + 1, i/10>{}; 

template <int depth> 
struct NUM_DIGITS_CORE<depth, 0> 
{ 
    enum : int { val = depth}; 
}; 

template <int i> struct NUM_DIGITS : NUM_DIGITS_CORE<0, i>{}; 

template <> 
struct NUM_DIGITS<0> 
{ 
    enum : int { val = 1 }; 
}; 

/////////////////////////////////////////////////////////////////////////////// 
// Convert digit to character (1 -> '1') 
template <int i> 
struct DIGIT_TO_CHAR 
{ 
    enum : char{ val = i + 48 }; 
}; 

/////////////////////////////////////////////////////////////////////////////// 
// Find the digit at a given offset into a number of the form 0000000017 
template <unsigned int i, int place> // place -> [0 .. 10] 
struct DIGIT_AT 
{ 
    enum : char{ val = (i/POWER<10, place>::val) % 10 }; 
}; 

struct NULL_CHAR 
{ 
    enum : char{ val = '\0' }; 
}; 

/////////////////////////////////////////////////////////////////////////////// 
// Convert the digit at a given offset into a number of the form '0000000017' to a character 
template <unsigned int i, int place> // place -> [0 .. 9] 
    struct ALT_CHAR : DIGIT_TO_CHAR< DIGIT_AT<i, place>::val >{}; 

/////////////////////////////////////////////////////////////////////////////// 
// Convert the digit at a given offset into a number of the form '17' to a character 

// Template description, with specialization to generate null characters for out of range offsets 
template <unsigned int i, int offset, int numDigits, bool inRange> 
    struct OFFSET_CHAR_CORE_CHECKED{}; 
template <unsigned int i, int offset, int numDigits>     
    struct OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, false> : NULL_CHAR{}; 
template <unsigned int i, int offset, int numDigits>     
    struct OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, true> : ALT_CHAR<i, (numDigits - offset) - 1 >{}; 

// Perform the range check and pass it on 
template <unsigned int i, int offset, int numDigits> 
    struct OFFSET_CHAR_CORE : OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, offset < numDigits>{}; 

// Calc the number of digits and pass it on 
template <unsigned int i, int offset> 
    struct OFFSET_CHAR : OFFSET_CHAR_CORE<i, offset, NUM_DIGITS<i>::val>{}; 

/////////////////////////////////////////////////////////////////////////////// 
// Integer to char* template. Works on unsigned ints. 
template <unsigned int i> 
struct IntToStr 
{ 
    const static char str[]; 
}; 

template <unsigned int i> 
const char IntToStr<i>::str[] = 
{ 
    OFFSET_CHAR<i, 0>::val, 
    OFFSET_CHAR<i, 1>::val, 
    OFFSET_CHAR<i, 2>::val, 
    OFFSET_CHAR<i, 3>::val, 
    OFFSET_CHAR<i, 4>::val, 
    OFFSET_CHAR<i, 5>::val, 
    OFFSET_CHAR<i, 6>::val, 
    OFFSET_CHAR<i, 7>::val, 
    OFFSET_CHAR<i, 8>::val, 
    OFFSET_CHAR<i, 9>::val, 
    NULL_CHAR::val 
}; 


/////////////////////////////////////////////////////////////////////////////// 
// Tests 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::wcout << IntToStr<17>::str << std::endl; 
    std::wcout << IntToStr<173457>::str << std::endl; 
    std::wcout << IntToStr<INT_MAX>::str << std::endl; 
    std::wcout << IntToStr<0>::str << std::endl; 
    std::wcout << IntToStr<1>::str << std::endl; 
    std::wcout << IntToStr<-1>::str << std::endl; 

    return 0; 
} 
+0

El problema del OP requiere una matriz con un tamaño que sea al menos proporcional al valor del argumento de la plantilla. Creo que es imposible hacerlo en el estándar portátil C++ en la fase de inicio estático (aunque trivial en la fase de inicio dinámico). Pero podría estar equivocado ... :-) Cheers, –

+0

@Alf en C++ 0x es trivial para la fase de inicio estático. Lástima que todavía estamos en 2010! –

1

yo creo que podría ser factible con plantillas variadic. No tengo un compilador para probar, pero me imagino que algo en la línea de este podría funcionar.

template < char ... RHS, unsigned int i> 
struct t { 
    static const char s[] = t<' ', char(i+'0'), RHS, i-1>::s; 
}; 

template <char ... RHS > 
struct t<RHS, 0> { 
    static const char s[] = {'0', RHS, '\0'}; 
}; 

void main() { 
    std::cout << t<5>::s; // {'0',' ','1',' ','2',' ','3',' ','4',' ','5','\0'} 
} 
Cuestiones relacionadas