Explicación detallada de la API de c++{fmt}


¡Comencemos a explorar los misterios de la API de la biblioteca fmt sin más demora! Si no está familiarizado con las reglas de sintaxis de la biblioteca fmt, no se preocupe, puede consultar la guía de uso de la biblioteca c++ {fmt} para obtener más detalles.

La API principal está en fmt/core.h

  1. Formatee los argumentos de acuerdo con las especificaciones en fmt y devuelva el resultado como una cadena.

    template<typename ...T>  
    auto fmt::format(format_string<T...> fmt, T&&... args) -> std::string  
    
    #include <fmt/core.h>
    // message == The answer is 42.
    std::string message = fmt::format("The answer is {}.", 42);
    
  2. Formatee los argumentos de acuerdo con la especificación en fmt, formatee en un iterador, devuelva el iterador al final del iterador y no agregue el carácter nulo de terminación.

    template<typename OutputIt, typename ...T>  
    auto fmt::format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt
    
    #include <fmt/core.h>
    auto out = std::vector<char>();
    // out vector 中有两个元素,4、2
    fmt::format_to(std::back_inserter(out), "{}", 42);
    
  3. Formatee los argumentos de acuerdo con la especificación en fmt, formatee n caracteres en un iterador (devuelva el iterador inicial + n ese iterador) y no agregue el carácter nulo final.

    template<typename OutputIt, typename ...T>  
    auto fmt::format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T&&... args) -> format_to_n_result<OutputIt>
    
    #define FMT_HEADER_ONLY 
    #include "fmt/core.h"
    #include <string>
    #include <vector>
    // 迭代器开始位置是容器末尾位置,crash  
    // n 数操作容器最大数,crash
    // 返回迭代器是格式化的总个数字符
    // 如果n < out.size && 小于123456的个数,那么后面会格式化'\0'字符。
    int main()
    {
          
          
        auto out = std::vector<char>(10);
        // nit n代表个数,it代表迭代器^_^ 
        auto nit =  fmt::format_to_n(out.begin(), 3, "{}", 123456); 
        // 返回的迭代器为 out.begin() + 3
        std::vector<char>::iterator it = nit.out;
        // 返回格式化总的个数
        int size = nit.size;
        return 0;
    }
    
  4. Formatee en la consola, similar al lenguaje C printf (el nombre de la función es imprimir, no f, f es la abreviatura de formato)

    template<typename ...T>   
    void fmt::print(format_string<T...> fmt, T&&... args)  
    
        #define FMT_HEADER_ONLY 
        #include "fmt/core.h"
        #include <string>
        #include <vector>
        int main()
        {
          
          
            // 输出到stdout
            fmt::print("Elapsed time: {} seconds\n", 1.23);
            // 输出到stdout
            fmt::print(stdout, "Elapsed time: {} seconds\n", 1.23);
            // 输出到stderr
            fmt::print(stderr, "Elapsed time: {} seconds\n", 1.23);
            return 0;
        }
    
  5. salida al archivo

    template<typename ...T>   
    void fmt::print(std::FILE *f, format_string<T...> fmt, T&&... args)  
    
        #define FMT_HEADER_ONLY 
        #include "fmt/core.h"
        #include <stdio.h>
        int main()
        {
          
          
            FILE* file;
            file = fopen("d:/demo/test.txt", "w");
            // 打开d盘文件,即可看到输出内容
            fmt::print(file, "Elapsed time: {} seconds\n", 1.23);
            return 0;
        }
    

parámetros nombrados

plantilla<typename Char, typename T>
auto fmt::arg(const Char *name, const T &arg) -> detalle::named_arg<Char, T>

  1. Devuelve un argumento con nombre a la función de formato, que solo se puede utilizar en la función de formato o endynamic_format_arg_store::push_back
  2. Esta función no admite la verificación en tiempo de compilación.
    // 命名参数的名字是agr_id 
    fmt::print("my name is {name:}", fmt::arg("name", "knox"));
    // 输出: my name is knox
    

lista de parámetros

