我问是否可以转换如下函数:
string escape(const string& s)
{
string r = "";
for(char c:s)
{
if(c=='"')
{
r+='\\'; r+=c;
}
else r+=c;
}
return r;
}
变成这样的东西:
constexpr const char* escape(const char* s) { ??? }
这是一个说明一般 constexpr array[N]->array[M] 方法的示例,其中 M 取决于 array[N] 的内容。该解决方案可能有利于某些情况。我正在编译一个平台,其中每一位 RAM 都很重要,并且我试图将尽可能多的数据推送到只读部分,同时保持代码清晰度(并使用 C++ 语法制作)。我听说 C++20 可能可以,但我不考虑切换到全新标准只是为了解决这个特定问题,所以我想知道我们是否可以利用我们已经拥有的东西来实现。
我尝试的解决方案依赖于输出的上限。如果输入有 N 个字符,则输出最多为 2*N 大小(最坏情况是全引号字符串
""..."
->\"\"...\"
)。代码大致如下:
template<int N>
struct static_str { char data[N]; };
template<int N>
constexpr static_str<2*N> escape(static_str<N> s)
{
static_str<2*N> r;
int k=0;
for(int i=0;i<N;i++)
{
if(s.data[i]=='"') { r.data[k++]='\\'; }
r.data[k++] = s.data[i];
}
return r;
}
但是,考虑到输出的平均长度,很明显这种方法会浪费内存。我有一些想法来解决这个问题,但由于一些限制或缺少拼图而失败了:
您可以预先计算输出的长度(
expected_len(s) := strlen(s)+<n.o. quotes>
),但是不允许您创建static_str<expected_len(s)>
类型的变量 - “expected_len(s) 不是常量”。
假设我使用上面的
escape(s)
函数来获得2*N大小的输出,我想我可以修剪数组以保留其第一个元素,直到它匹配expected_len(s)
。但我不知道如何在不与模板机制冲突的情况下做到这一点。
如果输出大小不容易预测怎么办?
一种可能性是首先计算
constexpr
目标数组的大小。
如果有的话,我们可以分配一个所需大小的
std::array
,并根据要求对其进行初始化。但是,所需的大小无法作为函数的参数提供,因为它不能用作 constexpr
,因此我们可以将其作为非类型模板参数提供。
为了保持 API 简单(即使用输入字符串对
escape
进行一次调用),我们必须(不幸的是)依赖预处理器。
这是代码:
#include <array>
// we get the size and the occurrences number in the provided string
constexpr auto info (const char* str, char c)
{
int size=0;
int found=0;
while (*str)
{
if(*str==c) { found++; }
str++;
size++;
}
return std::make_pair(size,found);
}
// we provide the size and occurs number as non-type template parameters
template<int size, int occurs>
constexpr auto escape_impl (const char* s, char c)
{
// we can allocate the result array with the required size
std::array<char,size+occurs> r {};
int k=0;
for(int i=0;i<size;i++)
{
if(s[i]==c) { r[k++]='\\'; }
r[k++] = s[i];
}
return r;
}
// We can't provide size and occurs number as function parameters
// because they would not be constexpr. So we have to provide them
// as non-type template arguments.
#define escape(s,c) escape_impl<info(s,c).first, info(s,c).second> (s,c)
static_assert (escape ("abab", 'b').size() == 6);
static_assert (escape ("abab", 'b') [0] == 'a' );
static_assert (escape ("abab", 'b') [1] == '\\');
static_assert (escape ("abab", 'b') [2] == 'b' );
static_assert (escape ("abab", 'b') [3] == 'a' );
static_assert (escape ("abab", 'b') [4] == '\\');
static_assert (escape ("abab", 'b') [5] == 'b' );
int main()
{
}