C ++在编译时将整数转换为字符串

时间:2021-05-04 16:24:45

I want to do something like this:

我想做这样的事情:

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

It seems like the boost MPL library might allow this but I couldn't really figure out how to use it to accomplish this. Is this possible?

似乎boost MPL库可能允许这样但我无法弄清楚如何使用它来实现这一目标。这可能吗?

4 个解决方案

#1


21  

First of all, if usually you know the number at run time, you can as easily build the same string. That is, if you have 12 in your program, you can have also "12".

首先,如果通常你知道运行时的数字,你可以轻松地构建相同的字符串。也就是说,如果你的程序中有12个,你也可以有“12”。

Preprocessor macros can add also quotes to arguments, so you can write:

预处理器宏可以在参数中添加引号,因此您可以编写:

#define STRINGIFICATOR(X) #X

This, whenever you write STRINGIFICATOR(2), it will produce "2".

这样,无论何时编写STRINGIFICATOR(2),它都会产生“2”。

However, it actually can be done without macros (using compile-time metaprogramming). It is not straightforward, so I cannot give the exact code, but I can give you ideas on how to do it:

但是,它实际上可以在没有宏的情况下完成(使用编译时元编程)。这不是直截了当的,所以我不能给出确切的代码,但我可以给你如何做的想法:

  1. Write a recursive template using the number to be converted. The template will recurse till the base case, that is, the number is less than 10.
  2. 使用要转换的数字编写递归模板。模板将递归到基本情况,即数字小于10。

  3. In each iteration, you can have the N%10 digit to be converted into a character as T.E.D. suggests, and using mpl::string to build the compile-time string that appends that character.
  4. 在每次迭代中,您可以将N%10位数转换为T.E.D.建议,并使用mpl :: string来构建追加该字符的编译时字符串。

  5. You'll end up building a mpl::string, that has a static value() string.
  6. 您最终将构建一个具有静态值()字符串的mpl :: string。

I took the time to implement it as a personal exercise. Not bad at the end:

我花时间将其作为个人练习来实施。最后还不错:

#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


16  

I know this question is a few years old now, but I wanted a solution using pure C++11, with no boost dependency. So here is some code (with ideas borrowed from this answer to a different question):

我知道这个问题现在已经有几年了,但是我想要一个使用纯C ++ 11的解决方案,没有增强依赖性。所以这里有一些代码(从这个问题的答案借用了另一个问题):

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

Output:

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

#3


3  

Maybe i missed something, but this should be as simple as:

也许我错过了一些东西,但这应该是这样简单:

 #define NUM(x) #x

Unfortunately this won't work with non-type template parameters.

不幸的是,这不适用于非类型模板参数。

#4


2  

One trick I've seen done in situations where you know for a fact you will never have a number outside the range 0..9 is the following:

在你知道事实上你永远不会有超出0..9范围的数字的情况下我见过的一个技巧如下:

return '0' + N;

返回'0'+ N;

At first blush this is annoyingly limited. However, I'm surprised how many times this condition holds.

乍一看,这令人讨厌。但是,我很惊讶这种情况有多少次。

Oh, and I'm aware this returns a char rather than a std::string. This is a feature. string isn't a built in language type, so there's no way to create one at compile time.

哦,我知道这会返回一个char而不是一个std :: string。这是一个功能。 string不是内置语言类型,因此无法在编译时创建一个。

#1


21  

First of all, if usually you know the number at run time, you can as easily build the same string. That is, if you have 12 in your program, you can have also "12".

首先,如果通常你知道运行时的数字,你可以轻松地构建相同的字符串。也就是说,如果你的程序中有12个,你也可以有“12”。

Preprocessor macros can add also quotes to arguments, so you can write:

预处理器宏可以在参数中添加引号,因此您可以编写:

#define STRINGIFICATOR(X) #X

This, whenever you write STRINGIFICATOR(2), it will produce "2".

这样,无论何时编写STRINGIFICATOR(2),它都会产生“2”。

However, it actually can be done without macros (using compile-time metaprogramming). It is not straightforward, so I cannot give the exact code, but I can give you ideas on how to do it:

但是,它实际上可以在没有宏的情况下完成(使用编译时元编程)。这不是直截了当的,所以我不能给出确切的代码,但我可以给你如何做的想法:

  1. Write a recursive template using the number to be converted. The template will recurse till the base case, that is, the number is less than 10.
  2. 使用要转换的数字编写递归模板。模板将递归到基本情况,即数字小于10。

  3. In each iteration, you can have the N%10 digit to be converted into a character as T.E.D. suggests, and using mpl::string to build the compile-time string that appends that character.
  4. 在每次迭代中,您可以将N%10位数转换为T.E.D.建议,并使用mpl :: string来构建追加该字符的编译时字符串。

  5. You'll end up building a mpl::string, that has a static value() string.
  6. 您最终将构建一个具有静态值()字符串的mpl :: string。

I took the time to implement it as a personal exercise. Not bad at the end:

我花时间将其作为个人练习来实施。最后还不错:

#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


16  

I know this question is a few years old now, but I wanted a solution using pure C++11, with no boost dependency. So here is some code (with ideas borrowed from this answer to a different question):

我知道这个问题现在已经有几年了,但是我想要一个使用纯C ++ 11的解决方案,没有增强依赖性。所以这里有一些代码(从这个问题的答案借用了另一个问题):

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

Output:

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

#3


3  

Maybe i missed something, but this should be as simple as:

也许我错过了一些东西,但这应该是这样简单:

 #define NUM(x) #x

Unfortunately this won't work with non-type template parameters.

不幸的是,这不适用于非类型模板参数。

#4


2  

One trick I've seen done in situations where you know for a fact you will never have a number outside the range 0..9 is the following:

在你知道事实上你永远不会有超出0..9范围的数字的情况下我见过的一个技巧如下:

return '0' + N;

返回'0'+ N;

At first blush this is annoyingly limited. However, I'm surprised how many times this condition holds.

乍一看,这令人讨厌。但是,我很惊讶这种情况有多少次。

Oh, and I'm aware this returns a char rather than a std::string. This is a feature. string isn't a built in language type, so there's no way to create one at compile time.

哦,我知道这会返回一个char而不是一个std :: string。这是一个功能。 string不是内置语言类型,因此无法在编译时创建一个。