Esta es una serie de clases de plantilla y métodos de plantilla. Podemos usar estas clases para construir nuestras propias funciones de cambio de formato. Por ejemplo, la parte frontal del registro se compone de fecha, identificación del hilo, nombre de la función, etc.

  1. función fmt::make_format_args

    template<typename Context = format_context, typename ...Args>
    constexpr auto fmt::make_format_args(Args&&... args) -> format_arg_store<Context, remove_cvref_t<Args>...>
    // 构造一个包含参数引用的format_arg_store 对象,并且可以隐式转换为format_args, Context使用默认的即可
    // 由于是引用,不建议单独写一个变量来存储变量,变为悬空引用。
    fmt::format_args args = fmt::make_format_args(42); 
    
  2. clase format_arg_store

    template<typename Context, typename ...Args>
    class format_arg_store
    {
          
          
        // 该类是存储参数引用的数组,并且可以隐式转换为basic_format_args
    };
    
  3. clase basic_format_args

    template<typename Context>
    class basic_format_args
    {
          
          
    public:
        // 从format_arg_store 构造basic_format_args
        template<typename ...Args>
        constexpr basic_format_args(const format_arg_store<Context, Args...> &store)// 从dynamic_format_arg_store 构造basic_format_args
        template<typename ...Args>
        constexpr basic_format_args(const dynamic_format_arg_store<Context> &store)// 从动态参数中 构造basic_format_args
        template<typename ...Args>
        constexpr basic_format_args(const format_arg *args, int count)// 使用id获取一个format_arg
        auto get(int id) const -> format_arg;
    }// format_args 是basic_format_args<format_context> 的别名
    using format_args = basic_format_args<format_context>;
    
  4. basic_format_parse_context

    template<typename Char, typename ErrorHandler = detail::error_handler>
    class fmt::basic_format_parse_context : private fmt::detail::error_handler
    {
          
          
    public:
        constexpr auto begin() const noexcept->iterator;
        constexpr auto end() const noexcept->iterator;
        void advance_to(iterator it);
        auto next_arg_id() -> int;
        void check_arg_id(int id);
    };
    using format_parse_context  = basic_format_parse_context<char>;
    using wformat_parse_context = basic_format_parse_context<wchar_t>;
    
  5. basic_format_context

    template<typename OutputIt, typename Char>
    class basic_format_context
    {
          
          
    public:
        // 构造
        constexpr basic_format_context(OutputIt out, basic_format_args<basic_format_context> ctx_args, detail::locale_ref loc = detail::locale_ref());
    } ;
    using format_context = buffer_context<char>
  6. Utilice la interfaz anterior para implementar una versión de impresión de registros

    #include <chrono>
    #include <iostream>
    #include <string>
    #include <thread>
    #include <sstream>
    #include "fmt/printf.h"
    #include "fmt/chrono.h"
    #undef	__MODULE__
    #define __MODULE__ "Test"
    void vlog(const char *module, int line, fmt::string_view format,
        fmt::format_args args) {
          
          
        std::string log;
        //  时间 打印行数 线程id
        auto now = std::chrono::system_clock::now();
        auto now_c = std::chrono::system_clock::to_time_t(now);
        std::stringstream ss;
        // 获取线程整数真难,没有公开方法
        ss << std::this_thread::get_id();
        fmt::format_to(std::back_inserter<std::string>(log), "[{0:%m%d %H\:%m\:%S}:{1:}][{2:}:{3:}][tid:{4:}] ", 
        fmt::localtime(now_c), (now.time_since_epoch() % 1000).count(),
            module, line, ss.str());
        fmt::vformat_to(std::back_inserter<std::string>(log), format, args);
    
        // 此处可以写文件 或者打印到控制台 简单起见打印到控制台
        fmt::print("{}", log);
    }
    
    template <typename S, typename... Args>
    void log(const char *file, int line, const S &format, Args&&... args) {
          
          
        vlog(file, line, format, fmt::make_format_args(args...));
    }
    
    #define log_v_notic(format, ...) \
    log(__MODULE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
    
    int main()
    {
          
          
        
        while (true)
        {
          
          
            log_v_notic("hello {} {}, hi {}\n", "v", "log", "knox");
            std::this_thread::sleep_for(std::chrono::milliseconds(3));
        }
        
        return 0;
    }
    

fmt::string_view

template<typename Char>
class basic_string_view 
{
    
    
public:
    // 使用c 风格字符串和大小构造一个字符串引用对象。
    constexpr basic_string_view(const Char *s, size_t count) noexcept;
    // 使用c风格字符串,大小由 std::char_traits<Char>::length 计算构造一个字符串引用对象。
    basic_string_view(const Char *s)
    // 从std::string or std::wstring 构造一个字符串引用对象。
    template<typename Traits, typename Alloc>
    basic_string_view(const std::basic_string<Char, Traits, Alloc> &s) noexcept
    // 返回字符对象字符的内容
    constexpr auto data() const noexcept -> const Char*
    // 返回字符对象的字符长度
    constexpr auto size() const noexcept -> size_t
};
using string_view = basic_string_view<char>;

Dar formato a tipos personalizados, incluidas clases y estructuras.

  1. Especializar el formateador e implementar métodos de análisis y formato.
#include <fmt/format.h>
struct Human {
    
    
    std::string name;
    int age;
    std::string address;
    std::string number; // 电话号码
};

// 特例化formatter模板
template <> struct fmt::formatter<Human> {
    
    
    // 自定义格式化风格,比如brief 简单介绍
    // detail详细介绍
    // 默认风格是简单介绍
    char style[10]{
    
    0};
    // 这个函数是解析格式化字符串的函数:{arg_id: brief | detail}
    // 解析是从:后面开始的 这里可以实现自己的逻辑,想怎么实现都可以
    constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) 
    {
    
    
        
        auto it = ctx.begin(), end = ctx.end() - 1;
        int index = 0;
        for (; it != end; it++)
        {
    
    
            style[index++] = *it;
            if (index > 9)
            {
    
    
                style[9] = '\0';
                break;
            }
        }

        // 合规性检查,是不是针对该自定义类型乱输入,比如输入{:1234343343} 这种就是错误的格式
        if (it != end && *(it) != '}')
        {
    
    
            throw format_error("invalid format");
        }
        return it;
    }

    // 2.使用指定的风格(简单风格,详细风格)实现格式化函数
    template <typename FormatContext>
    auto format(const Human & human, FormatContext &ctx) const -> decltype(ctx.out()) 
    {
    
    
        // 将内容都格式化到ctx的尾指针中,ctx.out();
        if (strcmp(style, "brief") == 0)
        {
    
    
            fmt::format_to(ctx.out(), "name:{}, age:{}, address:{}, number:{} \n", human.name, human.age, human.address, human.number);
        }
        else if(strcmp(style, "detail") == 0)
        {
    
    
            fmt::format_to(ctx.out(), "Hello, my name is {}. "
                "I am {} years old and currently reside in {}. "
                "If you need to contact me, you can reach me at{}.Thank you!\n",
                human.name, human.age, human.address, human.number);
        }
        else
        {
    
    
            // 都不是我们需要的格式,直接报错
            throw format_error("invalid format");
        }
        return ctx.out();
    }
};

