0
votes

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?

1
You are trying to replace part of the string from being (args...) to being (). Since this is a mutating operation, and this is in a constexpr context in C++11, the only way you will be able to accomplish this is with constructing a new string containing the name of the function name by building it in a variadic pack expansion (e.g. as an array of characters) -- which won't be easy. Why exactly do you need this functionality?Human-Compiler
In my company we log some information from our methods, including when it starts and finishs. All logs are formated as (Class::Method() - message). For start and finish, I've created an object where the constructor will print "begin" and destructor "end". To make everything simpler, I'm using PRETTY_FUNCTION and some macros so I don't need to pass classname and methodname on every call. I would like to have this format because some other messages have this format.Felipe
Separately, types can have spaces, e.g. const char* or even std::map<int, const char*>, so removeType as shown is unreliable.Tony Delroy
The macros are working, but I have this parameters printedFelipe
@TonyDelroy you are actually right... So it's worse than I thoughtFelipe

1 Answers

1
votes

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