Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::source_location - filename only, not full path - compile-time substring?

Is there any way to make a substring at compile time, and NOT store the original string in the binary ?

I'm using std::experimental::source_location, and really only need the filename, and not the full path, which ends up taking lots of space in the binary

Here's an example:

#include <iostream>
#include <experimental/source_location>

consteval std::string_view filename_only(
    std::experimental::source_location location = std::experimental::source_location::current())
{
    std::string_view s = location.file_name();
    return s.substr(s.find_last_of('/')+1);
}

int main()
{
    std::cout << "File: " << filename_only() << '\n';
}

https://godbolt.org/z/TqE7T87j3

The full string "/app/example.cpp" is stored, but only the file name is needed, so "/app/" is wasted memory.

like image 380
Nebular Noise Avatar asked Sep 16 '25 11:09

Nebular Noise


2 Answers

Based on this, I ended up using the __FILE__ macro combined with the -fmacro-prefix-map compiler option, instead of source_location.

So I essentially use the following code

#include <cstdio>
#include <cstdint>
#define ERROR_LOG(s) log_impl(s, __FILE__, __LINE__);

void log_impl(const char* s, const char* file_name, uint16_t line)
{
    printf("%s:%i\t\t%s", file_name, line, s);
}

int main()
{
    ERROR_LOG("Uh-oh.")
}

with the following compiler option:

-fmacro-prefix-map=${SOURCE_DIR}/=/

I can verify that the constant strings stored in the binary do not include the full file path, as they did before, which was my objective.

Note that starting from GCC12, the macro __FILE_NAME__ should be available, making the use of the -fmacro-prefix-map option redundant. I'm not using gcc 12 just yet, so the solution above is adequate.

like image 71
Nebular Noise Avatar answered Sep 19 '25 02:09

Nebular Noise


Not sure if this is the most optimal way to do this, but with C++20 and its ability to use strings as template arguments, string can be trimmed at compile time like so:

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

        template <size_t N1>
        consteval fixed_string(const char (&str)[N1], size_t Trim)
        {
            for (size_t i = 0; i < N; ++i)
            {
                data[i] = str[i + Trim];
            }
        }

        consteval fixed_string(const char (&str)[N])
        {
            for (size_t i = 0; i < N; ++i)
            {
                data[i] = str[i];
            }
        }

        constexpr size_t size() const { return N - 1; }
        constexpr char operator[](size_t i) const { return data[i]; }
    };

    template <fixed_string Str>
    consteval size_t find_last_slash()
    {
        size_t last_index = 0;
        for (size_t i = 0; i < Str.size(); ++i)
        {
            if (Str[i] == '/' || Str[i] == '\\')
            {
                last_index = i + 1;
            }
        }
        return last_index;
    }

    template <fixed_string Str>
    consteval auto get_file_name()
    {
        constexpr size_t idx = find_last_slash<Str>();
        return fixed_string<Str.size() - idx + 1>(Str.data, idx);
    }
}

#define FILE_NAME (([]{ return ::helpers_impl::get_file_name<__FILE__>(); })().data)

https://godbolt.org/z/83zjnYsWc

like image 41
Surfin Bird Avatar answered Sep 19 '25 01:09

Surfin Bird