int main()
{
    
    
    Human knox;
    knox.name = "Knox";
    knox.age = 18;
    knox.number = "1234567";
    knox.address = "tu chuan";
    // 简单版本
    fmt::print("{:brief}", knox);
    // 详细版本
    fmt::print("{:detail}", knox);
    try
    {
    
    
        // 错误版本
        fmt::print("{:2222}", knox);
        fmt::print("{:detail22222222}", knox);
    }
    catch (const fmt::format_error &excption)
    {
    
    
        fmt::print("{}", excption.what());
    }

}

producción

name:Knox, age:18, address:tu chuan, number:1234567
Hello, my name is Knox. I am 18 years old and currently reside in tu chuan. If you need to contact me, you can reach me at1234567.Thank you!
invalid format
  1. La función fmt::formatter::parse debe devolver el puntero de cola para analizar la cadena formateada.
  2. Dado que la función fmt::formatter::parse es tiempo de ejecución, ciertamente no podemos tener demasiadas operaciones que consuman mucho tiempo, por lo que podemos abreviar la versión anterior del estilo en una sola. Intente utilizar un carácter para representar un formato para reducir las operaciones.
#include <fmt/format.h>
struct Human {
    
    
    std::string name;
    int age;
    std::string address;
    std::string number; // 电话号码
};

