uprintln!
次の演習は、uprint!
系マクロの実装です。目標は、次のコードが動くことです。
#![allow(unused)] fn main() { uprintln!(serial, "The answer is {}", 40 + 2); }
これは、シリアルインタフェースを通じて、文字列の"The answer is 42"
を送信しなければなりません。
どうしたら良いのでしょうか?std
のprintln!
実装を見ると、有益な情報が得られます。
#![allow(unused)] fn main() { // src/libstd/macros.rs macro_rules! print { ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); } }
非常に単純に見えます。組込み機能のformat_args!
マクロ(コンパイラに実装されているため、実際に何をしているかは見えません)が必要です。
このマクロを、正しい方法で使わなければなりません。_print
関数は、何をやっているのでしょうか?
#![allow(unused)] fn main() { // src/libstd/io/stdio.rs pub fn _print(args: fmt::Arguments) { let result = match LOCAL_STDOUT.state() { LocalKeyState::Uninitialized | LocalKeyState::Destroyed => stdout().write_fmt(args), LocalKeyState::Valid => { LOCAL_STDOUT.with(|s| { if s.borrow_state() == BorrowState::Unused { if let Some(w) = s.borrow_mut().as_mut() { return w.write_fmt(args); } } stdout().write_fmt(args) }) } }; if let Err(e) = result { panic!("failed printing to stdout: {}", e); } } }
複雑に見えますが、興味のある部分は、w.write_fmt(args)
とstdout().write_fmt(args)
だけです。
結局のところ、print!
が行っていることは、format_args!
の出力を引数にして、fmt::Write::write_fmt
メソッドを呼ぶことなのです。
幸運なことに、fmt::Write::write_fmt
メソッドはデフォルトメソッドなので、実装する必要はありません。
fmt::Write::write_str
メソッドだけを実装しなければなりません。
では、やってみましょう。
ここまでが、方程式のマクロ側がどのように見えるか、です。残りの取り組まなければならないことは、write_str
メソッドの実装を提供することです。
上の方で、std::fmt
のWrite
を見ました。std
にはアクセスできませんが、Write
はcore::fmt
でも利用可能です。
#![deny(unsafe_code)] #![no_main] #![no_std] use core::fmt::{self, Write}; #[allow(unused_imports)] use aux11::{entry, iprint, iprintln, usart1}; macro_rules! uprint { ($serial:expr, $($arg:tt)*) => { $serial.write_fmt(format_args!($($arg)*)).ok() }; } macro_rules! uprintln { ($serial:expr, $fmt:expr) => { uprint!($serial, concat!($fmt, "\n")) }; ($serial:expr, $fmt:expr, $($arg:tt)*) => { uprint!($serial, concat!($fmt, "\n"), $($arg)*) }; } struct SerialPort { usart1: &'static mut usart1::RegisterBlock, } impl fmt::Write for SerialPort { fn write_str(&mut self, s: &str) -> fmt::Result { // TODO ここの実装して下さい // ヒント:以前のプログラムと非常に似たものになります Ok(()) } } #[entry] fn main() -> ! { let (usart1, mono_timer, itm) = aux11::init(); let mut serial = SerialPort { usart1 }; uprintln!(serial, "The answer is {}", 40 + 2); loop {} }