Frage C ++ Konvertieren von Integer in String zur Kompilierzeit


Ich möchte so etwas machen:

template<int N>
char* foo() {
  // return a compile-time string containing N, equivalent to doing
  // ostringstream ostr; 
  // ostr << N;
  // return ostr.str().c_str();
}

Es sieht so aus, als ob die Boost-MPL-Bibliothek dies erlauben würde, aber ich konnte nicht wirklich herausfinden, wie man es benutzt, um dies zu erreichen. Ist das möglich?


16
2017-07-15 21:37


Ursprung


Antworten:


Wenn Sie normalerweise die Nummer zur Laufzeit kennen, können Sie genauso einfach die gleiche Zeichenfolge erstellen. Das heißt, wenn Sie haben 12 In Ihrem Programm können Sie auch haben "12".

Präprozessor-Makros können auch Anführungszeichen zu Argumenten hinzufügen, sodass Sie schreiben können:

#define STRINGIFICATOR(X) #X

Dies, wann immer du schreibst STRINGIFICATOR(2), es wird "2" produzieren.

Tatsächlich aber kann ohne Makros durchgeführt werden (mit der Metaprogrammierung der Kompilierungszeit). Es ist nicht einfach, daher kann ich den genauen Code nicht angeben, aber ich kann Ihnen Ideen geben, wie es geht:

  1. Schreiben Sie eine rekursive Vorlage unter Verwendung der zu konvertierenden Zahl. Die Vorlage wird bis zum Basisfall rekursieren, dh die Zahl ist kleiner als 10.
  2. In jeder Iteration können Sie die Zahl N% 10 in ein Zeichen als T.E.D. schlägt vor, und verwenden mpl::string Erstellen der Kompilierungszeichenfolge, die das Zeichen anfügt.
  3. Du wirst am Ende einen bauen mpl::string, das hat eine statische value() Zeichenfolge.

Ich habe mir die Zeit genommen, es als eine persönliche Übung umzusetzen. Nicht schlecht am Ende:

#include <iostream>
#include <boost/mpl/string.hpp>

using namespace boost;

// Recursive case
template <bool b, unsigned N>
struct int_to_string2
{
        typedef typename mpl::push_back<
                typename int_to_string2< N < 10, N/10>::type
                                         , mpl::char_<'0' + N%10>
                                         >::type type;
};

// Base case
template <>
struct int_to_string2<true,0>
{
        typedef mpl::string<> type;
};


template <unsigned N>
struct int_to_string
{
        typedef typename mpl::c_str<typename int_to_string2< N < 10 , N>::type>::type type;
};

int
main (void)
{
        std::cout << int_to_string<1099>::type::value << std::endl;
        return 0;
}

21
2017-07-15 22:14



Ich weiß, dass diese Frage jetzt ein paar Jahre alt ist, aber ich wollte eine Lösung, die reines C ++ 11 verwendet, ohne Abhängigkeit von der Verstärkung. Also hier ist ein Code (mit Ideen von diese Antwort auf eine andere Frage):

/* IMPLEMENTATION */

/* calculate absolute value */
constexpr int abs_val (int x)
    { return x < 0 ? -x : x; }

/* calculate number of digits needed, including minus sign */
constexpr int num_digits (int x)
    { return x < 0 ? 1 + num_digits (-x) : x < 10 ? 1 : 1 + num_digits (x / 10); }

/* metaprogramming string type: each different string is a unique type */
template<char... args>
struct metastring {
    const char data[sizeof... (args)] = {args...};
};

/* recursive number-printing template, general case (for three or more digits) */
template<int size, int x, char... args>
struct numeric_builder {
    typedef typename numeric_builder<size - 1, x / 10, '0' + abs_val (x) % 10, args...>::type type;
};

/* special case for two digits; minus sign is handled here */
template<int x, char... args>
struct numeric_builder<2, x, args...> {
    typedef metastring<x < 0 ? '-' : '0' + x / 10, '0' + abs_val (x) % 10, args...> type;
};

/* special case for one digit (positive numbers only) */
template<int x, char... args>
struct numeric_builder<1, x, args...> {
    typedef metastring<'0' + x, args...> type;
};

/* convenience wrapper for numeric_builder */
template<int x>
class numeric_string
{
private:
    /* generate a unique string type representing this number */
    typedef typename numeric_builder<num_digits (x), x, '\0'>::type type;

    /* declare a static string of that type (instantiated later at file scope) */
    static constexpr type value {};

public:
    /* returns a pointer to the instantiated string */
    static constexpr const char * get ()
        { return value.data; }
};

/* instantiate numeric_string::value as needed for different numbers */
template<int x>
constexpr typename numeric_string<x>::type numeric_string<x>::value;

/* SAMPLE USAGE */

#include <stdio.h>

/* exponentiate a number, just for fun */
static constexpr int exponent (int x, int e)
    { return e ? x * exponent (x, e - 1) : 1; }

/* test a few sample numbers */
static constexpr const char * five = numeric_string<5>::get ();
static constexpr const char * one_ten = numeric_string<110>::get ();
static constexpr const char * minus_thirty = numeric_string<-30>::get ();

/* works for any constant integer, including constexpr calculations */
static constexpr const char * eight_cubed = numeric_string<exponent (8, 3)>::get ();

int main (void)
{
    printf ("five = %s\n", five);
    printf ("one ten = %s\n", one_ten);
    printf ("minus thirty = %s\n", minus_thirty);
    printf ("eight cubed = %s\n", eight_cubed);

    return 0;
}

Ausgabe:

five = 5
one ten = 110
minus thirty = -30
eight cubed = 512

14
2017-11-09 04:21



Vielleicht habe ich etwas verpasst, aber das sollte so einfach sein wie:

 #define NUM(x) #x

Leider funktioniert dies nicht mit nicht-typischen Vorlagenparametern.


3
2017-07-15 22:03



Ein Trick, den ich in Situationen gesehen habe, in denen Sie wissen, dass Sie nie eine Nummer außerhalb des Bereichs haben werden 0..9 ist das Folgende:

return '0' + N;

Auf den ersten Blick ist das ärgerlich begrenzt. Ich bin jedoch überrascht, wie oft diese Bedingung gilt.

Oh, und ich bin mir bewusst, dass das eine zurückgibt char eher als ein std::string. Dies ist eine Funktion. string ist kein integrierter Sprachtyp, daher gibt es keine Möglichkeit, einen zur Kompilierungszeit zu erstellen.


2
2017-07-15 21:47