Подтвердить что ты не робот

С++ конвертировать целое число в строку во время компиляции

Я хочу сделать что-то вроде этого:

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

Похоже, что библиотека расширения MPL может это позволить, но я не мог понять, как ее использовать для достижения этой цели. Это возможно?

4b9b3361

Ответ 1

Прежде всего, если вы обычно знаете номер во время выполнения, вы можете легко построить одну и ту же строку. То есть, если у вас есть 12 в вашей программе, вы можете также "12".

Макросы препроцессора могут также добавлять кавычки к аргументам, поэтому вы можете написать:

#define STRINGIFICATOR(X) #X

Это, всякий раз, когда вы пишете STRINGIFICATOR(2), он произведет "2".

Однако это можно сделать без макросов (используя метапрограммирование времени компиляции). Это не просто, поэтому я не могу дать точный код, но я могу дать вам идеи о том, как это сделать:

  • Напишите рекурсивный шаблон с использованием числа, которое нужно преобразовать. Шаблон будет возвращаться до базового случая, то есть число меньше 10.
  • На каждой итерации вы можете преобразовать символ N% 10 в символ как T.E.D. предлагает и использует mpl::string для построения строки времени компиляции, которая добавляет этот символ.
  • В итоге вы создадите mpl::string со статической строкой value().

Я потратил время, чтобы реализовать его как личное упражнение. Неплохо в конце:

#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;
}

Ответ 2

Я знаю, что этот вопрос уже несколько лет, но мне нужно решение, использующее чистый С++ 11, без зависимости от boost. Итак, вот какой код (с идеями, заимствованными из этого ответа на другой вопрос):

/* 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;
}

Вывод:

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

Ответ 3

Возможно, я что-то пропустил, но это должно быть так же просто, как:

 #define NUM(x) #x

К сожалению, это не будет работать с параметрами шаблона не-типа.

Ответ 4

Один трюк, который я видел в ситуациях, когда вы знаете, что у вас никогда не будет номера за пределами диапазона 0..9, будет следующее:

return '0' + N;

Сначала краснеть раздражает. Однако я удивлен, сколько раз это условие выполняется.

О, и я знаю, что это возвращает char, а не std::string. Это особенность. string не является встроенным языком, поэтому нет способа создать его во время компиляции.

Ответ 5

Еще одна полезная опция:

template <int i, bool gTen>
struct UintToStrImpl
{
   UintToStrImpl<i / 10, (i > 99)> c;
   const char c0 = '0' + i % 10;
};

template <int i>
struct UintToStrImpl <i, false> 
{ 
   const char c0 = '0' + i; 
};

template <int i, bool sign>
struct IntToStrImpl
{
   UintToStrImpl<i, (i > 9)> num_;
};

template <int i>
struct IntToStrImpl <i, false>
{
   const char sign = '-';
   UintToStrImpl<-i, (-i > 9)> num_;
};

template <int i>
struct IntToStr
{
   IntToStrImpl<i, (i >= 0)> num_;
   const char end = '\0';
   const char* str = (char*)this;
};

std::cout << IntToStr<-15450>().str;