C++ 从某字符串文字和int模板参数的串联创建constexpr C字符串
我有一个带有int模板参数的类。在某些情况下,我希望它输出错误消息。此消息应该是来自某些固定文本和模板参数的连接字符串。出于性能原因,我希望避免每次错误发生时都在运行时生成此字符串,理论上,字符串文本和模板参数在编译时都是已知的。所以我在寻找一种可能,将其声明为constexpr 代码示例:C++ 从某字符串文字和int模板参数的串联创建constexpr C字符串,c++,string,constexpr,C++,String,Constexpr,我有一个带有int模板参数的类。在某些情况下,我希望它输出错误消息。此消息应该是来自某些固定文本和模板参数的连接字符串。出于性能原因,我希望避免每次错误发生时都在运行时生成此字符串,理论上,字符串文本和模板参数在编译时都是已知的。所以我在寻找一种可能,将其声明为constexpr 代码示例: template<int size> class MyClass { void onError() { // obviously won't work but e
template<int size>
class MyClass
{
void onError()
{
// obviously won't work but expressing the concatenation like
// it would be done with a std::string for clarification
constexpr char errMsg[] = "Error in MyClass of size " + std::to_string (size) + ": Detailed error description\n";
outputErrorMessage (errMsg);
}
}
模板
类MyClass
{
void onError()
{
//显然不起作用,但表达串联方式如下
//它将使用std::字符串来进行澄清
constexpr char errMsg[]=“大小为“+std::to_string(size)+”的MyClass中的错误:详细错误描述\n”;
输出消息(errMsg);
}
}
不幸的是,您的选择有限。C++不允许字符串文本用于模板参数,即使它确实存在,在模板插入之前,文字连接也会发生在预处理器中。您需要一些可怕的逐字符数组定义和一些手动int-to-char转换。太可怕了,我不能让自己去尝试,太可怕了,老实说,我建议不要打扰。我会在运行时生成它,尽管只生成一次(您可以将errMsg
设置为函数-static
std::string
)。在导栓上测试:
#include <string_view>
#include <array>
#include <algorithm>
void outputErrorMessage(std::string_view s);
template<int N> struct cint
{
constexpr int value() const { return N; }
};
struct concat_op {};
template<std::size_t N>
struct fixed_string
{
constexpr static std::size_t length() { return N; }
constexpr static std::size_t capacity() { return N + 1; }
template<std::size_t L, std::size_t R>
constexpr fixed_string(concat_op, fixed_string<L> l, fixed_string<R> r)
: fixed_string()
{
static_assert(L + R == N);
overwrite(0, l.data(), L);
overwrite(L, r.data(), R);
}
constexpr fixed_string()
: buffer_ { 0 }
{
}
constexpr fixed_string(const char (&source)[N + 1])
: fixed_string()
{
do_copy(source, buffer_.data());
}
static constexpr void do_copy(const char (&source)[N + 1], char* dest)
{
for(std::size_t i = 0 ; i < capacity() ; ++i)
dest[i] = source[i];
}
constexpr const char* data() const
{
return buffer_.data();
}
constexpr const char* data()
{
return buffer_.data();
}
constexpr void overwrite(std::size_t where, const char* source, std::size_t len)
{
auto dest = buffer_.data() + where;
while(len--)
*dest++ = *source++;
}
operator std::string_view() const
{
return { buffer_.data(), N };
}
std::array<char, capacity()> buffer_;
};
template<std::size_t N> fixed_string(const char (&)[N]) -> fixed_string<N - 1>;
template<std::size_t L, std::size_t R>
constexpr auto operator+(fixed_string<L> l, fixed_string<R> r) -> fixed_string<L + R>
{
auto result = fixed_string<L + R>(concat_op(), l , r);
return result;
};
template<int N>
constexpr auto to_string()
{
auto log10 = []
{
if constexpr (N < 10)
return 1;
else if constexpr(N < 100)
return 2;
else if constexpr(N < 1000)
return 3;
else
return 4;
// etc
};
constexpr auto len = log10();
auto result = fixed_string<len>();
auto pow10 = [](int n, int x)
{
if (x == 0)
return 1;
else while(x--)
n *= 10;
return n;
};
auto to_char = [](int n)
{
return '0' + char(n);
};
int n = N;
for (int i = 0 ; i < len ; ++i)
{
auto pow = pow10(10, i);
auto digit = to_char(n % 10);
if (n == 0 && i != 0) digit = ' ';
result.buffer_[len - i - 1] = digit;
n /= 10;
}
return result;
}
template<int size>
struct MyClass
{
void onError()
{
// obviously won't work but expressing the concatenation like
// it would be done with a std::string for clarification
static const auto errMsg = fixed_string("Error in MyClass of size ") + to_string<size>() + fixed_string(": Detailed error description\n");
outputErrorMessage (errMsg);
}
};
int main()
{
auto x = MyClass<10>();
x.onError();
}
#包括
#包括
更新:
没有必要呼叫pow10。它是可以删除的死代码。使用静态常量将只允许计算一次(但在运行时):
最后:
template<int size>
class MyClass
{
public:
void onError()
{
constexpr auto errMsg = concat(to_array("Error in MyClass of size "),
to_char_array<size>(),
to_array(": Detailed error description\n"),
std::array<char, 1>{{0}});
std::cout << errMsg.data();
}
};
模板
类MyClass
{
公众:
void onError()
{
constexpr auto errMsg=concat(到数组(“大小为MyClass的错误”),
到_char_array(),
到_数组(“:详细错误描述\n”),
std::数组{{0});
std::cout在你的应用程序中输出错误消息真的是一个热门路径吗?很好的观点。可能没有那么热门,但是错误是从实时处理循环中抛出的,所以部分是肯定的。但即使不是,我还是想知道答案,只是为了学习。希望编译器开发人员很快实现,所以这可以通过tem解决你的字符串是“MyClass中的错误…”常量?我的意思是,你打算使用另一个字符串,还是只使用这个?因为如果你只有这个字符串,这个问题已经很容易解决了。不确定我是否正确理解你的问题@geza,但是这个类的字符串总是相同的,只有模板参数和字符串中包含的数字可以更改您可以在constexpr中计算log10:constexpr long long log(long long n,long long base=2){return((n
thenconstexpr auto log10=log(n,10)
@KamilCuk很好的解决方案。@jarod42回滚到static const-在这种情况下,gcc使用常量折叠比constepr做得更好。@RichardHodges:最初,它是为了删除过时的注释:)是static
使事情发生不同的作用,顺便说一句,而不是const
与constepr
…肯定是因为string_view
。我读取了constexpr以表明它确实是在编译时计算的。@Jarod42值得一看godbolt上的输出。clang处理编译时字符串操作的效果总是比gcc好。
template<int size>
class MyClass
{
void onError()
{
static const std::string = "Error in MyClass of size "
+ std::to_string(size)
+ ": Detailed error description\n";
outputErrorMessage(errMsg);
}
};
template <std::size_t N>
constexpr std::size_t count_digit() {
if (N == 0) {
return 1;
}
std::size_t res = 0;
for (int i = N; i; i /= 10) {
++res;
}
return res;
}
template <std::size_t N>
constexpr auto to_char_array()
{
constexpr auto digit_count = count_digit<N>();
std::array<char, digit_count> res{};
auto n = N;
for (std::size_t i = 0; i != digit_count; ++i) {
res[digit_count - 1 - i] = static_cast<char>('0' + n % 10);
n /= 10;
}
return res;
}
template <std::size_t N>
constexpr std::array<char, N - 1> to_array(const char (&a)[N])
{
std::array<char, N - 1> res{};
for (std::size_t i = 0; i != N - 1; ++i) {
res[i] = a[i];
}
return res;
}
template <std::size_t ...Ns>
constexpr std::array<char, (Ns + ...)> concat(const std::array<char, Ns>&... as)
{
std::array<char, (Ns + ...)> res{};
std::size_t i = 0;
auto l = [&](const auto& a) { for (auto c : a) {res[i++] = c;} };
(l(as), ...);
return res;
}
template<int size>
class MyClass
{
public:
void onError()
{
constexpr auto errMsg = concat(to_array("Error in MyClass of size "),
to_char_array<size>(),
to_array(": Detailed error description\n"),
std::array<char, 1>{{0}});
std::cout << errMsg.data();
}
};