fmtlib源码学习:format_arg_store以及fmt::make_format_args(args...)

format_arg_store只是一个数据结构,基本上没什么成员函数,basic_format_args是它的viewer,提供了接口。包括get_id等

#define FMT_HEADER_ONLY
#include <fmt/core.h>
#include <fmt/chrono.h> //时间
#include "fmt/xchar.h" //支持wstring    

void fun()
{
    double const d = 123.45678;
    int x = 10;
    fmt::memory_buffer buf;
    if (1)
    {
        fmt::format_to(std::back_inserter(buf), "{:e},{}", d, x);
    }
    else
    {
        //auto arg0 = fmt::arg("x", 1);
        //fmt::format_to(std::back_inserter(buf), "{:e},{x}", d, arg0);
    }
        
    auto info = to_string(buf);  // "1.234568e+02,10"
}
template <typename OutputIt, typename... T,
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
    -> OutputIt {
  return vformat_to(out, fmt, fmt::make_format_args(args...));
}

 注意,是fmt::make_format_args(args...)而不是 fmt::make_format_args(args)...

前者是fmt::make_format_args(args0, args1, args2...)

后者是fmt::make_format_args(args0), fmt::make_format_args(args1), ...

所以针对实际的参数,展开之后:

针对实际的参数,展开之后:

template <typename OutputIt, typename... T,
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
    -> OutputIt {
  return vformat_to(

out, fmt, 
fmt::make_format_args(123.456, 10)

);
}


之后的逻辑,fmt::make_format_args会把数据保存为:arg_data
detail::arg_data data_;

 make_format_args的定义:

/**
  \rst
  Constructs a `~fmt::format_arg_store` object that contains references to
  arguments and can be implicitly converted to `~fmt::format_args`. `Context`
  can be omitted in which case it defaults to `~fmt::context`.
  See `~fmt::arg` for lifetime considerations.
  \endrst
 */
template <typename Context = format_context, typename... Args>
constexpr auto make_format_args(Args&&... args)
    -> format_arg_store<Context, remove_cvref_t<Args>...> {
  return {std::forward<Args>(args)...};
}

所以

template <typename Context = format_context, typename... Args>
constexpr auto make_format_args(Args&&... args)
    -> format_arg_store<Context, double, int> 
{
  return {123.456, 10};
}

那么是用{123.456, 10}这个参数包,
来初始化一个(注意,就是一个)format_arg_store<Context, double, int> 

注意,Context是默认的类型:format_context,定义为:

template <typename Char>
using buffer_context =
    basic_format_context<detail::buffer_appender<Char>, Char>;
using format_context = buffer_context<char>;

具体是:

basic_format_context<detail::buffer_appender<char>, char>

但需要注意的是,它在参数列表里不是最后一个。一般默认类型都是最后一个。可能跟后面的参数是变长有关

这个函数是把参数args生成format_arg_store

注意remove_cvref_t<Args>...,它的意思是把每个参数都给去掉const等信息,然后用逗号分隔:

remove_cvref_t<Args0>, remove_cvref_t<Args1>, remove_cvref_t<Args2>, ...

比如args展开是const double, int&, const std::string

remove_cvref_t<Args>...的结果就是double, int, std::string

format_to函数的形参的推导结果:

fmt::v8::format_to<
std::back_insert_iterator<fmt::v8::basic_memory_buffer<char,500,std::allocator<char> > >,
double const &,int &,0
>

(
std::back_insert_iterator<fmt::v8::basic_memory_buffer<char,500,std::allocator<char> > > out={...}, 
fmt::v8::basic_format_string<char,double const &,int &> fmt={...}, 
const double & <args_0>=123.45677999999999, 
int & <args_1>=10
) 


OutputIt: std::back_insert_iterator<fmt::v8::basic_memory_buffer<char,500,std::allocator<char> > > out={...}, 

format_string<T...>:
fmt::v8::basic_format_string<char,double const &,int &> fmt={...}, 

T&&...:
const double & <args_0>=123.45677999999999, 
int & <args_1>=10

make_format_args函数的推导结果:

