2013-08-03 10 views
7

Sto cercando di trovare un modo comodo per passare letterali stringa come argomenti del modello. Non mi preoccupo di supportare il numero più ampio possibile di compilatori, sto usando l'ultima versione di g ++ con --std=c++0x.Cercando di passare letterali stringa come argomenti del modello

Ho provato molte soluzioni possibili ma tutte mi hanno deluso. Sono un po 'arrendevole, ma prima mi piacerebbe sapere perché un paio di loro non è riuscito a fare il.

Eccoli:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 

    char const *operator()() const { 
     return m_sz; 
    } 
}; 

template<class _rstr> 
string const Get() { 
    return _rstr(); 
} 

int main() { 
    cout << Get<String("hello")>() << endl; 
    return 0; 
} 

E:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 
}; 

template<String const &_rstr> 
string const Get() { 
    return _rstr.m_sz; 
} 

int main() { 
    String constexpr str = "hello"; 
    cout << Get<str>() << endl; 
    return 0; 
} 

L'obiettivo era quello di trovare un modo comodo per passare una stringa letterale alla funzione Get inutile, che restituisce il suo argomento modello come std :: string object.

EDIT: scusa, forse la mia domanda principale non è chiara. La mia domanda è: perché quei due frammenti falliscono?

+1

Nel primo caso, 'String ("ciao")' è un valore, non un tipo, quindi non può essere passato al template 'Get <>' che si aspetta un tipo. Nel secondo caso, C++ 11 non consente che i tipi arbitrari definiti dall'utente (come 'String') siano parametri del modello. –

risposta

3

Re: il vostro OP: I'd like to know why a couple of them failed.

Il commento di @NatanReed è corretto:

  • Il tuo primo frammento non riesce a causa Get ha bisogno di un TYPE e prova un object.
  • Il secondo snippet non riesce perché è illegale definire un argomento modello come riferimento a un oggetto.
    • fino a C++ 2003, ovvero. Quindi reference to an object è diventato legale. argomenti

modello deve essere costanti da un insieme limitato di tipi.

  • See: ISO/IEC 14882-2003 §14.1: parametri del modello
  • See: ISO/IEC 14882-2003 §14.3.2: Template non-argomenti di tipo

e anche allora, il String constexpr str = "hello"; deve avere un collegamento esterno. Quindi metterlo in pila all'interno di main() non funzionerà.

dare una prova:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 
}; 

template<String const &_rstr> 
string const Get() { 
    return _rstr.m_sz; 
} 

extern String constexpr globally_visible_str = "hello"; 
int main() { 
    cout << Get<globally_visible_str>() << endl; 
    return 0; 
} 
13

Non è possibile utilizzare valori letterali stringa come argomento modello, per il semplice motivo non è specificato se due istanze di un valore letterale con lo stesso testo siano lo stesso oggetto o meno. In altre parole , data:

template <char const* str> 
class TC {}; 

TC< "xyz" > v1; 
TC< "xyz" > v2; 

Sarebbe specificato se v1 e v2 avevano lo stesso tipo o meno.

È possibile utilizzare char const[] variabili come argomenti di template, tuttavia, dal momento che hanno un indirizzo definito:

template <char const* str> 
class TC {}; 

extern char const xyz[] = "xyz"; 
TC<xyz> v1; 
TC<xyz> v2; 

In questo caso, v1 e v2 sono garantiti per avere lo stesso tipo di .

EDIT:

Penso C++ 11 elimina la necessità per il extern sulla definizione della stringa, almeno se la stringa e la istanziazione sono tutti nella stessa unità di traduzione. Non sono sicuro, tuttavia, ; l'unica volta che ho fatto qualcosa del genere, non ho accesso a C++ 11.

+0

Posso confermare che la limitazione di "extern" è stata rimossa perché ho provato senza di essa e funziona (era uno dei miei numerosi tentativi). Inoltre, so che non posso passare direttamente le string letterali, ma stavo cercando di aggirare questo problema in qualche modo, e probabilmente ho fallito. –

+0

Chiarimento: dichiarare una variabile con un collegamento esterno (di solito globale) non è "confortevole" per me.Essere in grado di utilizzare una sorta di adattatore 'String', come nei miei due frammenti, è molto più comodo. –

5

È possibile "simulare" stringhe con C++ 11 modelli variadic:

Questo stampa:

ciao mondo !!!

+0

Sì, l'ho provato e funziona, ma non è nemmeno comodo. Ad ogni modo, la mia domanda principale è: perché i due frammenti che ho postato non funzioneranno? –

5

So che il post è vecchio, ma non ho trovato alcuna soluzione per questo problema qui, e forse qualcuno sarebbe interessato a mia soluzione:

template <int N> 
constexpr int string_literal_length(const char (&str)[N]) { 
    return N - 1; 
} 

template <int PassedLength, int CountedLength, char... Characters> 
struct string_literal { 
    static_assert(PassedLength == CountedLength, "Passed to STRING_LITERAL length does not match the length of string..."); 
}; 

#define STRING_LITERAL(N, str) string_literal<N, string_literal_length(str), STRING_LITERAL_##N(str)> 

// ... as long as we need it ... 
#define STRING_LITERAL_128(str) STRING_LITERAL_127(str), str[127] 
#define STRING_LITERAL_127(str) STRING_LITERAL_126(str), str[126] 
#define STRING_LITERAL_126(str) STRING_LITERAL_125(str), str[125] 
#define STRING_LITERAL_125(str) STRING_LITERAL_124(str), str[124] 
// ... 
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), str[4] 
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), str[3] 
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), str[2] 
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), str[1] 
#define STRING_LITERAL_1(str) str[0] 

Ora utilizzo:

template <class SLiteral> 
struct usage_of_string_literal { 
}; 

