std::wstring s{L"456"};
std::wstring str = fmt::format(L"{}hello{}", 123, s);
return 1;
实例化的Context就是:
fmt::v8::basic_format_context<std::back_insert_iterator<fmt::v8::detail::buffer<wchar_t> >,wchar_t>
后面的参数,123和s,在编译的时候会被放到一个叫data_的数组里
data_{detail::make_arg<>(123), detail::make_arg<>(L"456")};
-> data_{value<Context>(123), value<Context>(L"456")};
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> {
//注意这个地方,map是一些重载函数。当参数是123时,走的map是:
//auto map(int val) -> int { return val; }
//为什么要用const auto& arg,防止拷贝动作?
const auto& arg = arg_mapper<Context>().map(val);
return {arg};
}
//虽然看起来arg_mapper::map的很多定义,都是直接把值按原样返回。
//但其实arg_mapper::map应该是为了让各种数据类型返回能让value<Context>接受的类型:
//观察一下map众多定义中的其中一个定义:如果T类型是string,则返回basic_string_view
template <typename T, FMT_ENABLE_IF(is_string<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view<char_type> {
static_assert(std::is_same<char_type, char_t<T>>::value,
"mixing character types is disallowed");
return to_string_view(val);
}
先看arg_mapper的定义,有个char_type:
// Maps formatting arguments to core types.
template <typename Context> struct arg_mapper {
using char_type = typename Context::char_type;
...
};
再看value的定义,也有个char_type,从一定程度上可以看出,arg_mapper返回
的类型也能被value所接受:
// A formatting argument value.
template <typename Context> class value {
public:
using char_type = typename Context::char_type;
...
};
make_arg的各个参数
template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(IS_PACKED)>
在参数为L"456"时的实例:
fmt::v8::detail::make_arg<
1, /*IS_PACKED*/
fmt::v8::basic_format_context<std::back_insert_iterator<fmt::v8::detail::buffer<wchar_t> >,wchar_t>, /*Context*/
13, /*type*/
std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >, /*T*/
0 /*FMT_ENABLE_IF(IS_PACKED)*/
>
在咱们这个例子中,arg_data有两个版本,最后走的是特化版本,NUM_NAMED_ARGS为0,就是没有
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
的那个版本,只有
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
可能是因为没有带名字的NAMED_ARGS
因为NUM_NAMED_ARGS为0,所以编译器匹配了特化版本:
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.
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
template <typename... U>
FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
return nullptr; //由于没有带名字的参数(named_arg),所以返回空
}
};
至于arg_data的构造函数为什么又是个模板函数,还没搞明白。
template <typename... U>
FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
arg_data的变量定义:
using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>;
由于is_packed是1,所以value_type就是value<Context>
detail::arg_data<
value_type, //value<Context>,见下
typename Context::char_type, //wchar_t
num_args, //2
num_named_args //0
>
data_;
实例化之后:
fmt::v8::detail::arg_data<
fmt::v8::detail::value<fmt::v8::basic_format_context<std::back_insert_iterator<fmt::v8::detail::buffer<wchar_t> >,wchar_t> >, //这个就是所谓的value<Context>
wchar_t,
2,
0
>
把数据转换成字符串的逻辑在write函数里,在format.h里,
有很多针对各种数据类型的利用sfinae进行分支的write。
比如下面就是针对整数的,注意有个is_integral<T>::value。
先用reserve申请一段长度,长度的数值是count_digits计算出来的。比如整数123,
count_digits的值就是3.
format_decimal函数里进行转换。
format_decimal<Char>(
ptr, //缓冲区。存放最后的结果,也就是"123"
abs_value, //数值:123
num_digits //数值:3
);
最后的结果就在out里。
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value &&
!std::is_same<T, Char>::value)>
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
bool negative = is_negative(value);
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
if (negative) abs_value = ~abs_value + 1;
int num_digits = count_digits(abs_value);
auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
auto it = reserve(out, size);
if (auto ptr = to_pointer<Char>(it, size)) {
if (negative) *ptr++ = static_cast<Char>('-');
format_decimal<Char>(ptr, abs_value, num_digits);
return out;
}
if (negative) *it++ = static_cast<Char>('-');
//具体的写入字符串的,就是这个函数
it = format_decimal<Char>(it, abs_value, num_digits).end;
return base_iterator(out, it);
}
format_arg_store只是一个数据结构,基本上没什么成员函数,basic_format_args是它的viewer,提供了接口。包括get_id等。就保存了数据的指针:const detail::value<Context>* values_;
它来自:arg_data里的数组:T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
/**
\rst
A view of a collection of formatting arguments. To avoid lifetime issues it
should only be used as a parameter type in type-erased functions such as
``vformat``::
void vlog(string_view format_str, format_args args); // OK
format_args args = make_format_args(42); // Error: dangling reference
\endrst
*/
template <typename Context> class basic_format_args {
...
};
#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的定义:
尾部返回提供类型:format_arg_store
/**
\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...);
}
};
template <typename Char> struct named_arg_info {
const Char* name;
int id;
};
template <typename T, typename Char, size_t NUM_ARGS, 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_; }
};
实例化后的样子:
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);
}