0

I created a macro named DBG, which prints an expression itself and the value it evaluates to. So DBG(5+1) should print 5+1 = 6. That macro works fine.

Yet, if I encapsulate more than one of those macro, that becomes quite unreadible, because the "DBG" itself is always dragged around.

What I want to do is to remove all occurences of the substring "DBG" from the expression itself at compile time. So that DBG(DBG(5*3) + DBG(20/4)) the result would not be

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

but instead

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

If that's needed: The Macro looks like this: #define DBG(expression) debug_log((#expression, expression) with debug_log being:

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

I wrote already a helper function that is supposed to do that, but I can't figure out how to concatenate two string_views at compile time.


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);
    }
}
Jack Sabbath
  • 1,398
  • 2
  • 9
  • 12
  • 1
    You cannot "remove" anything from a string_view. A string_view is, basically, a constant, non-mutable object. – Sam Varshavchik Dec 30 '19 at 15:12
  • 3
    I'm not sure why you want it at compile time. – apple apple Dec 30 '19 at 15:13
  • 1
    `DBG(DBG(5*3) + DBG(20/4))` __should__ evaluate to `DBG((5*3 = 15) + (20/4 = 5))` by your macro. You want `DBG((5*3) + (20/4))`, which evaluates to `5*3 + 20/4 = 20`. – Jaideep Shekhar Dec 30 '19 at 15:17
  • *What I want to do is to remove all occurences of the substring "DBG" from the expression itself* -- Sounds like a job for a tool like `sed` instead of trying to do this programmatically. – PaulMcKenzie Dec 30 '19 at 15:19
  • 2
    you need sth like `fixed_string` or `constexpr const char[]` with some `concat` operation rather than `string_view` – MamCieNaHita Dec 30 '19 at 15:32
  • @SamVarshavchik Yes, but you can construct a new string that represents the old one minus that specific substring. – Jack Sabbath Dec 30 '19 at 16:03
  • @appleapple Compile time string manipulation is generally desirable, since it lowers the overhead and C++ is all about efficiency. I just picked this example because it's not too complex. – Jack Sabbath Dec 30 '19 at 16:04
  • @JaideepShekhar The macro actually is and shall be evaluated three times (one might want to know intermediate results). I'm trying to reduce the noise of the surrounding call. – Jack Sabbath Dec 30 '19 at 16:04
  • well, then your question is actually *how to join two compile time string* which does have some answers around. But I still don't think you have a valid use case here. – apple apple Dec 30 '19 at 16:56
  • 1
    completely unrelated: `constexpr` functions are inherently `inline`, so the extra qualifier is unnecessary. – parktomatomi Dec 30 '19 at 19:47

1 Answers1

1

Your current strategy won't work because std::string_view has to point to some existing, contiguous block of data. So, unless you're returning a single slice of the string_view, you have to allocate a char array and return it inside a struct:

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

Now, unless you want to do things C-style and pick a buffer of maximum length, you need to get the expression size as a compile-time constant (knowing that is an upper limit). This... doesn't work because function parameters are not constexpr:

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

So, strange as it sounds, you need to pass your input as a plain old char array to be safe:

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

Since we know the result size will be less than or equal to the buffer size, we can add a field to make it usable as a string:

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

After that, just skip the std::string methods and write a little loop to selectively copy characters:

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

And to use:

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

Demo: https://godbolt.org/z/8j3RCs

The caveat is that you have to make it a variable before using, because the result of this expression is still a temporary, not a global. If you pass it directly to a function argument the view will outlive the object.

Maybe that might not work for you because you were hoping to stick the macro directly in place of a string literal. But you might get away with it because the cleaned string data will probably be in the global data section and the resulting undefined behavior would have the intended output.

parktomatomi
  • 3,851
  • 1
  • 14
  • 18