2012-01-11 17 views
14

In C++, è possibile generare un numero intero da una stringa letterale utilizzando solo le funzioni di compilazione?Come posso generare un intero da un valore letterale stringa in fase di compilazione?

Ad esempio, se tutto ciò che abbiamo è il letterale "6", c'è un modo per usarlo come argomento modello, come std::array<GET_INTEGER("6")> a;?

che so di constexpr tecniche basati, come ad esempio:

template <int N> constexpr char get_char(const char s[N], int n) { 
    return s[n]; 
} 

Tuttavia constexpr non è ancora pronto sulla maggior parte dei compilatori, quindi sono alla ricerca di soluzioni che utilizzano probabilmente macro e TMP.

È solo per la sperimentazione, quindi idee pazze sono ben accette.

+0

Che cosa significa "maggior parte dei compilatori"? Se "la maggior parte dei compilatori" include quelli che sono probabilmente i compilatori più usati (gcc e Visual C++), la risposta è che non può essere fatto. –

+4

Perché non scrivere solo 6, senza le virgolette? – littleadv

+0

Potrebbe funzionare se usi caratteri letterali 'GET_INTEGER ('6', '7', '8')', ma non credo che funzionino le stringhe letterali. – kennytm

risposta

4

Apparentemente gcc permette "abcd"[3] essere interpretato come 'd', che permette di far funzionare tutto questo (almeno su g ++ - 4.6 e 4.7):

#include <boost/preprocessor/repetition/enum.hpp> 

template <const char... characters> 
struct GetIntegerTemplate; 

template <const char head, const char... rest> 
struct GetIntegerTemplate<head, rest...> 
{ 
    typedef GetIntegerTemplate<rest...> Prev; 
    enum 
    { 
     power = Prev::power * 10, 
     value = (head - '0') * Prev::power + Prev::value 
    }; 
}; 

template <> 
struct GetIntegerTemplate<> 
{ 
    enum 
    { 
     power = 1, 
     value = 0 
    }; 
}; 

#define GET_NTH_CHARACTER(z, n, data) data[n] 
#define GET_INTEGER(length, the_string) GetIntegerTemplate<BOOST_PP_ENUM(length, GET_NTH_CHARACTER, the_string)>::value 

int main() 
{ 
    static_assert(GET_INTEGER(7, "1234567") == 1234567, "oops"); 
} 

Ma non si compila il clang, che dice "non L'argomento del tipo type di tipo "const char" non è un'espressione costante integrata ".


ciò che realmente fa è quello di abbattere la stringa letterale "1234567" in una lista di letterali carattere '1', '2', '3', '4', '5', '6', '7'. L'istanziazione

GetIntegerTemplate<'1', '2', '3', '4', '5', '6', '7'>::value 

viene invocato per trasformare la lista in un numero intero 1234567. La stringa → char passo letterale può comportare un comportamento non standard che non può funzionare al di fuori di g ++ (cioè peggio constexpr ☺), ma che GetIntegerTemplate<...>::value è portatile.

+0

Forse lanciando su 'int'? Al momento non ho LLVM, ma potrebbe valere la pena di provarlo. – slaphappy

+0

@kbok: Il problema non è il tipo, ma che "" 1234567 "[0]' in clang non è considerato una costante. Questa è una estensione gcc, ma non riesco a trovarlo da nessuna parte menzionando questo. – kennytm

+0

Vedo, ho pensato che il problema era che non era "integrale". Anche a MSVC non piace ("argomento template non valido per" foo ", espressione della costante attesa in fase di compilazione"); è un peccato che solo gcc lo supporti, mentre è l'unico compilatore che supporta constexpr. – slaphappy

-1

Forse?

template<int C> 
struct get_char 
{ 
    static const int value = C - 48; 
}; 

static_assert(get_char<'0'>::value == 0, ""); 
2

(rinnovo di da another answer of mine)

Se non ti dispiace cambiare la definizione concettuale di 'stringa letterale' da esempio
"425897" a '4258','97', quindi è possibile utilizzare Boost. MPL s' boost::mpl::string<> per raggiungere questo obiettivo:

#include <cstddef> 
#include <boost/type_traits/is_integral.hpp> 
#include <boost/type_traits/is_same.hpp> 
#include <boost/type_traits/is_signed.hpp> 
#include <boost/mpl/and.hpp> 
#include <boost/mpl/assert.hpp> 
#include <boost/mpl/char.hpp> 
#include <boost/mpl/contains.hpp> 
#include <boost/mpl/end.hpp> 
#include <boost/mpl/eval_if.hpp> 
#include <boost/mpl/find_if.hpp> 
#include <boost/mpl/fold.hpp> 
#include <boost/mpl/front.hpp> 
#include <boost/mpl/identity.hpp> 
#include <boost/mpl/integral_c.hpp> 
#include <boost/mpl/minus.hpp> 
#include <boost/mpl/negate.hpp> 
#include <boost/mpl/next.hpp> 
#include <boost/mpl/not.hpp> 
#include <boost/mpl/pair.hpp> 
#include <boost/mpl/placeholders.hpp> 
#include <boost/mpl/plus.hpp> 
#include <boost/mpl/pop_front.hpp> 
#include <boost/mpl/push_back.hpp> 
#include <boost/mpl/reverse_fold.hpp> 
#include <boost/mpl/size_t.hpp> 
#include <boost/mpl/string.hpp> 
#include <boost/mpl/times.hpp> 
#include <boost/mpl/vector.hpp> 

namespace details 
{ 
    namespace mpl = boost::mpl; 

