Context-aware function-like macro

Hi all.

I'm writing my first set of macros. So far so good, but there's one thing I'd like to do but I don't know if it's doable.

struct First {
	#[field_macro(identifier = ID_1)]
	value_1: i32,
	#[field_macro(identifier = ID_2)]
	value_2: i32
}

struct Second {
	#[field_macro(identifier = ID_1)]
	value_1: f32,
	#[field_macro(identifier = ID_2)]
	value_2: f32
}

My four invocations of the field_macro attribute macro will generate the code to declare four constants:

const FIRST_STRUCT_ID_1: usize = 10;
const FIRST_STRUCT_ID_2: usize = 12;
const SECOND_STRUCT_ID_1: usize = 13;
const SECOND_STRUCT_ID_2: usize = 14;

So far I know what to do.

My next plan is to write a function-like macro, let's call it pick_identifier, that replaces the correct constant based in the impl block where the macro is called in. For example:

impl First {
	fn get_value(&self, id: usize) -> i32 {
		match id {
			pick_identifier!(ID_1) /* replaced with FIRST_STRUCT_ID_1 */ => self.value_1,
			pick_identifier!(ID_2) /* replaced with FIRST_STRUCT_ID_2 */ => self.value_2
		}
	}
}

impl Second {
	fn get_value(&self, id: usize) -> f32 {
		match id {
			pick_identifier!(ID_1) /* replaced with SECOND_STRUCT_ID_1 */ => self.value_1,
			pick_identifier!(ID_2) /* replaced with SECOND_STRUCT_ID_2 */ => self.value_2
		}
	}
}

This example is incomplete and not working, but you get the idea.

Is it possible to do something like this or should I squeeze my brain hoping for a better idea?

Thanks!

No. The only thing macros get as input is the tokens you provide them when you invoke them. There is no context.

Aside: I'm not sure you can do the field_macro bit, either. Even if you can attach attribute macros to struct fields (I don't know off-hand), you can't emit a constant in that position. That's another limitation: macros can only expand to something that would be valid in the invocation position if it were written out directly.

If I were doing this, I'd use associated constants and access them via Self.

struct First { value_1: i32, value_2: i32 }

impl First {
    const ID_1: usize = 1;
    const ID_2: usize = 2;

	fn get_value(&self, id: usize) -> i32 {
		match id {
			Self::ID_1 => self.value_1,
			Self::ID_2 => self.value_2,
			_ => panic!("you wot?")
		}
	}
}

fn main() {}

I haven't thought about associated constants... I knew they existed but I never used them before. It's very likely they fit my case.

Thanks a lot!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.