Skip to content

Commit

Permalink
Add libc_bitflags convenience macro
Browse files Browse the repository at this point in the history
We define many bitflags types with values from the libc crate. Currently
these look like this:

    bitflags!{
        flags ProtFlags: libc::c_int {
            const PROT_NONE      = libc::PROT_NONE,
            const PROT_READ      = libc::PROT_READ,
            const PROT_WRITE     = libc::PROT_WRITE,
            const PROT_EXEC      = libc::PROT_EXEC,
            #[cfg(any(target_os = "linux", target_os = "android"))]
            const PROT_GROWSDOWN = libc::PROT_GROWSDOWN,
            #[cfg(any(target_os = "linux", target_os = "android"))]
            const PROT_GROWSUP   = libc::PROT_GROWSUP,
        }
    }

There's some repetition which is tedious. With the new macro, the above
can instead be written

    libc_bitflags!{
        flags ProtFlags: libc::c_int {
            PROT_NONE,
            PROT_READ,
            PROT_WRITE,
            PROT_EXEC,
            #[cfg(any(target_os = "linux", target_os = "android"))]
            PROT_GROWSDOWN,
            #[cfg(any(target_os = "linux", target_os = "android"))]
            PROT_GROWSUP,
        }
    }

Thanks to Daniel Keep for the Little Book of Rust Macros, and for
helping with this macro.

Refs nix-rust#264
  • Loading branch information
kamalmarhubi committed Mar 16, 2016
1 parent 97157b4 commit b4c9f5b
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 3 deletions.
26 changes: 23 additions & 3 deletions CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,32 @@ When creating newtypes, we use Rust's `CamelCase` type naming convention.

## Bitflags

We represent sets of constants that are intended to be combined using bitwise
operations as parameters to functions by types defined using the `bitflags!`
macro from the [bitflags crate][bitflags].
Many C functions have flags parameters that are combined from constants using
bitwise operations. We represent the types of these parameters by types defined
using our `libc_bitflags!` macro, which is a convenience wrapper around the
`bitflags!` macro from the [bitflags crate][bitflags] that brings in the
constant value from `libc`.

We name the type for a set of constants whose element's names start with `FOO_`
`FooFlags`.

For example,

```rust
libc_bitflags!{
flags ProtFlags : libc::c_int {
PROT_NONE,
PROT_READ,
PROT_WRITE,
PROT_EXEC,
#[cfg(any(target_os = "linux", target_os = "android"))]
PROT_GROWSDOWN,
#[cfg(any(target_os = "linux", target_os = "android"))]
PROT_GROWSUP,
}
}
```


## Enumerations

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ extern crate cfg_if;
#[cfg(test)]
extern crate nix_test as nixtest;

#[macro_use] mod macros;

// In rust 1.8+ this should be `pub extern crate libc` but prior
// to https://github.com/rust-lang/rust/issues/26775 being resolved
// it is necessary to get a little creative.
Expand Down
188 changes: 188 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/// The `libc_bitflags!` macro helps with a common use case of defining bitflags with values from
/// the libc crate. It is used the same way as the `bitflags!` macro, except that only the name of
/// the flag value has to be given.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```
/// libc_bitflags!{
/// flags ProtFlags: libc::c_int {
/// PROT_NONE,
/// PROT_READ,
/// PROT_WRITE,
/// PROT_EXEC,
/// #[cfg(any(target_os = "linux", target_os = "android"))]
/// PROT_GROWSDOWN,
/// #[cfg(any(target_os = "linux", target_os = "android"))]
/// PROT_GROWSUP,
/// }
/// }
/// ```
macro_rules! libc_bitflags {
// (non-pub) Exit rule.
(@call_bitflags
{
name: $BitFlags:ident,
type: $T:ty,
attrs: [$($attrs:tt)*],
flags: [$($flags:tt)*],
}
) => {
bitflags! {
$($attrs)*
flags $BitFlags: $T {
$($flags)*
}
}
};

// (pub) Exit rule.
(@call_bitflags
{
pub,
name: $BitFlags:ident,
type: $T:ty,
attrs: [$($attrs:tt)*],
flags: [$($flags:tt)*],
}
) => {
bitflags! {
$($attrs)*
pub flags $BitFlags: $T {
$($flags)*
}
}
};

// (non-pub) Done accumulating.
(@accumulate_flags
{
name: $BitFlags:ident,
type: $T:ty,
attrs: $attrs:tt,
},
$flags:tt;
) => {
libc_bitflags! {
@call_bitflags
{
name: $BitFlags,
type: $T,
attrs: $attrs,
flags: $flags,
}
}
};

// (pub) Done accumulating.
(@accumulate_flags
{
pub,
name: $BitFlags:ident,
type: $T:ty,
attrs: $attrs:tt,
},
$flags:tt;
) => {
libc_bitflags! {
@call_bitflags
{
pub,
name: $BitFlags,
type: $T,
attrs: $attrs,
flags: $flags,
}
}
};

// Munch an attr.
(@accumulate_flags
$prefix:tt,
[$($flags:tt)*];
#[$attr:meta] $($tail:tt)*
) => {
libc_bitflags! {
@accumulate_flags
$prefix,
[
$($flags)*
#[$attr]
];
$($tail)*
}
};

// Munch last ident if not followed by a comma.
(@accumulate_flags
$prefix:tt,
[$($flags:tt)*];
$flag:ident
) => {
libc_bitflags! {
@accumulate_flags
$prefix,
[
$($flags)*
const $flag = libc::$flag,
];
}
};

// Munch an ident; covers terminating comma case.
(@accumulate_flags
$prefix:tt,
[$($flags:tt)*];
$flag:ident, $($tail:tt)*
) => {
libc_bitflags! {
@accumulate_flags
$prefix,
[
$($flags)*
const $flag = libc::$flag,
];
$($tail)*
}
};

// (non-pub) Entry rule.
(
$(#[$attr:meta])*
flags $BitFlags:ident: $T:ty {
$($vals:tt)*
}
) => {
libc_bitflags! {
@accumulate_flags
{
name: $BitFlags,
type: $T,
attrs: [$(#[$attr])*],
},
[];
$($vals)*
}
};

// (pub) Entry rule.
(
$(#[$attr:meta])*
pub flags $BitFlags:ident: $T:ty {
$($vals:tt)*
}
) => {
libc_bitflags! {
@accumulate_flags
{
pub,
name: $BitFlags,
type: $T,
attrs: [$(#[$attr])*],
},
[];
$($vals)*
}
};
}

0 comments on commit b4c9f5b

Please sign in to comment.