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

时间:2019-12-30 15:07:32

标签: c++ substring string-concatenation constexpr string-view

我创建了一个名为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);
    }
}

1 个答案:

答案 0 :(得分:1)

您的当前策略无法正常工作,因为std::string_view必须指向一些现有的连续数据块。因此,除非您要返回string_view的单个切片,否则必须分配一个char数组并将其返回到struct内部:

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

需要说明的是,使用前必须将其设置为变量,因为此表达式的结果仍是临时的,而不是全局的。如果直接将其传递给函数参数,则视图将使对象失效。

也许这可能对您不起作用,因为您希望直接将宏替换为字符串文字。但是您可能会避开它,因为清除后的字符串数据可能会在全局数据部分中,并且产生的未定义行为将具有预期的输出。