    typedef mpl::vector10< 
     mpl::char_<'0'>, mpl::char_<'1'>, mpl::char_<'2'>, mpl::char_<'3'>, 
     mpl::char_<'4'>, mpl::char_<'5'>, mpl::char_<'6'>, mpl::char_<'7'>, 
     mpl::char_<'8'>, mpl::char_<'9'> 
    > valid_chars_t; 

    template<typename IntegralT, typename PowerT> 
    struct power_of_10; 

    template<typename IntegralT, std::size_t Power> 
    struct power_of_10<IntegralT, mpl::size_t<Power> > : mpl::times< 
     power_of_10<IntegralT, mpl::size_t<Power - 1u> >, 
     mpl::integral_c<IntegralT, 10> 
    > { }; 

    template<typename IntegralT> 
    struct power_of_10<IntegralT, mpl::size_t<1u> > 
     : mpl::integral_c<IntegralT, 10> 
    { }; 

    template<typename IntegralT> 
    struct power_of_10<IntegralT, mpl::size_t<0u> > 
     : mpl::integral_c<IntegralT, 1> 
    { }; 

    template<typename IntegralT, typename StringT> 
    struct is_negative : mpl::and_< 
     boost::is_signed<IntegralT>, 
     boost::is_same< 
      typename mpl::front<StringT>::type, 
      mpl::char_<'-'> 
     > 
    > { }; 

    template<typename IntegralT, typename StringT> 
    struct extract_actual_string : mpl::eval_if< 
     is_negative<IntegralT, StringT>, 
     mpl::pop_front<StringT>, 
     mpl::identity<StringT> 
    > { }; 

    template<typename ExtractedStringT> 
    struct check_valid_characters : boost::is_same< 
     typename mpl::find_if< 
      ExtractedStringT, 
      mpl::not_<mpl::contains<valid_chars_t, mpl::_> > 
     >::type, 
     typename mpl::end<ExtractedStringT>::type 
    > { }; 

    template<typename ExtractedStringT> 
    struct pair_digit_with_power : mpl::first< 
     typename mpl::reverse_fold< 
      ExtractedStringT, 
      mpl::pair<mpl::vector0<>, mpl::size_t<0> >, 
      mpl::pair< 
       mpl::push_back< 
        mpl::first<mpl::_1>, 
        mpl::pair<mpl::_2, mpl::second<mpl::_1> > 
       >, 
       mpl::next<mpl::second<mpl::_1> > 
      > 
     >::type 
    > { }; 

    template<typename IntegralT, typename ExtractedStringT> 
    struct accumulate_digits : mpl::fold< 
     typename pair_digit_with_power<ExtractedStringT>::type, 
     mpl::integral_c<IntegralT, 0>, 
     mpl::plus< 
      mpl::_1, 
      mpl::times< 
       mpl::minus<mpl::first<mpl::_2>, mpl::char_<'0'> >, 
       power_of_10<IntegralT, mpl::second<mpl::_2> > 
      > 
     > 
    > { }; 

    template<typename IntegralT, typename StringT> 
    class string_to_integral_impl 
    { 
     BOOST_MPL_ASSERT((boost::is_integral<IntegralT>)); 

     typedef typename extract_actual_string< 
      IntegralT, 
      StringT 
     >::type ExtractedStringT; 
     BOOST_MPL_ASSERT((check_valid_characters<ExtractedStringT>)); 

     typedef typename accumulate_digits< 
      IntegralT, 
      ExtractedStringT 
     >::type ValueT; 

    public: 
     typedef typename mpl::eval_if< 
      is_negative<IntegralT, StringT>, 
      mpl::negate<ValueT>, 
      mpl::identity<ValueT> 
     >::type type; 
    }; 
} 

template<typename IntegralT, typename StringT> 
struct string_to_integral2 
    : details::string_to_integral_impl<IntegralT, StringT>::type 
{ }; 

template<typename IntegralT, int C0, int C1 = 0, int C2 = 0, 
    int C3 = 0, int C4 = 0, int C5 = 0, int C6 = 0, int C7 = 0> 
struct string_to_integral : string_to_integral2< 
    IntegralT, 
    boost::mpl::string<C0, C1, C2, C3, C4, C5, C6, C7> 
> { }; 

Uso sarà simile:

int i = string_to_integral<int, '4258','97'>::value; 
// or 
typedef boost::mpl::string<'4258','97'> str_t; 
unsigned j = string_to_integral2<unsigned, str_t>::value; 

supporto per i numeri negativi è implementato, il supporto per il rilevamento di overflow non è (ma il compilatore probabilmente dare un avvertimento) .

+0

Questo è interessante, ma cosa è "4258"? – slaphappy

+0

@kbok: "'4258', '97'' è il modo di rappresentare la stringa letterale' "425897" 'in un modo utilizzabile con' boost :: mpl :: string <> '. – ildjarn

+0

@ildjarn: tutti questi boost sono necessari per quel codice di esempio? Penso di ricordare perché odiavo usare boost ... –

-1

Non sono sicuro che sia possibile, ma è qualcosa che potresti provare.

È possibile decrementare il valore di una cifra di carattere per "0" per ottenere tale valore in valori numerici.

come:

char a = '5'; 
int num = a - '0'; 

che avrebbe risolto il problema per una cifra.

Per risolvere un numero con più cifre (come "12345"), è necessario eseguire il ciclo di tutte le cifre e sommare i risultati (ognuno moltiplicato per 10^pos).

Questo sarebbe facile da eseguire in tempo di esecuzione, ma in fase di compilazione non è così semplice.

"Ricompilazione del tempo di compilazione" potrebbe essere tuo amico qui. Ad essere onesti, non potevo pensare a nessuna soluzione che lo usasse, ma potresti trovarne uno.

Buona fortuna!

Problemi correlati