// 特例化formatter模板
template <> struct fmt::formatter<Human> {
    
    
    // 自定义格式化风格,比如b->brief 简单介绍
    // d->detail详细介绍
    // 默认风格是简单介绍
    char style = 'b';
    // 这个函数是解析格式化字符串的函数:{arg_id: brief | detail}
    // 解析是从:后面开始的 这里可以实现自己的逻辑,想怎么实现都可以
    constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) 
    {
    
    
        auto it = ctx.begin(), end = ctx.end();
        char ch = *it++;
        if (ch == 'b' || ch == 'd')
        {
    
    
            style = ch;
        }
        // 合规性检查
        if (it != end && *it != '}')
        {
    
    
            throw format_error("invalid format");
        }
        return it;
    }

    // 2.使用指定的风格(简单风格,详细风格)实现格式化函数
    template <typename FormatContext>
    auto format(const Human & human, FormatContext &ctx) const -> decltype(ctx.out()) 
    {
    
    
        // 将内容都格式化到ctx的尾指针中,ctx.out();
        if (style == 'b')
        {
    
    
            fmt::format_to(ctx.out(), "name:{}, age:{}, address:{}, number:{} \n", human.name, human.age, human.address, human.number);
        }
        else if(style == 'd')
        {
    
    
            fmt::format_to(ctx.out(), "Hello, my name is {}. "
                "I am {} years old and currently reside in {}. "
                "If you need to contact me, you can reach me at{}.Thank you!\n",
                human.name, human.age, human.address, human.number);
        }
        else
        {
    
    
            // 都不是我们需要的格式,直接报错
            throw format_error("invalid format");
        }
        return ctx.out();
    }
};

int main()
{
    
    
    Human knox;
    knox.name = "Knox";
    knox.age = 18;
    knox.number = "1234567";
    knox.address = "tu chuan";
    // 简单版本
    fmt::print("{:b}", knox);
    fmt::print("{:d}", knox);
}
  1. Continúe con la clase de formato original de fmt y utilice la sintaxis de formato de la biblioteca fmt.
#include <fmt/format.h>

enum class color {
    
     red, green, blue };

template <> struct fmt::formatter<color> : formatter<string_view>
{
    
    
    //从 formatter<string_view> 继续parse 函数
    template <typename FormatContext>
    auto format(color c, FormatContext &ctx) const 
    {
    
    
        string_view name = "unknown";
        switch (c) {
    
    
        case color::red:   name = "red"; break;
        case color::green: name = "green"; break;
        case color::blue:  name = "blue"; break;
        }
        return formatter<string_view>::format(name, ctx);
    }
};

int main()
{
    
    
    fmt::print("{:<10} |\n", color::red);
    fmt::print("{:^10} |\n", color::green);
    fmt::print("{:>10} |\n", color::blue);
    return 0;
}

Producción:

red        |
  green    |
      blue |

fmt::subyacente

plantilla
constexpr auto fmt::underlying(Enum e) noexcept -> subyacente_t

se convierte al tipo de datos más bajo, observe el código fuente: usando subyacente_t = typename std::underlying_type::type;\

fmt::a_cadena

template
auto fmt::to_string(const T &value) -> std::string

es similar a std::to_string, pero no conserva el 0 final

fmt::unirse

template<typename Range>
auto fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t, Detail::sentinel_t>

devuelve una vista de caracteres, conectada utilizando los caracteres especificados

std::vector<int> v = {
    
    1, 2, 3};
fmt::print("{}", fmt::join(v, ", "));
// Output: "1, 2, 3"
fmt::print("{:02}", fmt::join(v, ", "));
// Output: "01, 02, 03"

plantilla<typename It, typename Sentinel>
auto fmt::join(It comienza, Sentinel final, string_view sep) -> join_view<It, Sentinel>

devuelve los caracteres de formato entre iteradores

std::vector<int> v = {
    
     1, 2, 3, 6, 7 };
