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

oipij1gg  于 2023-02-10  发布在  其他
关注(0)|答案(6)|浏览(173)

我想做这样的事情:

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

看起来boost MPL库可能允许这样做,但我真的不知道如何使用它来完成这个。这可能吗?

wyyhbhjk

wyyhbhjk1#

首先,如果你通常在运行时知道这个数字,你可以很容易地构建相同的字符串,也就是说,如果你的程序中有12,你也可以有"12"
预处理器宏还可以给参数加上引号,因此可以编写:

#define STRINGIFICATOR(X) #X

这个,每当你写STRINGIFICATOR(2),它就会产生“2”。
然而,实际上不用宏(使用编译时元编程)就可以完成,这并不简单,所以我不能给予确切的代码,但我可以给你一些想法:
1.用要转换的数字写一个递归模板,这个模板会递归到基本情况,也就是数字小于10。
1.在每次迭代中,可以将N%10数字转换为T.E.D.建议的字符,使用mpl::string构建附加该字符的编译时字符串 * 和 *。
1.您最终将构建一个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;
}
jmo0nnb3

jmo0nnb32#

我知道这个问题已经有几年的历史了,但是我想要一个使用纯C++11的解决方案,没有boost依赖性。所以这里有一些代码(借鉴了this answer to a different question的思想):

/* 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
pxq42qpu

pxq42qpu3#

这可以用C++14来实现,不需要任何外部依赖。标准的关键增加是拥有非平凡的constexpr构造函数的能力,允许将功能包含在一个简单的类中。
给定一个整数模板参数,构造函数可以执行整数到字符串的转换。它存储在一个成员字符缓冲区中,该缓冲区的大小由一个附加的constexpr函数确定。然后,用户定义的转换提供对缓冲区的访问:

#include <cstdint>

template<std::intmax_t N>
class to_string_t {

    constexpr static auto buflen() noexcept {
        unsigned int len = N > 0 ? 1 : 2;
        for (auto n = N; n; len++, n /= 10);
        return len;
    }

    char buf[buflen()] = {};

public:
    constexpr to_string_t() noexcept {
        auto ptr = buf + buflen();
        *--ptr = '\0';

        if (N != 0) {
            for (auto n = N; n; n /= 10)
                *--ptr = "0123456789"[(N < 0 ? -1 : 1) * (n % 10)];
            if (N < 0)
                *--ptr = '-';
        } else {
            buf[0] = '0';
        }
    }

    constexpr operator const char *() const { return buf; }
};

最后,一个变量模板(C++14的另一个附加功能)简化了语法:

template<std::intmax_t N>
constexpr to_string_t<N> to_string;

puts(to_string<62017>); // prints "62017"

该功能可以被扩展以支持其他基数(例如十六进制)、宽字符类型和公共容器接口;我把这些打包到一个头文件中,放在GitHub上的tcsullivan/constexpr-to-string
在C++20中,这也可以扩展为支持浮点数。浮点文字需要一个容器类型,以前不能作为模板参数。具体实现见GitHub repo中的f_to_string.hpp头文件。

6vl6ewon

6vl6ewon4#

也许我错过了什么,但这应该是简单的:

#define NUM(x) #x

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

q3aa0525

q3aa05255#

在你知道你永远不会有一个超出0..9范围的数字的情况下,我看到过这样一个技巧:
return '0' + N;
乍看之下,这是令人烦恼的限制,然而,我很惊讶有多少次这种情况成立。
哦,我知道这返回了一个char而不是std::string,这是一个特性,string不是一个内置的语言类型,所以没有办法在编译时创建一个。

ni65a41a

ni65a41a6#

另一个有用的选项:

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;

相关问题