首页 > 其他分享 >std::format 如何实现编译期格式检查

std::format 如何实现编译期格式检查

时间:2024-04-07 21:35:06浏览次数:20  
标签:std consteval 函数 format 编译 string

C++ 20 的 std::format 是一个很神奇、很实用的工具,最神奇的地方在于它能在编译期检查字符串的格式是否正确,而且不需要什么特殊的使用方法,只需要像使用普通函数那样传参即可。

#include <format>

int a = 1;
std::string s1 = std::format("a: {}", a); // OK
std::string s2 = std::format("a: {}, b: {}", a); // 编译错误

C++ 20 的 std::format 来自一个著名的开源库 {fmt}。在 C++ 20 之前,fmt 需要为每个字符串字面量创建不同的类型才能实现编译期格式检查。fmt 提供了一个 FMT_STRING 宏以简化使用的流程。

#include <fmt/format.h>

int a = 1;
std::string s1 = fmt::format(FMT_STRING("a: {}"), a); // OK
std::string s2 = fmt::format(FMT_STRING("a: {}, b: {}"), a); // 编译错误

C++ 20 有了 consteval 后就不用这么别扭了。consteval 函数与以前的 constexpr 函数不同,constexpr 函数只有在必须编译期求值的语境下才会在编译期执行函数,而 consteval 函数在任何情况下都强制编译期求值。std::format 就是利用 consteval 函数在编译期执行代码,来检查字符串参数的格式。

然而 std::format 自身不能是 consteval 函数,只好曲线救国,引入一个辅助类型 std::format_string,让字符串实参隐式转换为 std::format_string。只要这个转换函数是 consteval 函数,并且把格式检查的逻辑写在这个转换函数里面,照样能实现编译期的格式检查。

这里我们实现了一个极简版的 format,可以检查字符串中 {} 的数量是否与参数的个数相同。format_string 的构造函数就是我们需要的隐式转换函数,它是一个 consteval 函数。若字符串中 {} 的数量不对,则代码会执行到 throw 这一行。C++ 的 throw 语句不能在编译期求值,因此会引发编译错误,从而实现了在编译期检查出字符串的格式错误。

namespace my {
    template<class ...Args>
    class format_string {
    private:
        std::string_view str;

    public:
        template<class T>
            requires std::convertible_to<const T &, std::string_view>
        consteval format_string(const T &s)
            : str(s)
        {
            std::size_t actual_num = 0;
            for (std::size_t i = 0; i + 1 < str.length(); i++) {
                if (str[i] == '{' && str[i + 1] == '}') {
                    actual_num++;
                }
            }
            constexpr std::size_t expected_num = sizeof...(Args);
            if (actual_num != expected_num) {
                throw std::format_error("incorrect format string");
            }
        }

        std::string_view get() const { return str; }
    };

    template<class ...Args>
    std::string format(format_string<std::type_identity_t<Args>...> fmt, Args &&...args) {
        // 省略具体的格式化逻辑
    }
}

有一个细节,此处 format 函数的参数写的是 format_string<std::type_identity_t<Args>...>,直接写 format_string<Args ...> 是无法隐式转换的,因为模板实参推导 (template argument deduction) 不会考虑隐式转换,C++ 20 提供了一个工具 std::type_identity 可以解决这个问题。std::type_identity 其实就是一个关于类型的恒等函数,但是这么倒腾一下就能在模板实参推导中建立非推导语境 (non-deduced context),进而正常地匹配到隐式转换,C++ 就是这么奇怪。参考资料:c++ - why would type_identity make a difference? - Stack Overflow

标签:std,consteval,函数,format,编译,string
From: https://www.cnblogs.com/zhb2000/p/18119964/std-format-compile-time-check

相关文章

  • make编译报错:fatal error: filesystem: 没有那个文件或目录 #include <filesystem>
    报错:fatalerror:filesystem:没有那个文件或目录#include(filesystem)解决方法一:修改头文件#include<experimental/filesystem>添加依赖在编译时,后面添加:-lstdc++fs编译通过。解决方法二:升级gcc升级到gcc-8或8以上问题即可解决:添加PPA存储库首先,您需要添加Ub......
  • 【编译原理】手工打造语法分析器
    重点:语法分析的原理递归下降算法(RecursiveDescentParsing)上下文无关文法(Context-freeGrammar,CFG)关键点:左递归问题深度遍历求值-后续遍历上一篇「词法分析器」将字符串拆分为了一个一个的token。本篇我们将token变成语法树。一、递归下降算法还是这个例子in......
  • Windows系统编译protobuf
    在编译ncnn前,需要编译好protobuf。这篇文章将记录protobuf的编译方法。什么是protobufProtocolBuffers是一种轻量级、高效的数据交换格式,它可以用于结构化数据的序列化,适用于通信协议、数据存储等领域。优点:高效性:ProtocolBuffers生成的数据序列化后体积小,速度快。跨语......
  • CentOS安装RustDesk自建服务器
    一、安装node.js1、下载并解压[root@VM-8-2-centossrc]#cd/usr/local#下载node文件[root@VM-8-2-centoslocal]#wgethttps://registry.npmmirror.com/-/binary/node/v16.18.1/node-v16.18.1-linux-x64.tar.gz#解压文件[root@VM-8-2-centoslocal]#sudotar-zxvfno......
  • opencv4.9.0交叉编译教程
    opencv4.9.0交叉编译教程1.准备工作在linux系统中下载opencv源码;在linux系统中安装cmake-gui;linux系统具有嵌入式板卡对应的交叉编译工具链,且能够正常使用。2.配置opencv​​​​​​​​勾选opencl(如果你芯片支持),opengl(如果你芯片支持),openmp。​​​​使能zLI......
  • Nginx 1.25.4 编译安装
    Nginx1.25.4源码编译安装最新长期技术支持版本 nginx-1.25.4,官方下载源码包: https://nginx.org/download/nginx-1.25.4.tar.gz一键安装脚本不啰嗦,脚本简单快速编译安装#!/bin/bash#安装相关包yum-yinstallgccpcre-developenssl-develzlib-develmakewget#......
  • centos7:编译升级 openssh:主要参考“https://github.com/boypt/openssh-rpms”
    参考“https://github.com/boypt/openssh-rpms”  “Releases·boypt/openssh-rpms(github.com)” 安装人家大牛的文档来操作即可。可选的,自行定制 version.env,可进行各种组合! BackportOpenSSHRPM/SRPMforoldCentOSAsimplescripttobuildlatestOpen......
  • ILSpy是一个开源的.NET反编译器
    在软件开发领域,反编译器是一种强大的工具,它允许开发者查看已编译代码的内部结构。对于.NET平台,ILSpy是一款备受欢迎的反编译器,它能够将已编译的.NET程序集(如DLL或EXE文件)转换回近似的C#或VB.NET源代码。这不仅有助于理解第三方库的工作原理,还能在调试、优化或学习他人代码时提供巨......
  • 常用API(一):Date SimpleDateFormat
       packagecom.itheima.日期;importjava.text.ParseException;importjava.text.SimpleDateFormat;importjava.util.Date;publicclassdate{publicstaticvoidmain(String[]args)throwsParseException{Dated=newDate();System.out.pr......
  • OGRE 渲染引擎 Windows 平台构建及编译
    0OGRE机器人领域常见的Rviz和Gazebo可视化的渲染后端。1无数的坑这东西真的可恶,官方教程文档以及项目构建的方式是真的繁琐,在Windows上。CMake的构建必须要使用CMake-Gui,否则你会遭遇到莫名奇妙的BUG。并不是说不可以用命令行,问题是出现问题,你需要花很多时间去解决。......