C++17 constexpr 函数将数组转换为不同大小的数组

问题描述 投票:0回答:1

我问是否可以转换如下函数:

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

但是,考虑到输出的平均长度,很明显这种方法会浪费内存。我有一些想法来解决这个问题,但由于一些限制或缺少拼图而失败了:

  1. 您可以预先计算输出的长度(

    expected_len(s) := strlen(s)+<n.o. quotes>
    ),但是不允许您创建
    static_str<expected_len(s)>
    类型的变量 - “expected_len(s) 不是常量”。

  2. 假设我使用上面的

    escape(s)
    函数来获得2*N大小的输出,我想我可以修剪数组以保留其第一个元素,直到它匹配
    expected_len(s)
    。但我不知道如何在不与模板机制冲突的情况下做到这一点。

  3. 如果输出大小不容易预测怎么办?

c++ c++17 constexpr
1个回答
0
投票

一种可能性是首先计算

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() 
{
}

演示

© www.soinside.com 2019 - 2024. All rights reserved.