为什么这个 __METHOD__NAME__ 需要内存副本?

Kan*_*bin 3 c++ classname

这个想法来自/sf/answers/1104286361/

我在 string_view 末尾添加了一个 '\0',这样我们就可以将 data() 用于 printf、spdlog 等。

我使用这个宏来打印带有类名的函数名。

但是,我发现编译器不够智能,无法内联字符串,而是需要先进行内存复制才能堆栈:

https://godbolt.org/z/bqob37G3z

请参阅 main 函数中 CALL 和 CALLFUNC 的区别。

是否可以告诉编译器将字符串放在某个 RO 部分中,例如 const char *?

template<std::size_t N>
consteval const std::array<char, N> __get_function_name(const char * arr)
{
    std::array<char, N>         data {};
    std::string_view            prettyFunction(arr);
    size_t                      bracket = prettyFunction.rfind("(");
    size_t                      space = prettyFunction.rfind(" ", bracket) + 1;
    size_t                      i;

    for (i = 0; i < bracket - space; i += 1) {
        data[i] = arr[space + i];
    }
    data[i] = '\0';

    return data;
}

#define __METHOD_NAME__ __get_function_name<strlen(__PRETTY_FUNCTION__)>(__PRETTY_FUNCTION__).data()
Run Code Online (Sandbox Code Playgroud)

感谢@nm 和 user17732522。我终于得到了一个可行的版本: https ://godbolt.org/z/sof1j3Md4

仍然不完美,因为该解决方案需要搜索“(”和“”两次,这可能会增加编译时间。


更新:仅调用 rfind(" ") 一次:https ://godbolt.org/z/zYcajqaje

#include <array>
#include <string_view>

constexpr size_t my_func_end(const char * arr)
{
    std::string_view prettyFunction(arr);
    return prettyFunction.rfind("(");
}

constexpr size_t my_func_start(const char * arr, const size_t end)
{
    std::string_view prettyFunction(arr);
    return prettyFunction.rfind(" ", end) + 1;
}


template<std::size_t S, std::size_t E>
constexpr const std::array<char, E - S + 1> my_get_function_name(const char * arr)
{
    std::array<char, E - S + 1> data {};
    size_t i;
    for ( i = 0; i < E - S; i += 1) {
        data[i] = arr[S + i];
    }
    data[i] = '\0';

    return data;
}

template<auto tofix>
struct fixme
{
   static constexpr decltype(tofix) fixed = tofix;
};

#define __METHOD_NAME_ARRAY__(x)    my_get_function_name<my_func_start(x, my_func_end(x)), my_func_end(x)>(x)
#define __METHOD_NAME__             (fixme<__METHOD_NAME_ARRAY__(__PRETTY_FUNCTION__)>::fixed.data())

Run Code Online (Sandbox Code Playgroud)

不确定编译器是否可以缓存计算。如果是的话,那就足够了。

use*_*522 5

编译器没有意识到或考虑 的特定行为puts,即它不存储传递的指针,也不会再次调用其调用者。

问题是,如果没有这些知识,编译器必须考虑puts存储传递给全局变量的指针的可能性,然后再次调用其调用者,然后将新的指针参数与存储在变量中的旧指针参数进行比较。全局变量。它们必须比较不相等,因为它们在其生命周期中都是指向不同临时对象的指针。因此编译器无法重用相同的只读静态内存位置作为调用的参数puts

因此,您需要明确告诉编译器使用静态内存位置:

#define METHOD_NAME get_function_name<my_strlen(__PRETTY_FUNCTION__)>(__PRETTY_FUNCTION__)

#define CALL() { static constexpr auto v = METHOD_NAME; puts(v.data()); }
Run Code Online (Sandbox Code Playgroud)

从技术上讲,constexpron与on 功能v是多余的。consteval只要constexpr两者都满足就足够了。

如果您不添加,constexprv可能需要添加const以确保编译器不需要考虑puts修改字符串内容的可能性,尽管示例中的 GCC 似乎意识到了这一点。(puts需要一个const char*作为参数不足以证明这一点。)


顺便说一句,你不能strlen在那里使用(假设你希望它能够移植到 GCC 以外的其他编译器,该编译器__PRETTY_FUNCTION__以你使用的方式支持,例如带有 libc++ 的 Clang)。GCC 在没有诊断的情况下允许它不符合标准,并且不能保证它可以在其他编译器上工作。std::strlen未按constexpr标准标记。您可以使用C++17std::char_traits<char>::length以来的替代方法constexpr

  • 可以使用 [`std::char_traits&lt;char&gt;::length(...)`](https://en.cppreference.com/w/cpp/string/char_traits/length) 代替 `std::strlen ` 为了可移植性,因为它是 c++17 之后的 `constexpr`。所以你真的不需要编写自己的实现 (3认同)