2017-02-11 2 views
1

При расширении аргументов макросу есть способ включить номер аргумента в макросИспользование номера аргумента в расширении макросов?

Вот полный пример, показывающий, как это можно использовать для назначения индекса структуре с использованием признака. В настоящее время struct_number() всегда возвращает 0, можно ли это сделать, чтобы вернуть постоянное число в соответствии с порядком аргумента макросу?

struct Foo {_var: bool} 
struct Bar {_var: u8} 
struct Baz {_var: i16} 

trait NumberStruct { 
    fn struct_number() -> usize; 
} 

macro_rules! number_structs_impl { 
    ($($t:ty)*) => ($(
     impl NumberStruct for $t { 
      fn struct_number() -> usize { 
       // How to return a number based on the argument order? 
       return 0; 
      } 
     } 
    )*) 
} 

number_structs_impl!(Foo Bar Baz); 

fn main() { 
    // see if the numbers are correct 
    macro_rules! print_numbers { 
     ($($t:tt)*) => ($(
      print!("{}:{} ", stringify!($t), $t::struct_number()); 
     )*) 
    } 

    // should print: 
    // Baz:2 Bar:1 Foo:0 
    print_numbers!(Baz Bar Foo); 
    println!(); 
} 

ответ

0

Это может быть сделано:

  • Сначала подсчитывать все аргументы.
  • Использование рекурсивного макроса, поэтому аргументы tail* можно посчитать.
  • Необязательно хранить список структур в макросе, чтобы оба макросочетания не нуждались в повторении списка.

Рабочий пример:

struct Foo {_var: bool} 
struct Bar {_var: u8} 
struct Baz {_var: i16} 

trait NumberStruct { 
    fn struct_number() -> usize; 
} 

macro_rules! count_tts { 
    () => {0usize}; 
    ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)}; 
} 

macro_rules! number_structs_impl { 
    () => {}; 
    ($head:tt $($tail:tt)*) => { 
     impl NumberStruct for $head { 
      fn struct_number() -> usize { 
       return STRUCT_NUM - (1 + count_tts!($($tail)*)); 
      } 
     } 
     number_structs_impl!($($tail)*); 
    }; 
} 

// avoid repeating same structs 
macro_rules! apply_structs { 
    ($macro_id:ident) => (
     $macro_id! { 
      Foo 
      Bar 
      Baz 
     } 
    ) 
} 

const STRUCT_NUM: usize = apply_structs!(count_tts); 
apply_structs!(number_structs_impl); 

fn main() { 
    // see if the numbers are correct 
    macro_rules! print_numbers { 
     ($($t:tt)*) => ($(
      print!("{}:{} ", stringify!($t), $t::struct_number()); 
     )*) 
    } 

    // should print: 
    // Baz:2 Bar:1 Foo:0 
    print_numbers!(Baz Bar Foo); 
    println!(); 
} 

Примечание: Я вывешиваю этот ответ, чтобы показать его можно, однако это немного грязное решение, так как она включает в себя прохождение макросов макросов и два invocations, это можно сделать более чисто, если бы рекурсивные макросы могли быть расширены, взяв последний аргумент, каждый рекурсивный, see related question.

0

Подход к определению нумерованных реализаций заключается в использовании рекурсивного макроса. Уникальный номер может быть создан counting arguments, в этом случае подсчет возвращающих аргументов.

Проблема с этим: индексы меняются на противоположные, где первая структура имеет наибольшее число, последняя структура равна нулю.

Если вам нужны только номера, чтобы быть уникальными, это не имеет значения, однако в этом случае я хочу, чтобы каждый индекс structs соответствовал порядку его передачи макросу.

Входные аргументы могут быть изменены с помощью рекурсивного макроса, see this example.

Используя этот макрос, его можно написать общий макрос:

apply_args_reverse!(macro_name, arg1 arg2 arg3) 

который расширяется в:

macro_name!(arg3 arg2 arg1) 

Конечно это еще не очень полезно само по себе, но это может быть полезно, если аргументы не записываются напрямую, а передаются как аргументы.

Это может быть использован для создания сделать макрос, который расширяется с числом каждого аргумента следующим образом:

struct Foo {_var: bool} 
struct Bar {_var: u8} 
struct Baz {_var: i16} 

trait NumberStruct { 
    fn struct_number() -> usize; 
} 

macro_rules! count_args_space { 
    () => {0_usize}; 
    ($_head:tt $($tail:tt)*) => {1_usize + count_args_space!($($tail)*)}; 
} 

macro_rules! number_structs_impl { 
    (@single $t:tt $($tail:tt)*) => (
     impl NumberStruct for $t { 
      fn struct_number() -> usize { 
       return count_args_space!($($tail)*); 
      } 
     } 
    ); 

    () => {}; 
    ($head:tt $($tail:tt)*) => { 
     number_structs_impl!(@single $head $($tail)*); 
     number_structs_impl!($($tail)*); 
    }; 
} 

macro_rules! apply_args_reverse { 
    ($macro_id:tt [] $($reversed:tt)*) => { 
     $macro_id!($($reversed) *); 
    }; 
    ($macro_id:tt [$first:tt $($rest:tt)*] $($reversed:tt)*) => { 
     apply_args_reverse!($macro_id [$($rest)*] $first $($reversed)*); 
    }; 
    // Entry point, use brackets to recursively reverse above. 
    ($macro_id:tt, $($t:tt)*) => { 
     apply_args_reverse!($macro_id [ $($t)* ]); 
    }; 
} 

// Note that both commands below work, and can be swapped to reverse argument order. 

// number_structs_impl!(Foo Bar Baz); 
apply_args_reverse!(number_structs_impl, Foo Bar Baz); 

fn main() { 
    // see if the numbers are correct 
    macro_rules! print_numbers { 
     ($($t:tt)*) => ($(
      print!("{}:{} ", stringify!($t), $t::struct_number()); 
     )*) 
    } 

    print_numbers!(Baz Bar Foo); 
    println!(); 
} 

Обратите внимание на заявлении:

number_structs_impl!(Foo Bar Baz); 

... и

apply_args_reverse!(number_structs_impl, Foo Bar Baz); 

...являются взаимозаменяемыми, обмен, который комментируется, меняет порядок номеров, присвоенных каждой структуре.


Примечание: держать мою other answer, в то время как это более кратким, но она более хрупкая, склонная к труднодоступные устранения проблем, так как макрос расширения становится глубоко вложенным (я нашел это при получении его на работу в наименее).

Смежные вопросы