Rust: Используйте # [repr (C)] HList, чтобы вывести указатели fmt fn с удаленным типом в статических данных format_args !.

Созданный на 5 сент. 2017  ·  3Комментарии  ·  Источник: rust-lang/rust

Прямо сейчас format_args! использует, например, ArgumentV1::new(&runtime_data, Debug::fmt) (для {:?} ), во время выполнения, используя два указателя на аргумент во время выполнения вместо одного ( &runtime_data ) .

С помощью allow_internal_unsafe и # 44240 мы можем разместить (например, Debug::fmt ) fn указатели в (rvalue-promoted) 'static data, остающееся препятствие - как чтобы сделать вывод о типе данных времени выполнения.
То есть Debug::fmt на самом деле <_ as Debug>::fmt и что _ прямо сейчас подразумевается из-за подписи ArgumentV1::new , вводящей их вместе. Если они разделены, нам нужно что-то новое.

Я предлагаю использовать шаблон HList ( struct HCons<H, T>(H, T); struct HNil; - так что для 3 элементов типов A , B и C вас будет 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 , продвигаемого через rvalue, что является необходимым требованием для перемещения указателей fn в 'static данные вообще.

Для вывода мы можем просто вставить некоторые вызовы функций, чтобы сопоставить типы, например, чтобы вывести B мы могли бы сделать fmt::unify_fn_with_data((list.1).0, &b) , что превратит B в typeof b .

На самом деле может быть проще иметь полностью безопасный интерфейс "построителя", который объединяет HList средств форматирования с HList ссылок среды выполнения, объединяя типы, но я немного беспокоюсь о время компиляции из-за отправки всех признаков - в любом случае влияние следует измерить.


A-fmt C-enhancement I-heavy T-libs

Самый полезный комментарий

Для вывода мы можем просто вставить некоторые вызовы функций, чтобы сопоставить типы, например, чтобы вывести 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 s "параллельно", один с полностью постоянными метаданными, а другой со ссылками на данные времени выполнения, а затем весь вывод типа может происходить из where пункт о fmt::Arguments::new , с нулевым взломом кодогенерации!

РЕДАКТИРОВАТЬ : @ m-ou-se должен был напомнить мне, почему я пошел с трюком явного вывода в первую очередь: аргументы произвольного доступа: разочарован:
(возможно, при использовании достаточного количества константных дженериков ab у нас может быть D: IndexHList<i, Output = T> но это требует больших усилий)

Все 3 Комментарий

@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 s "параллельно", один с полностью постоянными метаданными, а другой со ссылками на данные времени выполнения, а затем весь вывод типа может происходить из where пункт о fmt::Arguments::new , с нулевым взломом кодогенерации!

РЕДАКТИРОВАТЬ : @ m-ou-se должен был напомнить мне, почему я пошел с трюком явного вывода в первую очередь: аргументы произвольного доступа: разочарован:
(возможно, при использовании достаточного количества константных дженериков ab у нас может быть D: IndexHList<i, Output = T> но это требует больших усилий)

У меня есть новая реализация fmt::Arguments , размер которой составляет всего два указателя, с использованием новой формы 'статических метаданных, которые содержат как части строки, так и любые параметры форматирования (если есть). (Так что теперь он умещается в паре регистров, что действительно приятно.) Также для этого требуется только один указатель в стеке на аргумент вместо двух, как @eddyb, по- видимому, уже предлагал три года назад в этом выпуске. ^^ (Полностью пропустил эту проблему, пока @eddyb не указал на нее вчера. ^^ ')

Что еще осталось, так это обновить format_args!() чтобы вместо этого создать этот новый тип, что приведет к проблеме разделения указателя на объект и указателя на функцию (которые в настоящее время находятся вместе в ArgumentV1 ) в качестве указателя на функцию. теперь вместо этого следует использовать статические метаданные. Предложение в этом выпуске выглядит как хороший способ сделать это. Постараюсь реализовать это в ближайшее время. С нетерпением жду перфоманса :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги