uprintln!
次の演習では、uprint! マクロファミリーを実装します。目標は、この
コード行を動作させることです。
#![allow(unused)]
fn main() {
uprintln!(serial, "The answer is {}", 40 + 2);
}
これによって、文字列 "The answer is 42" がシリアルインターフェース経由で送信されなければなりません。
どう進めればよいのでしょうか。println! の std における実装を見ると参考になります。
#![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 メソッドの実装を
あなたが用意するだけです。
先ほど見たように、Write は std::fmt にあります。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 implement this
// hint: this will look very similar to the previous program
Ok(())
}
}
#[entry]
fn main() -> ! {
let (usart1, _mono_timer, _itm) = aux11::init();
let mut serial = SerialPort { usart1 };
uprintln!(serial, "The answer is {}", 40 + 2);
loop {}
}