我想做这样的事情:
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库可能允许这样但我无法弄清楚如何使用它来实现这一点。这可能吗?
答案 0 :(得分:22)
首先,如果通常你知道运行时的数字,你就可以轻松地构建相同的字符串。也就是说,如果您的计划中有12
,则还可以"12"
。
预处理器宏也可以为参数添加引号,因此您可以编写:
#define STRINGIFICATOR(X) #X
这样,无论何时写STRINGIFICATOR(2)
,它都会产生“2”。
然而,实际上可以在没有宏的情况下完成(使用编译时元编程)。这不是直截了当的,所以我不能给出确切的代码,但我可以给你如何做的想法:
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;
}
答案 1 :(得分:17)
我知道这个问题现在已经有几年了,但是我想要一个使用纯C ++ 11的解决方案,没有增强依赖性。所以这里有一些代码(从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
答案 2 :(得分:4)
也许我错过了一些东西,但这应该简单:
#define NUM(x) #x
不幸的是,这不适用于非类型模板参数。
答案 3 :(得分:3)
我见过的一个技巧是你知道事实上你永远不会有0..9
范围之外的数字,如下所示:
return '0' + N;
乍一看,这令人讨厌。但是,我很惊讶这种情况多少次。
哦,我知道这会返回char
而不是std::string
。这是一个功能。 string
不是内置语言类型,因此无法在编译时创建一个。
答案 4 :(得分:2)
另一个有用的选项:
template <int i, bool gTen>
struct UintToStrImpl
{
UintToStrImpl<i / 10, (i > 90)> 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;
答案 5 :(得分:0)
现在可以在C ++ 17及更高版本中实现此操作,而无需任何外部依赖性,并且转换完全在编译时完成。我已将代码打包在一个简短的头文件中,请在GitHub上查看constexpr-to-string。该代码还支持不同的基数和字符宽度。
使用示例:
const char *number = to_string<2147483648999954564, 16>; // produces "1DCD65003B9A1884"
puts(number);
puts(to_string<-42>); // produces "-42"
puts(to_string<30, 2>); // produces "11110"
// Below requires C++20
puts(f_to_string<3.1415926>); // Defaults to 5-point precision: "3.14159"
puts(f_to_string<{3.1415926, 7}>); // Specify precision: "3.1415926"