int main() { 
    usage_of_string_literal<STRING_LITERAL(12, "123456789012")> uosl; 
} 

Purtroppo uno sono tenuti a fornire la lunghezza della stringa per farlo funzionare ma e' s ancora la soluzione più comoda rispetto alla semplice variadic modello arg di caratteri, e la lunghezza è verificata dal static_assert in modo che il compilatore può aiutare a prendere valore appropriato ...


Modifica

Un altro modello Magia. Questo è facendo uso di corto circuito per eliminare delle dimensioni stringa dalla stringa-dichiarazione (C++ 17):

#include <type_traits> 
#include <utility> 

#define MAX_STRING_LITERAL_LENGTH 11 
#define STRING_LITERAL(str) string_literal<char_pack<STRING_LITERAL_11(str)>>::s 

#define STRING_LITERAL_11(str) STRING_LITERAL_10(str), ((TERMINATED_10(str))?(str[10]):('\0')) 
#define STRING_LITERAL_10(str) STRING_LITERAL_9(str), ((TERMINATED_9(str))?(str[9]):('\0')) 
#define STRING_LITERAL_9(str) STRING_LITERAL_8(str), ((TERMINATED_8(str))?(str[8]):('\0')) 
#define STRING_LITERAL_8(str) STRING_LITERAL_7(str), ((TERMINATED_7(str))?(str[7]):('\0')) 
#define STRING_LITERAL_7(str) STRING_LITERAL_6(str), ((TERMINATED_6(str))?(str[6]):('\0')) 
#define STRING_LITERAL_6(str) STRING_LITERAL_5(str), ((TERMINATED_5(str))?(str[5]):('\0')) 
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), ((TERMINATED_4(str))?(str[4]):('\0')) 
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), ((TERMINATED_3(str))?(str[3]):('\0')) 
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), ((TERMINATED_2(str))?(str[2]):('\0')) 
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), ((TERMINATED_1(str))?(str[1]):('\0')) 
#define STRING_LITERAL_1(str) str[0] 


#define TERMINATED_10(str) TERMINATED_9(str) && str[9] 
#define TERMINATED_9(str) TERMINATED_8(str) && str[8] 
#define TERMINATED_8(str) TERMINATED_7(str) && str[7] 
#define TERMINATED_7(str) TERMINATED_6(str) && str[6] 
#define TERMINATED_6(str) TERMINATED_5(str) && str[5] 
#define TERMINATED_5(str) TERMINATED_4(str) && str[4] 
#define TERMINATED_4(str) TERMINATED_3(str) && str[3] 
#define TERMINATED_3(str) TERMINATED_2(str) && str[2] 
#define TERMINATED_2(str) TERMINATED_1(str) && str[1] 
#define TERMINATED_1(str) str[0] 

template <char... Cs> 
struct char_pack { 
    static constexpr char const arr[sizeof...(Cs) + 1] = {Cs..., 0}; 
    static constexpr std::size_t non_zero_count = (((Cs != 0)?1:0) + ...); 
    static_assert(non_zero_count < MAX_STRING_LITERAL_LENGTH, "You need to create more macros"); 
}; 

template <char... Cs> 
constexpr char const char_pack<Cs...>::arr[sizeof...(Cs) + 1]; 

template <char... Cs> 
constexpr std::size_t char_pack<Cs...>::non_zero_count; 

template <class CP, class = void, class = std::make_index_sequence<CP::non_zero_count>> 
struct string_literal; 

template <char... Cs, std::size_t... Is> 
struct string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>> { 
    static constexpr char const s[sizeof...(Cs) + 1] = {Cs..., '\0'}; 
}; 

template <char... Cs, std::size_t... Is> 
constexpr char const string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>>::s[sizeof...(Cs) + 1]; 

template <char... Cs, std::size_t... Is> 
struct string_literal<char_pack<Cs...>, std::enable_if_t<!(Cs && ...)>, std::index_sequence<Is...>>: string_literal<char_pack<char_pack<Cs...>::arr[Is]...>> { }; 

template <const char *> 
struct foo {}; 

int main() { 
    foo<STRING_LITERAL("abcdefghij")> f; 
    static_cast<void>(f); 
} 

[live demo]

+0

Penso che il superamento della lunghezza possa essere banalmente rimosso con l'aggiunta di un'altra macro di riferimento indiretto .. '#define STRING_LITERAL_ (STR) STRING_LITERAL (sizeof (STR) - 1, STR)'. Utilizzo: 'STRING_LITERAL _ (" 123456789012 ")'. A proposito, c'è anche una funzione di boost, come menzionato in [questa risposta] (http://stackoverflow.com/a/18154638/514235). Tuttavia, la tua risposta è positiva e fornisce una soluzione già pronta. Puoi anche mettere una 'const char'" array "all'interno della' class string_literal' che semplicemente ricorderà di nuovo tutti i singoli caratteri nella stringa. – iammilind

+1

Verificato il tuo codice e sembra che tutto ciò che hai fatto sia corretto; vale a dire che dobbiamo passare esplicitamente la lunghezza come un numero letterale per far funzionare questo codice. Ho avuto l'impressione che tu abbia implementato il modo in cui è implementato 'MPLLIBS_STRING 'di boost. Sembra che ci debba essere qualche complicato trucco per far funzionare quella macro. – iammilind

+0

@iammilind Ho sentito parlare di 'BOOST_HANA_STRING' ma, per quanto ne so, non può essere utilizzato in un contesto non valutato poiché utilizza lambdas ... cercherò' MPLLIBS_STRING'. Per quanto riguarda la lunghezza non può essere scambiato con 'sizeof' perché ho usato la concatenazione per fare un semplice loop estensibile forse hanno usato un meccanismo di loop del preprocessore più complesso qui ... –

Problemi correlati