fmt::print("{:02}\n", fmt::join(v.begin(), v.end(), ", "));
fmt::print("{:02}\n", fmt::join(v.begin() +2,v.end(), ", "));
fmt::print("{:02}\n", fmt::join(v.begin(), v.end() -2, ", "));

producción

01, 02, 03, 06, 07
03, 06, 07
01, 02, 03

Comparando printf y sprintf en lenguaje C , esta función se encuentra en fmt/printf.h

template<typename S, typename …T>
auto fmt::printf(const S &fmt, const T&… args) -> int

formatea la salida estándar y devuelve el número de caracteres de formato

plantilla<typename S, typename …T, typename Char = char_t<S>>
auto fmt::fprintf(std::FILE *f, const S &fmt, const T&… args) -> int

formateado en archivo

plantilla<nombre de tipo S, nombre de tipo …T, nombre de tipo Char = enable_if_t<detalle::is_string<S>::valor, char_t<S>>>
auto fmt::sprintf(const S &fmt, const T&… args) -> std: :basic_string

格式化到string中

soporte std::ostream

#include <fmt/format.h>
#include <fmt/ostream.h>
struct Human {
    
    
    std::string name;
    int age;
    friend std::ostream &operator<<(std::ostream &os, const Human &human) {
    
    
        return os << human.name << " " <<
            human.age << " years .";
    }
};
template <> struct fmt::formatter<Human> : ostream_formatter {
    
    };
int main()
{
    
    
    fmt::print("the human is {}", Human{
    
     "Knox", 18 });
    // 输出:the human is Knox 18 years .
    return 0;
}

El color de la consola está en el archivo de encabezado fmt/color.h

// 这里多了text_style
template<typename S, typename ...Args>
void fmt::print(const text_style &ts, const S &format_str, const Args&... args);
// 文字的前景色,就是文字本身颜色 fg = foreground
text_style fmt::fg(detail::color_type foreground) noexcept;
// 文字的背景色 bg = background
text_style fmt::bg(detail::color_type background) noexcept;
// color_type 查看fmt::color
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
        "Elapsed time: {0:.2f} seconds", 1.23); // 所有的字体都被加粗,并且字体颜色为红色
// 将指定参数指定为特定的颜色
template<typename T>
auto fmt::styled(const T &value, text_style ts) -> detail::styled_arg<remove_cvref_t<T>>;
// 如
fmt::print("Elapsed time: {0:.2f} seconds",
    fmt::styled(1.23, fmt::fg(fmt::color::green) |  // 仅仅1.23 被定义为绿色的字,蓝色的背景
                        fmt::bg(fmt::color::blue)));

Acerca del formato de tipo de biblioteca estándar en el archivo de encabezado fmt/std.h

fmt::print("{}\n", std::this_thread::get_id());
std::filesystem::path p("/home/data/zhh");
fmt::print("{}", p);

producción

21412
/home/data/zhh

los datos y la hora están en el archivo de encabezado fmt/chrono.h

std::tm fmt::localtime(std::time_t time)
es similar a std::localtime, convierte la hora a la hora del calendario local, que es segura para subprocesos en la mayoría de las plataformas\

std::tm fmt::gmtime(std::time_t time)
y std::gmtime son similares a convertir la hora a hora UTC y son seguros para subprocesos en la mayoría de las plataformas\

std::time_t t = std::time(nullptr);

// Prints "The date is 2020-11-07." (with the current date):
fmt::print("The date is {:%Y-%m-%d %H\:%M:%S}.\n", fmt::localtime(t));
fmt::print("The date is {:%Y-%m-%d %H\:%M:%S}.\n", fmt::gmtime(t));
using namespace std::literals::chrono_literals;

// Prints "Default format: 42s 100ms":
fmt::print("Default format: {} {}\n", 42s, 100ms);

// Prints "strftime-like format: 03:15:30":
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);

producción

The date is 2023-04-13 20:25:13.
The date is 2023-04-13 12:25:13.
Default format: 42s 100ms
strftime-like format: 03:15:30

Supongo que te gusta

Origin blog.csdn.net/qq_33944628/article/details/130142408
Recomendado
Clasificación