gpt4 book ai didi

C++ 编译时子串

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:17:00 31 4
gpt4 key购买 nike

我有非常大的代码库,它广泛使用 __FILE__ 进行日志记录。但是,它包含完整路径,这是 (1) 不需要的,(2) 可能会违反安全规定。

我正在尝试编写编译时子字符串表达式。结束了 this solution

static constexpr cstr PastLastSlash(cstr str, cstr last_slash)
{
return *str == '\0' ? last_slash : *str == '/' ? PastLastSlash(str + 1, str + 1) : PastLastSlash(str + 1, last_slash);
}

static constexpr cstr PastLastSlash(cstr str)
{
return PastLastSlash(str, str);
}

// usage
PastLastSlash(__FILE__);

这很好,我检查了汇编代码,在编译时修剪了行,只有文件名以二进制形式存在。

但是,这个符号太冗长了。我想为此使用宏,但失败了。上面链接中的建议示例

#define __SHORT_FILE__ ({constexpr cstr sf__ {past_last_slash(__FILE__)}; sf__;})

不适用于 MSVC 编译器(我使用的是 MSVC 2017)。使用 c++17 有没有其他方法可以做到这一点?

UPD1: clang 由函数修剪 https://godbolt.org/z/tAU4j7

UPD2: 看起来可以使用函数在编译时进行修剪,但完整的字符串将以二进制形式存在。

最佳答案

想法是创建截断的字符数组,但它只需要使用编译时特性。通过带有 char 包的可变参数模板生成数据数组会强制编译器生成与传递的字符串文字没有直接关系的数据。这样编译器就不能使用输入字符串文字,尤其是当这个字符串很长的时候。

带有 clang 声的 Godbolt:https://godbolt.org/z/WdKNjB .

Godbolt 与 msvc:https://godbolt.org/z/auMEIH .

唯一的问题是模板深度编译器设置。

首先我们定义 int 可变参数模板来存储索引序列:

template <int... I>
struct Seq {};

将 int 插入 Seq :

template <int V, typename T>
struct Push;

template <int V, int... I>
struct Push<V, Seq<I...>>
{
using type = Seq<V, I...>;
};

创建序列:

template <int From, int To>
struct MakeSeqImpl;

template <int To>
struct MakeSeqImpl<To, To>
{
using type = Seq<To>;
};

template <int From, int To>
using MakeSeq = typename MakeSeqImpl<From, To>::type;

template <int From, int To>
struct MakeSeqImpl : Push<From, MakeSeq<From + 1, To>> {};

现在我们可以生成编译时整数序列,这意味着 MakeSeq<3,7> == Seq<3,4,5,6,7> .我们仍然需要一些东西来将选定的字符存储在数组中,但是使用编译时表示,这是带有字符的可变模板参数:

template<char... CHARS>
struct Chars {
static constexpr const char value[] = {CHARS...};
};
template<char... CHARS>
constexpr const char Chars<CHARS...>::value[];

接下来我们将选定的字符提取到 Chars 中输入:

template<typename WRAPPER, typename IDXS>
struct LiteralToVariadicCharsImpl;

template<typename WRAPPER, int... IDXS>
struct LiteralToVariadicCharsImpl<WRAPPER, Seq<IDXS...> > {
using type = Chars<WRAPPER::get()[IDXS]...>;
};

template<typename WRAPPER, typename SEQ>
struct LiteralToVariadicChars {
using type = typename LiteralToVariadicCharsImpl<WRAPPER, SEQ> :: type;
};

WRAPPER是一种包含我们的字符串文字的类型。

快完成了。缺少的部分是找到最后一个斜线。我们可以使用问题中找到的代码的修改版本,但这次它返回偏移量而不是指针:

static constexpr int PastLastOffset(int last_offset, int cur, const char * const str)
{
if (*str == '\0') return last_offset;
if (*str == '/') return PastLastOffset(cur + 1, cur + 1, str + 1);
return PastLastOffset(last_offset, cur + 1, str + 1);
}

最后一个获取字符串大小的工具:

constexpr int StrLen(const char * str) {
if (*str == '\0') return 0;
return StrLen(str + 1) + 1;
}

使用 define 将所有内容组合在一起:

#define COMPILE_TIME_PAST_LAST_SLASH(STR)                                   \
[](){ \
struct Wrapper { \
constexpr static const char * get() { return STR; } \
}; \
using Seq = MakeSeq<PastLastOffset(0, 0, Wrapper::get()), StrLen(Wrapper::get())>; \
return LiteralToVariadicChars<Wrapper, Seq>::type::value; \
}()

Lambda 函数是为了在使用该宏时具有良好的、类似于值的感觉。它还创建了一个范围来定义 Wrapper结构体。使用宏生成带有插入字符串文字的结构,导致字符串文字被限制为类型时的情况。

老实说,我不会在生产中使用这种代码。它正在杀死编译器。

无论是出于安全原因还是内存使用,我都建议使用带有自定义短路径的 docker 进行构建。

关于C++ 编译时子串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56471708/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com