现在format_args!
使用,例如ArgumentV1::new(&runtime_data, Debug::fmt)
(对于{:?}
),在运行时,在运行时每个参数使用两个指针而不是一个( &runtime_data
) .
使用allow_internal_unsafe
和 #44240,我们可以将(例如Debug::fmt
) fn
指针放在(rvalue-promoted) 'static
数据中,剩下的障碍是如何推断运行时数据的类型。
也就是说, Debug::fmt
真的是<_ as Debug>::fmt
并且_
现在被推断出来,因为ArgumentV1::new
的签名将它们组合在一起。 如果它们是分开的,我们需要一些新的东西。
我建议使用HList
模式( struct HCons<H, T>(H, T); struct HNil;
- 所以对于A
、 B
和C
类型的 3 个元素HCons<A, HCons<B, HCons<C, HNil>>>
),与#[repr(C)]
,这将为其提供与数组匹配的确定性布局,即这两个:
&'static HCons<fn(&A), HCons<fn(&B), HCons<fn(&C), HNil>>>
&'static [unsafe fn(*const Opaque); 3]
具有相同的表示形式,后者可以取消大小为切片。 这种从HList
到数组(然后是切片)的转换可以在安全的、右值提升的HCons
之上执行,这是移动fn
指针的必要条件完全变成'static
数据。
对于推断,我们可以简单地插入一些函数调用来匹配类型,例如推断B
我们可以执行fmt::unify_fn_with_data((list.1).0, &b)
,这将使B
变成typeof b
.
它实际上可能是简单的有一个完全安全的“建设者”的界面,它结合了HList
用格式化的HList
运行时引用的,统一的类型,但我有点担心由于所有特征分派而导致的编译时间 - 在任何情况下,都应该测量影响。
@rustbot声明
对于推断,我们可以简单地插入一些函数调用来匹配类型,例如推断
B
我们可以执行fmt::unify_fn_with_data((list.1).0, &b)
,这将使B
变成typeof b
.
不知道我在想什么,它应该比那容易得多!
struct ArgMetadata<T: ?Sized> {
// Only `unsafe` because of the later cast we do from `T` to `Opaque`.
fmt: unsafe fn(&T, &mut Formatter<'_>) -> Result,
// ... flags, constant string fragments, etc.
}
// TODO: maybe name this something else to emphasize repr(C)?
#[repr(C)]
struct HCons<T, Rest>(T, Rest);
// This would have to be in a "sealed module" to make it impossible to implement on more types.
trait MetadataFor<D> {
const LEN: usize;
}
impl MetadataFor<()> for () {
const LEN: usize = 0;
}
impl<'a, T: ?Sized, D, M> MetadataFor<HCons<&'a T, D>> for HCons<ArgMetadata<T>, M>
where M: MetadataFor<D>
{
const LEN: usize = M::LEN;
}
impl<'a> Arguments<'a> {
fn new<M, D>(meta: &'a M, data: &'a D) -> Self
where M: MetadataFor<D>
{
Self {
meta: unsafe { &*(meta as *const _ as *const [ArgMetadata<Opaque>; M::LEN]) },
data: unsafe { &*(data as *const _ as *const [&Opaque; M::LEN]) },
}
}
}
即我们“并行”构建两个HList
,一个具有完全不变的元数据,另一个具有对运行时数据的引用,然后所有类型推断都可以来自where
fmt::Arguments::new
上的子句,代码生成为零!
编辑:@m-ou-se 不得不提醒我为什么我首先使用显式推理技巧:随机访问参数:失望:
(也许有足够的 const 泛型ab用法,我们可以有D: IndexHList<i, Output = T>
但这需要很多努力)
我有一个fmt::Arguments
的新实现,通过使用包含字符串片段和任何格式选项(如果有)的新形式的“静态元数据”,它的大小只有两个指针。 (所以它现在适合一个寄存器对,这真的很好。)它也只需要每个参数堆栈上的一个指针而不是两个,正如三年前@eddyb显然已经在这个问题中建议的那样。 ^^(完全错过了这个问题,直到昨天@eddyb指出。^^')
剩下的就是更新format_args!()
以生成这个新类型,这会遇到拆分对象指针和函数指针(目前在ArgumentV1
)作为函数指针的问题现在应该进入“静态元数据”。 这个问题中的建议看起来是一个很好的方法。 将尽快实施。 期待性能运行:)
最有用的评论
不知道我在想什么,它应该比那容易得多!
即我们“并行”构建两个
HList
,一个具有完全不变的元数据,另一个具有对运行时数据的引用,然后所有类型推断都可以来自where
fmt::Arguments::new
上的子句,代码生成为零!编辑:@m-ou-se 不得不提醒我为什么我首先使用显式推理技巧:随机访问参数:失望:
(也许有足够的 const 泛型
ab用法,我们可以有D: IndexHList<i, Output = T>
但这需要很多努力)