diff options
| author | bors <bors@rust-lang.org> | 2013-09-12 03:21:08 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-09-12 03:21:08 -0700 |
| commit | 4825db44c81ca2b122282dfb59aa705ad2475e5d (patch) | |
| tree | c8a8f6bcd0c72b08d8ba369653b7eb3f53bb48e6 /src/libstd | |
| parent | 68125359cdd053628b437c2b963bd82e1ff03523 (diff) | |
| parent | 9a5f95a82c2836fa6c57ede49d27c060f2377fa1 (diff) | |
| download | rust-4825db44c81ca2b122282dfb59aa705ad2475e5d.tar.gz rust-4825db44c81ca2b122282dfb59aa705ad2475e5d.zip | |
auto merge of #9012 : alexcrichton/rust/format-args, r=huonw
The purpose of this macro is to further reduce the number of allocations which occur when dealing with formatting strings. This macro will perform all of the static analysis necessary to validate that a format string is safe, and then it will wrap up the "format string" into an opaque struct which can then be passed around. Two safe functions are added (write/format) which take this opaque argument structure, unwrap it, and then call the unsafe version of write/format (in an unsafe block). Other than these two functions, it is not intended for anyone to ever look inside this opaque struct. The macro looks a bit odd, but mostly because of rvalue lifetimes this is the only way for it to be safe that I know of. Example use-cases of this are: * third-party libraries can use the default formatting syntax without any forced allocations * the fail!() macro can avoid allocating the format string * the logging macros can avoid allocation any strings I plan on transitioning the standard logging/failing to using these macros soon. This is currently blocking on inner statics being usable in cross-crate situations (still tracking down bugs there), but this will hopefully be coming soon! Additionally, I'd rather settle on a name now than later, so if anyone has a better suggestion other than `format_args`, I'm not attached to the name at all :)
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/fmt/mod.rs | 91 |
1 files changed, 75 insertions, 16 deletions
diff --git a/src/libstd/fmt/mod.rs b/src/libstd/fmt/mod.rs index 7d5033e3a6a..c7ab508ea6e 100644 --- a/src/libstd/fmt/mod.rs +++ b/src/libstd/fmt/mod.rs @@ -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" ~~~ @@ -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. @@ -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. /// @@ -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, @@ -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 @@ -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()); } @@ -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 { @@ -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) } @@ -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 @@ -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) } } |
