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

发布于 2024-11-24 09:58:53 字数 285 浏览 2 评论 0原文

我想做这样的事情:

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 库可能允许这样做,但我真的不知道如何使用它来完成这个任务。这可能吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

谁的年少不轻狂 2024-12-01 09:58:53

首先,如果通常您在运行时知道该数字,则可以轻松构建相同的字符串。也就是说,如果您的程序中有 12,则也可以有 "12"

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

#define STRINGIFICATOR(X) #X

这样,每当您编写 STRINGIFICATOR(2) 时,它都会生成“2”。

然而,它实际上可以在没有宏的情况下完成(使用编译时元编程)。这并不简单,因此我无法给出确切的代码,但我可以为您提供有关如何执行此操作的想法:

  1. 使用要转换的数字编写递归模板。模板将递归直到基本情况,即数字小于 10。
  2. 在每次迭代中,您可以按照 TED 的建议将 N%10 数字转换为字符,使用mpl::string 构建附加该字符的编译时字符串。
  3. 您最终将构建一个 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;
}

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".

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".

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. 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.
  3. You'll end up building a mpl::string, that has a static value() 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;
}
苍景流年 2024-12-01 09:58:53

我知道这个问题已经有几年了,但我想要一个使用纯 C++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

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):

/* 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
感性不性感 2024-12-01 09:58:53

这可以使用 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 存储库中的 f_to_string.hpp 标头。

This can be done using C++14 without any external dependencies. The key addition to the standard is the ability to have non-trivial constexpr constructors, allowing the functionality to be contained within a simple class.

Given an integer template parameter, the constructor can carry out the integer-to-string conversion. This is stored in a member character buffer whose size is determined by an additional constexpr function. Then, a user-defined conversion provides access to the buffer:

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

Finally, a variable template (another C++14 addition) simplifies the syntax:

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

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

The functionality can be extended to support other bases (e.g. hexadecimal), wide character types, and the common container interface; I've packed this all into a single header and put it on GitHub at tcsullivan/constexpr-to-string.

With C++20, this can also be extended to support floating-point numbers. A container type is needed for the floating-point literal, which previously could not be a template parameter. See the f_to_string.hpp header in the GitHub repo for the implementation.

素手挽清风 2024-12-01 09:58:53

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

 #define NUM(x) #x

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

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.

萌梦深 2024-12-01 09:58:53

我见过的一个技巧是在你知道你永远不会有 0..9 范围之外的数字的情况下完成的,如下所示:

return '0' + N;< /code>

乍一看,这是令人恼火的限制。然而,令我惊讶的是这种情况出现了多少次。

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

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:

return '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.

月下凄凉 2024-12-01 09:58:53

另一个有用的选项:

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;

Another useful option:

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文