fmt::v8::make_format_args
<
    fmt::v8::basic_format_context<fmt::v8::appender,char>, double, int
>

(
    const double & <args_0>, 
    const int & <args_1>
)


Context:fmt::v8::basic_format_context<fmt::v8::appender,char>, double, int

Args:const double&, const int&

 去除掉一些“干扰”的代码,剩下:

template <typename Context, typename... Args>
class format_arg_store
{
 private:
  static const size_t num_args = sizeof...(Args);
  static const size_t num_named_args = detail::count_named_args<Args...>();
  static const bool is_packed = num_args <= detail::max_packed_args;

  using value_type = conditional_t<is_packed, detail::value<Context>,
                                   basic_format_arg<Context>>;

  detail::arg_data<value_type, typename Context::char_type, num_args, num_named_args>
      data_;

 public:
//构造函数,对data_进行初始化
 format_arg_store(const Args&... args)
      : data_{detail::make_arg<
            is_packed, Context,
            detail::mapped_type_constant<Args, Context>::value>(args)...} {
    detail::init_named_args(data_.named_args(), 0, 0, args...);
  }
};

实例化后的样子:

fmt::v8::format_arg_store<
fmt::v8::basic_format_context<fmt::v8::appender,char>,double,int>::format_arg_store
<同上>
(
const double & <args_0>, 
const int & <args_1>
)

其中:

num_args = sizeof...(Args);的结果是:2

is_packed = num_args <= detail::max_packed_args;的结果是:0

data_是用detail::make_arg<
            is_packed, Context,
            detail::mapped_type_constant<Args, Context>::value>(args)...

来初始化的。抛开干扰因素,就是data_{detail::make_arg<>(args)...},展开就类似于:

data_{detail::make_arg<>(args0), detail::make_arg<>(args1), detail::make_arg<>(args2),...}

当前有两个参数,一个是const double&,一个是const int&

所以最后初始化data_的实际样子就是:

data_{

    detail::make_arg
    <
        is_packed, Context,
        detail::mapped_type_constant<const double&, Context>::value
    >
    (123.45678),

    detail::make_arg
    <
        is_packed, Context,
        detail::mapped_type_constant<int&, Context>::value
    >
    (10)

}

简化一下:
data_{

    detail::make_arg(123.45678),
    detail::make_arg(10)

}

detail::make_arg的定义,它会返回Value类对象,其实它是一个union。

刨除掉干扰代码后,如下:

template <bool IS_PACKED, typename Context, type, typename T,
          FMT_ENABLE_IF(IS_PACKED)>
auto make_arg(const T& val) -> value<Context> {
  const auto& arg = arg_mapper<Context>().map(val);
  return {arg};
}

所以,data_最后的初始化是:

data_{value<Context>, value<Context>}

第一个参数是double类型的,所以const auto& arg = arg_mapper<Context>().map(val);返回了一个double值:

auto map(double val) -> double { return val; }

但make_arg要求返回Value<Context>类型的值,所以会用这个double来初始化这个Value。

看看Value的定义,发现是个Union

// A formatting argument value.
template <typename Context> class value {
 public:
  using char_type = typename Context::char_type;

  union {
    monostate no_value;
    int int_value;
    unsigned uint_value;
    long long long_long_value;
    unsigned long long ulong_long_value;
    int128_t int128_value;
    uint128_t uint128_value;
    bool bool_value;
    char_type char_value;
    float float_value;
    double double_value;
    long double long_double_value;
    const void* pointer;
    string_value<char_type> string;
    custom_value<Context> custom;
    named_arg_value<char_type> named_args;
  };

value(float val) : float_value(val) {}
value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {}
。。。

value(const named_arg_info<char_type>* args, size_t size)
      : named_args{args, size} {}

  template <typename T> value(const T& val) {
    custom.value = &val;
    // Get the formatter type through the context to allow different contexts
    // have different extension points, e.g. `formatter<T>` for `format` and
    // `printf_formatter<T>` for `printf`.
    custom.format = format_custom_arg<
        T, conditional_t<has_formatter<T, Context>::value,
                         typename Context::template formatter_type<T>,
                         fallback_formatter<T, char_type>>>;
  }

};

