I am trying to nest macro calls to ease converting between rust and c ffi types.
The inner macro generates calls to conversion functions from type names like this:
macro_rules! type_conversion {
($arg_name:ident, bool, BOOL) => (crate::type_wrappers::type_conversion::convert_rust_bool($arg_name));
($arg_name:ident, BOOL, bool) => (crate::type_wrappers::type_conversion::convert_c_bool($arg_name));
}
The outer macro now uses this to generate further conversions.
It basically works like this:
macro_rules! outer {
($arg_name:ident, $type1:ty, $type2:ty) => (type_conversion!($arg_name, $type1, $type2));
}
Now, when i try to use the outer macro like this:
fn outer() {
outer!(hello_world, bool, BOOL);
}
I get this Error:
error: no rules expected the token `bool`
--> src\type_wrappers\type_conversion.rs:252:77
|
202 | macro_rules! type_conversion {
| ---------------------------- when calling this macro
...
252 | ($arg_name:ident, $type1:ty, $type2:ty) => (type_conversion!($arg_name, $type1, $type2));
| ^^^^^^ no rules expected this token in macro call
...
256 | outer!(hello_world, bool, BOOL);
| ------------------------------- in this macro invocation
I cannot make sense out of this error, especially since CLions Macro expansion tool expands this into:
type_conversion!(hello_world , bool , BOOL )
Which is what i would expect and also, what the Error suggests.
Where is the Error in those Macros?
I guess the ty macro type cannot be casted to identifier (which bool & BOOL parameter are) and thus matched. So with ident type it works (playground):
macro_rules! outer {
($arg_name:ident, $type1:ident, $type2:ident) => {
type_conversion!($arg_name, $type1, $type2)
};
}
Related
In my project I am using a type definition to hold closures with a specific function signature as shown below:
// Standardized function signature
pub type InternalOperation = impl Fn(Ast, Rc<RefCell<VTable>>, Rc<RefCell<FTable>>) -> Ctr;
pub struct ExternalOperation {
// ...
// The project is on gitlab called relish if you are interested
}
/* A stored function may either be a pointer to a function
* or a syntax tree to eval with the arguments
*/
pub enum Operation {
Internal(InternalOperation),
External(ExternalOperation)
}
// function which does not need args checked
pub struct Function {
pub function: Operation,
// many more things like argument types and function name
}
I attempt to instantiate a function:
pub fn get_export(env_cfg: bool) -> Function {
return Function{
name: String::from("export"),
loose_syms: true,
eval_lazy: true,
args: Args::Lazy(2),
function: Operation::Internal(
|a: Ast, b: Rc<RefCell<VTable>>, c: Rc<RefCell<FTable>>| -> Ctr {
// so much logic here to manage variables in b
// if env_cfg is true, entries in b are tied to environment variables
})
}
}
But I am then greeted with the following error:
error[E0308]: mismatched types
--> src/vars.rs:49:13
|
49 | / |a: Ast, b: Rc<RefCell<VTable>>, c: Rc<RefCell<FTable>>| -> Ctr {
50 | | let inner = a.borrow_mut();
51 | | match &inner.car {
52 | | Ctr::Symbol(identifier) => {
... |
96 | | return Ctr::None;
97 | | }
| |_____________^ expected opaque type, found closure
|
What I have tried:
Declaring InternalOperation as an anonymous function. This works but then I cannot store a closure.
I attempted to use InternalOperation(......) to declare the closure, which is incorrect according to syntax rules
I am using the closure here so that user configuration for my application can be used within the body of the function operation. Is it possible to use the closure in this way, or do I need to refactor my code to apply the env_cfg value in some other way?
I assume the type_alias_impl_trait feature is turned on. If not, this code should not compile anyway because without this feature impl Trait is not allowed inside type aliases.
In theory, this feature should consider any use of the type alias a "defining use", i.e. a use we can infer the opaque type from. In practice, it is not (yet) implemented for many uses. AFAIK, currently only function return types are considered defining use.
In your case, however, you expect the compiler to infer the type from an expression. It should know that Operation::Internal's first field is of type InternalOperation, and thus it should know that InternalOperation is the type of the callback inside get_export(). In theory this should work, except the compiler doesn't (currently) applies this knowledge.
You can workaround this by creating a new function to return the callback:
fn get_callback(env_cfg: bool) -> InternalOperation {
|a: Ast, b: Rc<RefCell<VTable>>, c: Rc<RefCell<FTable>>| -> Ctr {
// so much logic here to manage variables in b
// if env_cfg is true, entries in b are tied to environment variables
}
}
pub fn get_export(env_cfg: bool) -> Function {
Function {
name: String::from("export"),
loose_syms: true,
eval_lazy: true,
args: Args::Lazy(2),
function: Operation::Internal(get_callback(env_cfg)),
}
}
I've been tyring to define a different function for eacu unsigned type in Rust, and of course they have to have different names because Rust can't pick the right one by signature. So I'm trying to give them different names but I was unable to do so:
macro_rules! define_my_function {
($t:ty, $f_name:ident) => {
pub fn concat_idents!(my_function_, $ty)()(
stream: &mut T
) -> Result<$ty, ParseError> {
todo!()
}
}
}
define_my_function!(u8, name_for_u8);
Compiling playground v0.0.1 (/playground)
error: expected one of `(` or `<`, found `!`
--> src/lib.rs:3:29
|
3 | pub fn concat_idents!(my_function_, $ty)()(
| ^ expected one of `(` or `<`
...
11 | define_my_function!(u8, name_for_u8);
| ------------------------------------- in this macro invocation
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9ccea3ddcc75bb2cfab14c52df7bc24f
It looks like this is impossible in Rust. What can I do then? Also I don't want to use concat_idents as it's nightly only
You can use the paste crate, that internally uses a procedural macro to create new identifiers:
macro_rules! define_my_function {
($t:ty) => {
::paste::paste! {
pub fn [<my_function_ $t>]() {
println!(stringify!($t));
}
}
}
}
define_my_function!(u8);
define_my_function!(u16);
fn main() {
my_function_u8();
my_function_u16();
}
Playground
Note that I had to slightly change the code you gave because it wasn't compiling.
From the documentation for std::concat_idents:
Also, as a general rule, macros are only allowed in item, statement or expression position. That means while you may use this macro for referring to existing variables, functions or modules etc, you cannot define a new one with it.
Unfortunately, it seems that at the time this is possible using a procedural macro, or by manually copy-pasting for each numeric type.
I'm trying to create two different versions of the same function, only one of which will be compiled. As an example:
#[cfg(debug_assertions)]
fn do_something(x: usize) -> usize {
x + 1
}
#[cfg(not(debug_assertions))]
fn do_something() -> usize {
0
}
This works fine and I can also call the correct version of do_something if I know which one I'm calling (In practice, the functions will do the exact same thing, the debug one just requires more info for some validation). So I can create two corresponding main functions:
#[cfg(debug_assertions)]
fn main() {
do_something(0);
}
#[cfg(not(debug_assertions))]
fn main() {
do_something();
}
But this is very clumsy, and I want to have only one version of the code that doesn't depend on debug_assertions. I'd like to do something like:
macro_rules! call {
($func:ident, $optional_arg:expr) => {{
if cfg!(debug_assertions) {
$func($optional_arg);
} else {
$func();
}
}};
}
fn main() {
call!(do_something, 0);
}
and then re-use the macro wherever I call this function, or similar ones. But this doesn't compile:
--> main.rs:16:13
|
2 | fn do_something(x: usize) -> usize {
| ---------------------------------- defined here
...
16 | $func();
| ^^^^^-- supplied 0 arguments
| |
| expected 1 argument
...
22 | call!(do_something, 0);
| ----------------------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
I don't understand why I get the error, since the wrong function call shouldn't even be compiled.
The error can be fixed by forcing the functions to have the same signature and simply ignoring the unnecessary argument in the release version, but that doesn't seem like the right way to go.
What would you do in this situation? Why doesn't the macro example compile?
From the reference :
cfg!, unlike #[cfg], does not remove any code and only evaluates
to true or false. For example, all blocks in an if/else expression
need to be valid when cfg! is used for the condition, regardless of
what cfg! is evaluating
Flags will be evaluated in compile time but you are doing this check at runtime. You need to use attributes to avoid the problem:
macro_rules! call {
($func:ident, $optional_arg:expr) => {{
#[cfg(debug_assertions)]
$func($optional_arg);
#[cfg(not(debug_assertions))]
$func();
}};
}
I want to define a variable with a macro ident and pass it to a derive macro, but I get an error:
Error
error: macro expansion ignores token `let` and any following
--> database/src/models.rs:15:9
|
15 | let struct_name = stringify!($name);
| ^^^
...
50 | / model!(Person {
51 | | name: String
52 | | });
| |___- caused by the macro expansion here
|
= note: the usage of `model!` is likely invalid in item context
Code
#[macro_export]
macro_rules! model {
(
$(#[$met: meta])*
$name: ident { $($attr: ident : $typ: ty),* }
) => {
let struct_name = stringify!($name);
//let table_name = struct_name.to_camel_case();
dbg!("{}", struct_name);
#[derive(Debug)]
struct $name {
name: String
};
};
}
model!(Person {
name: String
});
Playground
You use the macro model! where the compiler expects an item (function/struct/trait declaration or impl block). A let statement is only valid inside a code block. This is why your first example works (where the macro call is inside main), while your second example doesn't.
This seems like an instance of the XY problem. Check out procedural macros if you want to run code to generate code at compile time.
I want to write a macro that prints "OK" then returns self in a method. It's my first macro, so I tried this, thinking it will just make something like a text replacement, but it fails:
macro_rules! print_ok_and_return_self {
() => {
println!("OK");
self
}
}
fn main() {
let a = A{};
a.a().a();
}
struct A {}
impl A {
fn a(self) -> Self {
print_ok_and_return_self!()
}
}
Error:
error: macro expansion ignores token `self` and any following
--> src/main.rs:4:13
|
4 | self
| ^^^^
|
note: caused by the macro expansion here; the usage of `print_ok_and_return_self!` is likely invalid in expression context
--> src/main.rs:17:13
|
17| print_ok_and_return_self!()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
After a quick look at the documentation, I know it's not just text replacement, but I still don't know how to make it work.
There are two errors in a row, let's fix the first one.
The syntax for a macro arm is:
(...) => {
...
}
which means that what your macro expands to is:
println!("OK");
self
which is not OK (two statements).
Instead, it should expand to an expression (in this case), which you get by enclosing it within {}:
macro_rules! print_ok_and_return_self {
() => {
{
println!("OK");
self
}
}
}
This leads to the second error:
error[E0424]: `self` is not available in a static method
--> <anon>:4:9
|
4 | self
| ^^^^ not available in static method
...
17 | print_ok_and_return_self!()
| --------------------------- in this macro invocation
|
= note: maybe a `self` argument is missing?
A macro cannot assume the existence of a variable in its scope, so you need to pass self as an argument:
macro_rules! print_ok_and_return_value {
($v:expr) => {{
println!("OK");
$v
}}
}
and the invocation becomes:
impl A {
fn a(self) -> Self {
print_ok_and_return_value!(self)
}
}