简体   繁体   中英

Format a constexpr const char*

I'm working with __ PRETTY_FUNCTION __, C++11, that is formated like: (type Class::Object(parameters), but I need to format my string like (Class::Object()).

I know how to remove "type" from my string with constexpr. I can simply return a pointer skipping space, like this:

inline constexpr const char* removeType(const char* expression) noexcept
{
    return (*expression == ' ') ? expression+1 : removeType(expression+1);
}

This is working very well! But I can't think of a way to remove "parameters" from my string. I thought I could find "(" in my original expression and put a ") \\ 0" after that. But I can't change this string because it's a constant, and that would also change the __ PRETTY_FUNCTION __ return behavior. So, what can I do to create a new string (char* or const char*) in a constexpr and format it removing something in the middle of it?

I took a crack, ignoring the obvious caveat that __PRETTY_FUNCTION__ is a propriety GNU convention that can't be counted on for any guarantees in the formatting. My strategy was to start at the last ')', and then do a search for '(' while balancing parenthesis to avoid tricky parameters like function pointers. But then I was able to make this:

struct baz {
    template <typename T>
    decltype(auto) complex(int y, foo<int(*)()> a) const noexcept {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return 42;
    }
};
baz{}.complex<decltype(&biff)>(42, foo<decltype(&bar)>{});

Which produced:

decltype(auto) baz::complex(int, foo<int (*)()>) const [with T = float (*)()]

So, I made a helper function to do a search, balancing [] , <> , {} , and () , just in case .

constexpr const char* balancedReverseSeek(bool(*predicate)(char), const char* begin, const char* current) {
    size_t squareLevel = 0;
    size_t carrotLevel = 0;
    size_t parenLevel = 0;
    size_t braceLevel = 0;
    
    const char* c = current;
    for (; c != begin && (!predicate(*c) || squareLevel || carrotLevel || parenLevel || braceLevel); --c) {
        switch (*c) {
            case ')':
                parenLevel++;
                break;
            case '(':
                parenLevel--;
                break;
            case ']':
                squareLevel++;
                break;
            case '[':
                squareLevel--;
                break;
            case '>':
                carrotLevel++;
                break;
            case '<':
                carrotLevel--;
                break;
            case '}':
                braceLevel++;
                break;
            case '{':
                braceLevel--;
                break;
        }
    }
    return c;
}

Then this seems to work ok, and it gets the class name ( baz::complex ):

constexpr std::string_view getName(std::string_view prettyName) {
    if (prettyName.empty()) {
        return prettyName;
    }

    const char* signatureEnd = prettyName.data() + (prettyName.size() - 1);
    const char* paramEnd = balancedReverseSeek([](char c) { return c == ')'; }, prettyName.data(), signatureEnd);
    if (paramEnd != prettyName.data()) {
        paramEnd--;
    }
    const char* nameEnd = balancedReverseSeek([](char c) { return c == '('; }, prettyName.data(), paramEnd);
    if (nameEnd != prettyName.data()) {
        nameEnd--;
    }

    const char* nameBegin = balancedReverseSeek([](char c) { return c == ' '; }, prettyName.data(), nameEnd);
    return { nameBegin, nameEnd - nameBegin + 1 };
}

Demo: https://godbolt.org/z/W578vP

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM