mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +11:00
Improving newtype
This commit is contained in:
parent
1a19831373
commit
ce3ddd3bb0
|
@ -96,6 +96,11 @@ style, but there are some rules and considerations here:
|
||||||
* Parentheses macro use mostly gets treated like a function call.
|
* Parentheses macro use mostly gets treated like a function call.
|
||||||
* Bracket macro use mostly gets treated like an array declaration.
|
* Bracket macro use mostly gets treated like an array declaration.
|
||||||
|
|
||||||
|
**As a reminder:** remember that `macro_rules` macros have to appear _before_
|
||||||
|
they're invoked in your source, so the `newtype` macro will always have to be at
|
||||||
|
the very top of your file, or if you put it in a module within your project
|
||||||
|
you'll need to declare the module before anything that uses it.
|
||||||
|
|
||||||
## Upgrade That Macro!
|
## Upgrade That Macro!
|
||||||
|
|
||||||
We also want to be able to add `derive` stuff and doc comments to our newtype.
|
We also want to be able to add `derive` stuff and doc comments to our newtype.
|
||||||
|
@ -124,34 +129,78 @@ newtype! {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And that's about all we'll need for the examples.
|
Next, we can allow for the wrapping of types that aren't just a single
|
||||||
|
identifier by changing `$old_name` from `:ident` to `:ty`. We can't _also_ do
|
||||||
|
this for the `$new_type` part because declaring a new struct expects a valid
|
||||||
|
identifier that's _not_ already declared (obviously), and `:ty` is intended for
|
||||||
|
capturing types that already exist.
|
||||||
|
|
||||||
**As a reminder:** remember that `macro_rules` macros have to appear _before_
|
```rust
|
||||||
they're invoked in your source, so the `newtype` macro will always have to be at
|
#[macro_export]
|
||||||
the very top of your file, or if you put it in a module within your project
|
macro_rules! newtype {
|
||||||
you'll need to declare the module before anything that uses it.
|
($(#[$attr:meta])* $new_name:ident, $old_name:ty) => {
|
||||||
|
$(#[$attr])*
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct $new_name($old_name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Potential Homework
|
Next of course we'll want to usually have a `new` method that's const and just
|
||||||
|
gives a 0 value. We won't always be making a newtype over a number value, but we
|
||||||
|
often will. It's usually silly to have a `new` method with no arguments since we
|
||||||
|
might as well just impl `Default`, but `Default::default` isn't `const`, so
|
||||||
|
having `pub const fn new() -> Self` is justified here.
|
||||||
|
|
||||||
If you wanted to keep going and get really fancy with it, you could potentially
|
Here, the token `0` is given the `{integer}` type, which can be converted into
|
||||||
add a lot more:
|
any of the integer types as needed, but it still can't be converted into an
|
||||||
|
array type or a pointer or things like that. Accordingly we've added the "no
|
||||||
|
frills" option which declares the struct and no `new` method.
|
||||||
|
|
||||||
* Make a `pub const fn new() -> Self` method that outputs the base value in a
|
```rust
|
||||||
const way. Combine this with builder style "setter" methods that are also
|
#[macro_export]
|
||||||
const and you can get the compiler to do quite a bit of the value building
|
macro_rules! newtype {
|
||||||
work at compile time.
|
($(#[$attr:meta])* $new_name:ident, $old_name:ty) => {
|
||||||
* Making the macro optionally emit a `From` impl to unwrap it back into the base
|
$(#[$attr])*
|
||||||
type.
|
#[repr(transparent)]
|
||||||
* Allow for visibility modifiers to be applied to the inner field and the newly
|
pub struct $new_name($old_name);
|
||||||
generated type.
|
impl $new_name {
|
||||||
* Allowing for generic newtypes. You already saw the need for this once in the
|
/// A `const` "zero value" constructor
|
||||||
volatile section. Unfortunately, this particular part gets really tricky if
|
pub const fn new() -> Self {
|
||||||
you're using `macro_rules!`, so you might need to move up to a full
|
$new_name(0)
|
||||||
`proc_macro`. Having a `proc_macro` isn't bad except that they have to be
|
}
|
||||||
defined in a crate of their own and they're compiled before use. You can't
|
}
|
||||||
ever use them in the crate that defines them, so we won't be using them in any
|
};
|
||||||
of our single file examples.
|
($(#[$attr:meta])* $new_name:ident, $old_name:ty, no frills) => {
|
||||||
* Allowing for optional `Deref` and `DerefMut` of the inner value. This takes
|
$(#[$attr])*
|
||||||
away most all the safety aspect of doing the newtype, but there may be times
|
#[repr(transparent)]
|
||||||
for it. As an example, you could make a newtype with a different form of
|
pub struct $new_name($old_name);
|
||||||
Display impl that you want to otherwise treat as the base type in all places.
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, we usually want to have the wrapped value be totally private, but there
|
||||||
|
_are_ occasions where that's not the case. For this, we can allow the wrapped
|
||||||
|
field to accept a visibility modifier.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! newtype {
|
||||||
|
($(#[$attr:meta])* $new_name:ident, $v:vis $old_name:ty) => {
|
||||||
|
$(#[$attr])*
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct $new_name($v $old_name);
|
||||||
|
impl $new_name {
|
||||||
|
/// A `const` "zero value" constructor
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
$new_name(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($(#[$attr:meta])* $new_name:ident, $v:vis $old_name:ty, no frills) => {
|
||||||
|
$(#[$attr])*
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct $new_name($v $old_name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -42,10 +42,10 @@ pub(crate) use gba_proc_macro::register_bit;
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! newtype {
|
macro_rules! newtype {
|
||||||
($(#[$attr:meta])* $new_name:ident, $old_name:ident) => {
|
($(#[$attr:meta])* $new_name:ident, $v:vis $old_name:ty) => {
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct $new_name($old_name);
|
pub struct $new_name($v $old_name);
|
||||||
impl $new_name {
|
impl $new_name {
|
||||||
/// A `const` "zero value" constructor
|
/// A `const` "zero value" constructor
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
|
@ -53,10 +53,10 @@ macro_rules! newtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($(#[$attr:meta])* $new_name:ident, $old_name:ident, no frills) => {
|
($(#[$attr:meta])* $new_name:ident, $v:vis $old_name:ty, no frills) => {
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct $new_name($old_name);
|
pub struct $new_name($v $old_name);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ pub(crate) use self::base::*;
|
||||||
pub mod bios;
|
pub mod bios;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod mgba;
|
pub mod mgba;
|
||||||
pub mod video;
|
|
||||||
pub mod palram;
|
pub mod palram;
|
||||||
|
pub mod video;
|
||||||
|
|
||||||
newtype! {
|
newtype! {
|
||||||
/// A color on the GBA is an RGB 5.5.5 within a `u16`
|
/// A color on the GBA is an RGB 5.5.5 within a `u16`
|
||||||
|
|
Loading…
Reference in a new issue