草庐IT

C++ 编译时子串

coder 2024-02-14 原文

我有非常大的代码库,它广泛使用 __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/

有关C++ 编译时子串的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  5. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  6. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  7. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

  8. .net - 是否有 Ruby .NET 编译器? - 2

    是否有适用于Ruby语言的.NETFramework编译器?我听说过DLR(动态语言运行时),这是否将使Ruby能够用于.NET开发? 最佳答案 IronRuby是Microsoft支持的项目,建立在动态语言运行时之上。 关于.net-是否有Ruby.NET编译器?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/199638/

  9. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

    出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

  10. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

    我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

随机推荐