Search code examples
c++performancestdc++20standards

Why doesn't std::source_location provide lengths to avoid performance penalty in C++20?


Consider the following code:

void Log(std::string_view       msg,
    std::source_location const& loc = std::source_location::current()) {
    
    // Performance penalty!
    auto const n = std::strlen(loc.file_name()); 
    auto s = ""s;
    s.resize(msg.size() + n);

    std::memcpy(&s[0], msg.data(), msg.size());
    std::memcpy(&s[msg.size()], loc.file_name(), n);

    // sendToRemoteMachine(s); 
}

Obviously, if std::source_location::file_name returns a std::string_view, the performance penalty can be easily avoided.

I think, std::source_location::file_name is a compile-time generated and readonly information. The compiler can store it into an std::string_view object.

Why doesn't std::source_location provide lengths to avoid performance penalty in C++20?

Update

Another related article: std::source_location is Broken


Solution

  • C++ does not have a type that represents all of the following:

    1. Non-ownership of a string.
    2. Has a known length.
    3. Is NUL-terminated.

    std::string has both 2 and 3, but not 1. string_view has 1 and 2, but does not guarantee 3 at the type level. char const* has 1 and there's a general expectation that they use 3, but they don't carry 2.

    And yes, #3 is actually quite important. Your example wants to pass the string to an API that needs a sized string; that's why you're computing the size. Other people will have APIs that expect an NTBS. For them, a string_view-based API now requires that they copy the sized-string into an NTBS. Which is an O(N) operation.

    Unless you have a type that communicates all 3 of these, one of you is going to have to do an O(N) operation.

    Yes, technically, a string_view could point to an NTBS. But at the type level, if you're handed a string_view, you don't know that it points to an NTBS. That would be a guarantee from the function that generates it, not from the type itself. So it would be an inappropriate use of the type.


    Another related article

    This article is about a largely separate issue: the lack of a constexpr version of source_location, one which would allow the string to be guaranteed to only exist at compile-time.

    This issue is not something that any library construct could ever solve. Returning string_view from member functions wouldn't fix the problem from that article. While string_view can be constexpr, parameters cannot. And since source_location is passed as a parameter, it must follow the rules of the C++ language. Even though the compiler generated it, it is a parameter of the function and therefore it cannot be a constexpr parameter.

    Indeed, it couldn't even be a C++ language construct because the language cannot pass compile-time information as a function parameter. Remember: the source of the information is the caller. What the writer of that article would do with __FILE__ is manually stick it into a template parameter of that function, not a function parameter.

    And it should be noted: returning a size isn't the problem as outlined by the article. You can compute a size of a char const* at compile-time, if the char const* itself is constexpr. You can then use that compile-time size and the char const* itself to manufacture a type. The problem the article is talking about is entirely in the scope of getting a compile-time sequence of characters. Whether it's a string_view or a char const* NTBS or a char[N] is irrelevant; any of them would work.