uprintln!

次の演習は、uprint!系マクロの実装です。目標は、次のコードが動くことです。

#![allow(unused)]
fn main() {
    uprintln!(serial, "The answer is {}", 40 + 2);
}

これは、シリアルインタフェースを通じて、文字列の"The answer is 42"を送信しなければなりません。

どうしたら良いのでしょうか?stdprintln!実装を見ると、有益な情報が得られます。

#![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::fmtWriteを見ました。stdにはアクセスできませんが、Writecore::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 {}
}