注意,里面还有如下的类型:

const void* pointer;
string_value<char_type> string;
custom_value<Context> custom;
named_arg_value<char_type> named_args;

当用double类型的数据来初始化Value时,

value(double val) : double_value(val) {}

之后调用到arg_data的构造函数:

detail::make_arg<
    is_packed,    //第一个参数

    Context,  //第二个参数
    detail::mapped_type_constant<Args, Context>::value //第三个参数

>(args)...

arg_data的泛化版本:
template <typename T, typename Char, size_t NUM_ARGS/*参数的个数,比如2个*/,
size_t NUM_NAMED_ARGS>
struct arg_data {
  // args_[0].named_args points to named_args_ to avoid bloating format_args.
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];

  template <typename... U>
  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
  arg_data(const arg_data& other) = delete;
  auto args() const -> const T* { return args_ + 1; }
  auto named_args() -> named_arg_info<Char>* { return named_args_; }
};


其中:
named_arg_info的定义:
template <typename Char> struct named_arg_info {
  const Char* name;
  int id;
};


最后走的是arg_data的这个偏特化版本:
template <typename T, typename Char, size_t NUM_ARGS>
struct arg_data<T, Char, NUM_ARGS, 0> {
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
//如果参数是2个,那么就是T args_[2];,比如存放的是123.456和10,用Value的方式保存
//如果参数是3个,那么就是T args_[2];
  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];

  template <typename... U>
  arg_data(const U&... init) : args_{init...} {} //程序走到这里

  auto args() const -> const T* { return args_; }
  auto named_args() -> std::nullptr_t {
    return nullptr;
  }
};

注意唯一的成员变量是个数组:T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];

这个NUM_ARGS在前面已经用sizeof...(Args)推导出来了,是2。

注意args_{init...}的写法,一个数组可以用{}来初始化吗?答案是可以的。比如

int xxx[3] {3,6,1};
那么这里的arg_data是个偏特化版本:
arg_data<T, Char, NUM_ARGS, 0>

其中:

T: value<Context>
Context:fmt::v8::basic_format_context<fmt::v8::appender,char> //默认类型
Char: char  //
NUM_ARGS:2    //sizeof计算而来
NUM_NAMED_ARGS: 0 //显式指定的值

arg_data的构造函数实例化之后:

arg_data<value<Context>,char,2,0>::
arg_data<同上>
<value<Context >,value<Context > > 
//由于arg_data的构造函数也是个模板,所以这里也根据传进来的变长参数给实例化了template <typename... U>
//一个是包含的double类型数据,一个是int类型数据
(
const value<Context > & <init_0>, 
const value<Context > & <init_1>
)

这样的话,成员变量T args_[NUM_ARGS]

就变成了:value<Context> args_[2];

在这里,数据的类型被擦除了。

detail::arg_data<detail::value<basic_format_context<appender,char> >,char,2,0>::
        arg_data<detail::value<basic_format_context<appender,char> >,char,2,0>
		<detail::value<basic_format_context<appender,char> >,
		 detail::value<basic_format_context<appender,char> > 
		>
(
const detail::value<basic_format_context<appender,char> > & <init_0>, 
const detail::value<basic_format_context<appender,char> > & <init_1>
)

稍作简化:
detail::arg_data<detail::value<Context >,char,2,0>::
        arg_data<detail::value<Context >,char,2,0>
<detail::value<Context >,detail::value<Context > >
(
const detail::value<Context > & <init_0>, 
const detail::value<Context > & <init_1>
)

解下来detail::init_named_args函数里是空的:

template <typename... Args>
void init_named_args(std::nullptr_t, int, int, const Args&...) {
}

std::make_shared函数:

template<class _Ty,
	class... _Types>
	_NODISCARD inline shared_ptr<_Ty> make_shared(_Types&&... _Args)
	{	// make a shared_ptr
	const auto _Rx = new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);

	shared_ptr<_Ty> _Ret;
	_Ret._Set_ptr_rep_and_enable_shared(_Rx->_Getptr(), _Rx);
	return (_Ret);
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值