I have different versions of the same macro and I want to be able to choose one of them at compile time.
Here is the code I have:
macro_rules! macro_a {
($identifier:ident) => {
println!("A: {}", stringify!($identifier));
}
}
macro_rules! macro_b {
($identifier:ident) => {
println!("B: {}", stringify!($identifier));
}
}
macro_rules! macro_c {
($identifier:ident) => {
println!("C: {}", stringify!($identifier));
}
}
macro_rules! choose_macro {
(a) => {
const CHOSEN_MACRO: u32 = 1;
};
(b) => {
const CHOSEN_MACRO: u32 = 2;
};
(c) => {
const CHOSEN_MACRO: u32 = 3;
};
}
choose_macro!(c);
macro_rules! use_macro {
($identifier:ident) => {
match CHOSEN_MACRO {
1 => macro_a!($identifier),
2 => macro_b!($identifier),
3 => macro_c!($identifier),
_ => unreachable!(),
}
}
}
fn main() {
use_macro!(test);
}
This will print, as expected:
C: test
I wonder if there is a better way to doing this (with macro or attribute or anything else).
It is not clear if the macro is chosen at compile time here. Will Rust remove the match because it is on a constant?
Update: I prefer to choose the macro in the code, not using compiler flags. Also, I do not want to hide the macros that are not chosen: I want to be able to use them using their real name.
I would recommend using conditional compilation flags for something like this. See https://doc.rust-lang.org/book/conditional-compilation.html
In your case, it might look something like this:
#[cfg(feature = "feature_a")]
macro_rules! use_macro {
($identifier:ident) => {
println!("A: {}", stringify!($identifier));
}
}
#[cfg(feature = "feature_b")]
macro_rules! use_macro {
($identifier:ident) => {
println!("B: {}", stringify!($identifier));
}
}
#[cfg(feature = "feature_c")]
macro_rules! use_macro {
($identifier:ident) => {
println!("C: {}", stringify!($identifier));
}
}
fn main() {
use_macro!(test);
}
Then add the following to your Cargo.toml file:
[features]
feature_a = []
feature_b = []
feature_c = []
If you want it to print out "C: test" for example, then run the following:
cargo run --features feature_c
Related
I'm trying to write a macro that will rewrite certain Rust control flow, but I'm having difficulty matching an if expression. The problem is that the predicate is an expression, but an expr is not permitted to be followed by a block or {.
The best I've got is to use tt:
macro_rules! branch {
(
if $pred:tt
$r1:block
else
$r2:block
) => {
if $pred {
$r1
} else {
$r2
}
};
}
Which works fine with single-token or grouped predicates:
branch! {
if (foo == bar) {
1
} else {
2
}
}
But fails if the predicate was not grouped:
branch! {
if foo == bar {
1
} else {
2
}
}
error: no rules expected the token `==`
I also tried to use a repeating pattern of tt in the predicate:
macro_rules! branch {
(
if $($pred:tt)+
$r1:block
else
$r2:block
) => {
if $($pred)+ {
$r1
} else {
$r2
}
};
}
But this produces an error because it's now ambiguous whether subsequent block should match the tt too:
error: local ambiguity: multiple parsing options: built-in NTs tt ('pred') or block ('r1').
Is there a way to do this, or am I stuck with inventing special syntax to use in the macro?
You could use a TT muncher to parse the predicate:
macro_rules! branch {
{
if $($rest:tt)*
} => {
branch_parser! {
predicate = ()
rest = ($($rest)*)
}
};
}
macro_rules! branch_parser {
{
predicate = ($($predicate:tt)*)
rest = ({ $($then:tt)* } else { $($else:tt)* })
} => {
println!("predicate: {}", stringify!($($predicate)*));
println!("then: {}", stringify!($($then)*));
println!("else: {}", stringify!($($else)*));
};
{
predicate = ($($predicate:tt)*)
rest = ($next:tt $($rest:tt)*)
} => {
branch_parser! {
predicate = ($($predicate)* $next)
rest = ($($rest)*)
}
};
}
fn main() {
branch! {
if foo == bar {
1
} else {
2
}
}
}
Output:
predicate: foo == bar
then: 1
else: 2
I'd like to use a macro variable in the macro-generated documentation:
macro_rules! impl_foo {
($name:ident) => {
/// Returns a new `$name`.
fn myfoo() -> $name {
}
};
}
However, the variable won't be substituted. I also tried using the #[doc] attribute:
macro_rules! impl_foo {
($name:ident) => {
#[doc = concat!("Returns a new `", $name, "`.")]
fn myfoo() -> $name {
}
};
}
This one even fails to parse: unexpected token: 'concat'
This can be done using a recursive macro:
macro_rules! impl_foo {
($name:ident, $sname:expr) => {
#[doc = "Returns a new `"]
#[doc = $sname]
#[doc = "`."]
pub fn myfoo() -> $name {
42
}
};
($name:tt) => {
impl_foo!($name, stringify!($name));
};
}
impl_foo!(u32);
fn main() {
println!("Hello, world!");
}
Which renders as:
While the answer #mcarton gave does work perfectly fine for simple examples, it breaks a bit for more complicated ones. Rustdoc seems to insert spaces between the different doc attributes. The markdown processor strips them out most of the time, but sometimes, it transform them to spaces instead. Consider this example:
macro_rules! impl_foo {
($name:ident, $sname:expr) => {
#[doc = "You can call this as `myfoo("]
#[doc = $sname]
#[doc = ")`."]
pub fn myfoo(_: $name) -> $name {
42
}
};
($name:tt) => {
impl_foo!($name, stringify!($name));
};
}
impl_foo!(i32);
fn main() {
println!("Hello, world!");
}
This should generate the documentation "You can call this as myfoo(i32).", but in reality, it results in "You can call this as myfoo( i32 )." (note the additional spaces):
I'm not too sure whether my solution would have worked with the 2017 rustc back when the question was asked, but in modern Rust, this can be done by combining stringify! with concat!:
macro_rules! impl_foo {
($name:tt) => {
#[doc = concat!("You can call this as `myfoo(", stringify!($name), ")`.")]
pub fn myfoo(_: $name) -> $name {
42
}
};
}
impl_foo!(i32);
fn main() {
println!("Hello, world!");
}
This results in the documentation you want (so, without superfluous spaces):
I'm creating an observable and I'm creating the subscription separately:
class CustomQuery {
string Name;
IObservable<int> Occurrences;
}
public IEnumerable<CustomQuery> GatherCustomQueryObservables()
{
yield return new CustomQuery() {
Name = "NameXXX",
Occurrences = Observable.Create<int>(
observer =>
{
int occurrences = this.webservice.GetOccurrences()
observer.OnNext(occurrences);
return System.Reactive.Disposables.Disposable.Empty;
}
);
}
By other hand, there's another method deals with these CustomQueries:
public void CommitCustomQueryObservables(IEnumerable<CustomQuery> queries)
{
foreach (CustomQuery query in queries)
{
query.Occurrences
.Select(o => o)
.SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default)
.ObserveOn(System.Reactive.Concurrency.DispatcherScheduler.Current)
.Subscribe(
occurrences =>
{
string strOccurrences = occurrences > 0 ? occurrences.ToString() : "";
this.Label.Text = strOccurrences;
}
);
}
}
Nevertheless, I'm getting a System.InvalidOperationException exception:
The current thread has no Dispatcher associated with it.
The last line of the stacktrace is at
System.Reactive.Concurrency.DispatcherScheduler.get_Current().
I don't quite figure out how to handle it.
Any ideas?
For Windows Forms you need to use the ControlScheduler for synchronization, not the DispatcherScheduler.
Now you've added the System.Reactive.Windows.Forms package this can be achieved by simply using [observable].ObserveOn([control]); in your example this could be:
public void CommitCustomQueryObservables(IEnumerable<CustomQuery> queries)
{
foreach (CustomQuery query in queries)
{
query.Occurrences
.Select(o => o)
.SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default)
.ObserveOn(this.Label)
.Subscribe(
occurrences =>
{
string strOccurrences = occurrences > 0 ? occurrences.ToString() : "";
this.Label.Text = strOccurrences;
}
);
}
}
In some cases you may want to pass in an argument to a macro which is either some text, or nothing (blank space, as if nothing was written).
Given this starting point:
macro_rules! testme {
($var:ident, $code:block) => {
for i in 0..10 {
let $var = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
}
}
fn main() {
testme!(myvar, {
println!("{}", myvar);
});
}
We may want var to optionally be mutable, assuming the macro body is larger then in the example above, its best not to duplicate the entire macro.
macro_rules! testme {
(private $var:ident, $code:block, $var_qual:tt) => {
for i in 0..10 {
// imagine this is a lot more code :)
let $var_qual $var = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
};
(mut $var:ident, $code:block) => {
testme!(private $var, $code, mut)
};
/*
($var:ident, $code:block) => {
testme!(private $var, $code, )
// ^ how to pass in a blank argument?
};
*/
}
fn main() {
testme!(mut myvar_mut, {
myvar_mut += 10;
println!("{}", myvar_mut);
});
/*
testme!(myvar_immutable, {
println!("{}", myvar_immutable);
});
*/
}
As far as I can tell there is no way to pass in a an empty argument, uncomment the /**/ comments to see the error.
Is it possible to pass in an empty argument to a macro to make an example like this work?
As far as I know its not possible to pass in blank / empty arguments.
It is possible however to pass in a locally defined macro which optionally adds a prefix.
Working example:
macro_rules! testme {
(private $var:ident, $code:block, $var_qual_macro:ident) => {
for i in 0..10 {
// imagine this is a lot more code :)
let $var_qual_macro!($var) = i;
{ $code }
if $var > 5 {
println!("over 5");
}
}
};
(mut $var:ident, $code:block) => {
macro_rules! var_qualifier { ($v:ident) => { mut $v } }
testme!(private $var, $code, var_qualifier)
};
($var:ident, $code:block) => {
macro_rules! var_qualifier { ($v:ident) => { $v } }
testme!(private $var, $code, var_qualifier)
};
}
fn main() {
testme!(mut myvar_mut, {
myvar_mut += 10;
println!("{}", myvar_mut);
});
testme!(myvar_immutable, {
println!("{}", myvar_immutable);
});
}
Take care when nesting macros like this that the name of the macro (var_qualifier in this case) is isn't the same name used inside a different macro since the name will be silently shadowed.
Im trying to make a hashes of hashes to uniquely identify the number that only comes under one set of levels. the hash structure looks something like this :
my %gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
Can some please tell me what is the simplest way to traverse the hash so i can get the value 63.
I have been using
my $x = '';
foreach my $l0 (%gh){
foreach my $l1 (%{$l0}){
foreach my $l2 (%$l1){
foreach my $l3 (%{$l2}){
foreach my $l4 (%$l3){
foreach my $l5 (%{$l4}){
$x = $l5;
}
}
}
}
}
}
This process seems to be working fine . But i was just looking for something simpler and shorter;
Thanks in advance
This will work in your case (only hashes, and plain scalar value at the end)
sub hval {
my ($h) = #_;
return map { ref() ? hval($_) : $_ } values %$h;
}
my $gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
my ($x) = hval($gh);
If you use a reference to a hash instead, here is one way:
use warnings;
use strict;
my $gh = {
'Test1' => {
'level1a' => {
'level2b' => {
'level3a' => {
'level4a' => {
'level5' => '63'
}
}
}
}
}
};
print $gh->{Test1}{level1a}{level2b}{level3a}{level4a}{level5}, "\n";
See also: perldoc perldsc and Data::Diver