Skip to content

Implement a format_args!() macro #9012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 75 additions & 16 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Some examples of the `format!` extension are:
format!("Hello") // => ~"Hello"
format!("Hello, {:s}!", "world") // => ~"Hello, world!"
format!("The number is {:d}", 1) // => ~"The number is 1"
format!("{}", ~[3, 4]) // => ~"~[3, 4]"
format!("{:?}", ~[3, 4]) // => ~"~[3, 4]"
format!("{value}", value=4) // => ~"4"
format!("{} {}", 1, 2) // => ~"1 2"
~~~
Expand Down Expand Up @@ -363,6 +363,32 @@ pub struct Argument<'self> {
priv value: &'self util::Void,
}

impl<'self> Arguments<'self> {
/// When using the format_args!() macro, this function is used to generate the
/// Arguments structure. The compiler inserts an `unsafe` block to call this,
/// which is valid because the compiler performs all necessary validation to
/// ensure that the resulting call to format/write would be safe.
#[doc(hidden)] #[inline]
pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
args: &'a [Argument<'a>]) -> Arguments<'a> {
Arguments{ fmt: cast::transmute(fmt), args: args }
}
}

/// This structure represents a safely precompiled version of a format string
/// and its arguments. This cannot be generated at runtime because it cannot
/// safely be done so, so no constructors are given and the fields are private
/// to prevent modification.
///
/// The `format_args!` macro will safely create an instance of this structure
/// and pass it to a user-supplied function. The macro validates the format
/// string at compile-time so usage of the `write` and `format` functions can
/// be safely performed.
pub struct Arguments<'self> {
priv fmt: &'self [rt::Piece<'self>],
priv args: &'self [Argument<'self>],
}

/// When a format is not otherwise specified, types are formatted by ascribing
/// to this trait. There is not an explicit way of selecting this trait to be
/// used for formatting, it is only if no other format is specified.
Expand Down Expand Up @@ -410,6 +436,26 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
/// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided.
///
/// # Arguments
///
/// * output - the buffer to write output to
/// * args - the precompiled arguments generated by `format_args!`
///
/// # Example
///
/// ~~~{.rust}
/// use std::fmt;
/// let w: &mut io::Writer = ...;
/// format_args!(|args| { fmt::write(w, args) }, "Hello, {}!", "world");
/// ~~~
pub fn write(output: &mut io::Writer, args: &Arguments) {
unsafe { write_unsafe(output, args.fmt, args.args) }
}

/// The `write_unsafe` function takes an output stream, a precompiled format
/// string, and a list of arguments. The arguments will be formatted according
/// to the specified format string into the output stream provided.
///
/// See the documentation for `format` for why this function is unsafe and care
/// should be taken if calling it manually.
///
Expand All @@ -426,8 +472,9 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
pub unsafe fn write(output: &mut io::Writer,
fmt: &[rt::Piece], args: &[Argument]) {
pub unsafe fn write_unsafe(output: &mut io::Writer,
fmt: &[rt::Piece],
args: &[Argument]) {
let mut formatter = Formatter {
flags: 0,
width: None,
Expand All @@ -446,6 +493,25 @@ pub unsafe fn write(output: &mut io::Writer,
/// The format function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
/// # Arguments
///
/// * args - a structure of arguments generated via the `format_args!` macro.
/// Because this structure can only be safely generated at
/// compile-time, this function is safe.
///
/// # Example
///
/// ~~~{.rust}
/// use std::fmt;
/// let s = format_args!(fmt::format, "Hello, {}!", "world");
/// assert_eq!(s, "Hello, world!");
/// ~~~
pub fn format(args: &Arguments) -> ~str {
unsafe { format_unsafe(args.fmt, args.args) }
}

/// The unsafe version of the formatting function.
///
/// This is currently an unsafe function because the types of all arguments
/// aren't verified by immediate callers of this function. This currently does
/// not validate that the correct types of arguments are specified for each
Expand All @@ -465,9 +531,9 @@ pub unsafe fn write(output: &mut io::Writer,
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
pub unsafe fn format(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
pub unsafe fn format_unsafe(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let mut output = MemWriter::new();
write(&mut output as &mut io::Writer, fmt, args);
write_unsafe(&mut output as &mut io::Writer, fmt, args);
return str::from_utf8_owned(output.inner());
}

Expand Down Expand Up @@ -740,7 +806,7 @@ impl<'self> Formatter<'self> {

/// This is a function which calls are emitted to by the compiler itself to
/// create the Argument structures that are passed into the `format` function.
#[doc(hidden)]
#[doc(hidden)] #[inline]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
t: &'a T) -> Argument<'a> {
unsafe {
Expand All @@ -753,14 +819,14 @@ pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),

/// When the compiler determines that the type of an argument *must* be a string
/// (such as for select), then it invokes this method.
#[doc(hidden)]
#[doc(hidden)] #[inline]
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
argument(String::fmt, s)
}

/// When the compiler determines that the type of an argument *must* be a uint
/// (such as for plural), then it invokes this method.
#[doc(hidden)]
#[doc(hidden)] #[inline]
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
argument(Unsigned::fmt, s)
}
Expand Down Expand Up @@ -899,14 +965,8 @@ impl<T> Pointer for *T {
}
}
}

impl<T> Pointer for *mut T {
fn fmt(t: &*mut T, f: &mut Formatter) {
f.flags |= 1 << (parse::FlagAlternate as uint);
do ::uint::to_str_bytes(*t as uint, 16) |buf| {
f.pad_integral(buf, "0x", true);
}
}
fn fmt(t: &*mut T, f: &mut Formatter) { Pointer::fmt(&(*t as *T), f) }
}

// Implementation of Default for various core types
Expand Down Expand Up @@ -940,7 +1000,6 @@ delegate!(f64 to Float)
impl<T> Default for *T {
fn fmt(me: &*T, f: &mut Formatter) { Pointer::fmt(me, f) }
}

impl<T> Default for *mut T {
fn fmt(me: &*mut T, f: &mut Formatter) { Pointer::fmt(me, f) }
}
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ pub fn syntax_expander_table() -> SyntaxEnv {
builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
syntax_expanders.insert(intern(&"writeln"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
syntax_expanders.insert(intern(&"format_args"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_format_args));
syntax_expanders.insert(
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
Expand Down
Loading