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

gdzie ... jest "0 1 2 ... i-1", na przykład "0 1 2 3 4" dla i == 5.C++: generowanie literały ciągów z szablonu Parametry

Czy to możliwe? (brak rozwiązań wykonujących tę czynność w czasie wykonywania, proszę!)

  • Pytanie zadawane z ciekawości (robi to z makrami/stałymi preprocesorami byłoby łatwe, ale co z parametrami szablonu)?
  • Znaczenie: skonstruowany w czasie kompilacji łańcuch literowy. Teraz widzę, że const nie wymusza tego, ale może wykorzystać dowolną funkcję ewaluacyjną do generowania ciągów.
+1

To jest po prostu niemożliwe. Co próbujesz osiągnąć przez to? –

+1

@Moo: ciekawość, bez użycia jeszcze. Gdyby "i" było stałą preprocesora, byłoby to łatwe, ale z szablonami nie mogłem wymyślić sposobu na rozwiązanie tego problemu. – Thomas

+0

@Reno: Szablon, ponieważ może istnieć wiele instancji dla różnych 'i'; jak mogę przepisać to bez struktury? Nie sądzę, że 'szablon < int i > const char * s = ...;' zostanie skompilowany. – Thomas

Odpowiedz

3

Nie, ale jest to możliwe:

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(); 

btw dlaczego używasz c struny? C++ ma klasę std :: string, która jest lepsza.

EDIT

Chyba można użyć szablonu specjalizacji:

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

Rozwiązanie w czasie wykonywania jest oczywiście łatwe do znalezienia, ale to nie było moje pytanie; punktem był "const". – Thomas

+0

I oczywiście, mógłbym zainicjować 'std :: string' ze stałą łańcuchową, ale najpierw musi zostać wygenerowana w jakiś sposób! – Thomas

+0

Specjalizacja szablonów nie zapewnia ogólnego rozwiązania, chyba że jest rekursywna (ale nie widzę jeszcze sposobu na rekursję). – Thomas

1

niemożliwe.

Ponieważ ekspansja szablonu odbywa się w czasie kompilacji, gdy kompilator może zajmować się tylko stałą wartością, o której wie. Każda operacja związana z przydzielaniem pamięci (na przykład inicjowanie ciągu znaków) nie jest obecnie możliwa, ale tylko w czasie wykonywania.

+0

Ale kompilator zna stałą wartość szablonu i nie jest wymagane (dynamiczne) przydzielanie pamięci. – Thomas

+0

, ale nie zna długości znaków *, które muszą być * generowane * w pamięci. dlatego ciąg nie jest dozwolony jako parametr szablonu. –

0

Nie jest to możliwe przy użyciu szablonu. Ale używanie stringstream, tworzenie takich string jest trywialne. Oto pseudokod:

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 
} 

Więcej informacji o stringstreamcan be found here.

1

Kod jesteś prezentując ...

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

... jest nieważny. t::s musi mieć połączenie zewnętrzne. Ponadto definicja musi być szablonowa.

Mocowanie bezpośrednie problemy z kodem, jak ...

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

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

... następnie inicjowania T<i>::s z dowolnym ciągiem jest trywialne.

Więc modulo błędy w kodzie, odpowiedź brzmi "tak, to nie tylko możliwe, to banalne".

Ale dlaczego chcesz, aby ten schemat Rube Goldberga wykonał coś trywialnego?

+0

Masz rację, kod powinien pokazywać tylko ideę, ale był nieważny (przy pomocy C++ 0x mógłbym bezpośrednio napisać 'static const char * s = ...;' wewnątrz struktury). Poprawię powyższy kod. Nadal nie wiem, jak zainicjować ciąg znaków (i odpowiedzieć na moje pytanie)! – Thomas

+0

@Thomas: Po '=' możesz napisać dowolne wywołanie funkcji. To odpowiada na twoje pierwotne pytanie. Dodatkowe pytanie, które zadałeś później w komentarzach, jak to zrobić w czasie kompilacji, jest nieco trudniejsze (jeśli w ogóle jest możliwe), ale wtedy twoja motywacja do tego jest oparta na nieważnym założeniu o 'const' wymagające oceny czasu kompilacji. Pomaga więc skupić się na prawdziwym problemie, który należy rozwiązać (o którym nawet nie wspomniałeś), a nie na niejasno i niepoprawnie postrzeganych problemach z niewyraźnie spostrzeżonymi próbami rozwiązania. Cheers & hth., –

+0

@Alf: gdzie można się spodziewać, że funkcja przydzieli ciąg wyniku? W sterty lub w stosie? – Vlad

5

Jest to technicznie możliwe, jest po prostu bardzo brzydkie. Oto przykład, który generuje literał łańcuchowy dla unsigned int. Nie tworzy (jeszcze) ciągu w postaci "1 2 3 ... i-1", ale jestem pewien, że jest to możliwe, jeśli jesteś gotów poświęcić wysiłek.

#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

Problem OP wywołuje tablicę o rozmiarze co najmniej proporcjonalnym do wartości argumentu szablonu. Myślę, że nie można tego zrobić w przenośnym standardowym C++ w statycznej fazie inicjacji (chociaż trywialna w dynamicznej fazie inicjacji). Ale mogę się mylić ... :-) Pozdrawiam, –

+0

@Alf w C++ 0x jest to dość trywialne zadanie dla statycznej fazy inicjacji. Szkoda, że ​​nadal jesteśmy w 2010 roku! –

1

myślałem że może być wykonalne ze zmiennej liczbie argumentów szablonów. Nie mam kompilatora do przetestowania, ale wyobrażam sobie, że coś działa na tej podstawie.

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'} 
} 
Powiązane problemy