如何在编译时从 string_view 中删除子字符串?

Posted

技术标签:

【中文标题】如何在编译时从 string_view 中删除子字符串?【英文标题】:How to remove a substring from a string_view at compile time? 【发布时间】:2019-12-30 15:07:32 【问题描述】:

我创建了一个名为 DBG 的宏,它打印一个表达式本身及其计算结果。所以DBG(5+1) 应该打印5+1 = 6。该宏工作正常。

然而,如果我封装多个这些宏,那将变得非常不可读,因为“DBG”本身总是被拖来拖去。

我想要做的是在编译时从表达式本身中删除所有出现的子字符串“DBG”。这样DBG(DBG(5*3) + DBG(20/4)) 的结果将不是

5*3 = 15
20/4 = 5
DBG(5*3)+DBG(20/4) = 20

而是

5*3 = 15
20/4 = 5
(5*3)+(20/4) = 20

如果需要:宏如下所示:#define DBG(expression) debug_log((#expression, expression),其中 debug_log 为:

template<typename T>
inline constexpr T debug_log(const std::string_view& raw_expression, T&& x)

    using namespace std;
    cout << raw_expression << " = " << x << endl;
    return x;

我已经写了一个帮助函数,应该这样做,但我不知道如何在编译时连接两个 string_views。


inline constexpr auto clean_expression(const std::string_view& expression)

    constexpr std::string_view macro_name = "DBG";
    constexpr auto marco_name_length = macro_name.size();

    auto pos = expression.find(macro_name);

    if (pos == -1) 
        return expression;
    
    else 
        auto after_macro_name = expression.substr(pos + marco_name_length);

        auto length_before_macro = expression.size() - after_macro_name.size() - marco_name_length;
        std::string_view string_before_macro_name = expression.substr(0, length_before_macro);

        // TODO: Finish implementation by concatenating the string_before_macro_name and after_macro_name and cleaning the result
        //auto partly_cleaned_string = concatenate(string_before_macro_name, after_macro_name); <-- that one is missing
        //return clean_expression(partly_cleaned_string);
    

【问题讨论】:

您不能从 string_view 中“删除”任何内容。 string_view 基本上是一个不变的、不可变的对象。 我不确定你为什么要在编译时使用它。 DBG(DBG(5*3) + DBG(20/4)) 应该由您的宏评估为DBG((5*3 = 15) + (20/4 = 5))。你想要DBG((5*3) + (20/4)),它的计算结果是5*3 + 20/4 = 20 你需要像fixed_string&lt;N&gt;constexpr const char[] 这样的concat 操作而不是string_view 完全不相关:constexpr 函数本质上是inline,因此不需要额外的限定符。 【参考方案1】:

您当前的策略将不起作用,因为std::string_view 必须指向一些现有的、连续的数据块。因此,除非您返回 string_view 的单个切片,否则您必须分配一个 char 数组并在结构中返回它:

template <size_t N>
struct fixed_string 
    char data[N];
;

现在,除非您想做 C 风格的事情并选择最大长度的缓冲区,否则您需要将表达式大小作为编译时常量(知道这是一个上限)。这...不起作用,因为函数参数不是 constexpr:

constexpr auto clean_expression(std::string_view& expression) 
    fixed_string<expression.size()> result; // fails
    /*...*/
    return result;

因此,听起来很奇怪,您需要将输入作为普通的旧 char 数组传递以确保安全:

template <size_t N>
constexpr auto clean_expression(const char (&expr)[N]) 
    fixed_string<N> result = ;
    /* ... */
    return result;

由于我们知道结果大小将小于或等于缓冲区大小,我们可以添加一个字段以使其可用作字符串:

template <size_t N>
struct fixed_string 
    constexpr std::string_view view() const  return  data, size ; 
    char data[N];
    size_t size;
;

之后,只需跳过std::string 方法并编写一个小循环来选择性地复制字符:

template <size_t N>
template <size_t N>
constexpr auto clean_expression(const char (&expr)[N]) 
    fixed_string<N> result = ;

    int src_idx = 0;
    int dst_idx = 0;
    while (src_idx < N - 2) 
        if (expr[src_idx] == 'D' && expr[src_idx+1] == 'B' && expr[src_idx+2] == 'G') 
            src_idx += 3;
         else 
            result.data[dst_idx++] = expr[src_idx++];
        
    
    result.data[dst_idx++] = expr[N-2];
    result.data[dst_idx++] = expr[N-1];
    result.size = dst_idx;
    return result;

并使用:

constexpr auto expr = clean_expression("DBG(DBG(y) + DBG(z))");
std::cout << expr.view(); // ((y) + (z))

演示:https://godbolt.org/z/8j3RCs

需要注意的是,您必须在使用前将其设为变量,因为此表达式的结果仍然是临时的,而不是全局的。如果将它直接传递给函数参数,则视图将比对象寿命长。

也许这对您不起作用,因为您希望将宏直接粘贴到字符串文字的位置。但是您可能会侥幸成功,因为清理后的字符串数据可能会在全局数据部分中,并且由此产生的未定义行为将具有预期的输出。

【讨论】:

以上是关于如何在编译时从 string_view 中删除子字符串?的主要内容,如果未能解决你的问题,请参考以下文章

删除 std::string_view 的最后一个字符

如何在迭代字典时从字典中删除项目?

如何在运行时从超级视图中删除视图?

如何在迭代时从地图中删除?

如何编写一个函数以在单击删除时从购物车中删除一个项目

如何在迭代时从 HashMap 中删除键? [复制]