|
demos are
explained here;
a menu at top column right indexes actual topic demos.
Here we demo quote.
problem
Writing an object to an i/o stream can have an ambiguous meaning: did you mean to write the content inside the object? Or did you mean to write a description of the object itself? To distinguish these, Wil likes to quote objects written if a description is the intended result. Given an object of type TFoo, Wil wants a description of this object to appear on an out stream when syntax like this is used: So this part of Wil's quote demo is an api design problem: what value should TFoo::quote() return so this works? With as little fuss as possible? So a person reading code can guess where to look for code called? Wil's answer to this design problem appears everywhere in demos that debug print objects. So most of this demo aims to motivate Wil's solution with the questions that seemed important. Wil only needs a special value from a quote() method because he also wants to write this: That is, when foo represents an octet sequence, or when conversion to one (such as a string) is well-defined, Wil wants this syntax to just write the out stream with the content, not a description of foo. This made Wil ask himself, "But what should I do when I want a description instead of content?" His answer was to have quote() return a uniquely typed value causing a dump method to be called, instead of writing content. What if an object doesn't represent content as a stream of bytes? In that case foo and foo.quote() might do exactly the same thing on the right-hand-side of operator<<(): call the dump method.
boilerplate
Let's take type yv from the run demo as an example (cf «) because it was one of the first uses of quote() shown. As a first step Wil adds a nested class and quote(): struct yv { // sequence of octets «
struct Vq { yv const& q_v; Vq(yv const& v): q_v(v) { } }; «
Vq quote() const { return Vq(*this); } // to request dump «
// ...
}; // struct yv
Class Vq follows this convention: nested classes begin with the last letter in a parent class's name, in uppercase. So the upper V just means "I'm a nested type in yv" reminding you to write yv::Vq in global scope. The trailing q is just short for quote as a mnemonic. Class Vq does nothing but hold a reference to some yv instance. It's the same size as a pointer, and neither creating nor using Vq requires a function call because everything is inline and public. This sort of class is usually called a wrapper because it exists only to add a layer of indirection atop something else. In effect, Vq means the same thing as yv&: a reference to yv. However, it's a unique type we can use to overload methods, and that's the only reason Vq exists. Inline method quote turns a yv reference into Vq, adding no new information except the Vq type. Those two lines of code are the first half of boilerplate; the second half is a dump() method and operator<<() overloaded for Vq: struct yv { // (... continued) «
void vdump(yo& o) const; // multiline print
};
inline yo& operator<<(yo& o, yv::Vq const& x) {
x.q_v.vdump(o); return o; }
All together, these lines of code have the effect of calling vdump() in the following example: yv digits("0123456789"); // yv::yv(const char* cstr)
yout << digits.quote() << yendl << ynow; // to stdout via yout
Which prints this hex dump annotated in pseudo XML: <yv p=0xee85 n=10 crc='0xa684c7c6:10'>
00000: 30 31 32 33 34 35 36 37 38 39 ; 0123456789
</yv>
Here's nearly the same sample code but missing quote(): Which prints the ten digits by themselves on a line: 0123456789
See top of column right for single line cite format.
license
All this code is available only under the BriarPig mu-babel license described fully on the rights page. You do not have permission to reprint this page in any way. No feeds or repackaging allowed. You can link this page if you want folks to read it. |
A submenu for demos appears below, letting you
go to the page on a topic written as a demo (as the
demos page defines it).
menu
thorn: todo, names, fd, iovec, assert, log, run, hex, crc, buf, in, out, quote « Þ, escape, compare, file, deck, cow, arc, blob, tree, slice, rand, time, stat, hash, heap, node, primes, page, book, pile, stack, atomic, lock, mutex, thread, map, meter, list, iter, ctype (mu: toy, peg, imm, tag, box, symbol, token, number, bigint, class, method, reader, writer, eval, env, vm, gc, world, pcode, compiler, asm, lathe, lisp, smalltalk, design, weight, jar, card, harp, debug, profile) Some demos are stubs: todo is a demo guide. See toy for mu updates on language pages; names introduces naming schemes.
cite
Wil not only wants to quote values when printing, he also wants to cite values, with a goal of limiting output to a single line only. Wil prefers concise cite format over verbose dump format in several contexts. For example, sometimes only minimal confirmation of expected object state is needed. Also, when dumping objects in a circular graph, a back pointer to a containing parent object is best printed with cite format since otherwise an infinite print loop might occur. You can find a previous discussion in the iovec demo (cf «) where template struct yct was first shown. Here's another look at the definition in mu.h: template <typename T> struct yct { // mu.h: yct - 'cite' template «
T const& c_t; // a T wrapper requesting cite rather than dump
yct(T const& t) : c_t(t) { } // just capture pointer value &t
};
template <typename T> yct<T>
ycite(T const& t) { return yct<T>(t); } // cite wrapper «
This global inline ycite() method returns a wrapper similar to yv::Vq shown earlier. But here one inline method can be used for all object types because yct<T> is a wrapper with unique type for any type T. Here's an example using the same digits printed earlier: yout << ycite(digits) << yendl; // yv::vcite() const;
This returns an instance of type yct<yv>, which invokes the following overloaded operator<<() which calls yv::ycite(): inline yo& operator<<(yo& o, yct<yv> const& x) {
x.c_t.vcite(o); return o; }
"Why didn't you use a template like that for quote?" asked Stu. "That would have worked, right? A single global yquote() inline could have replaced every quote() method you defined on every class? "Yes, that would have worked," confirmed Wil. "But look at how confusing the code is for ycite() because C++ templates are miserably opaque. Which is more clear to you?" "Well obviously quote() as an instance method is more clear," Stu admitted. "But you have to keep defining it." "I don't mind defining quote() each time," Wil explained. "It takes little effort since classes like yv::Vq are one-liners. It helps bring the idea to a developer's attention using simple syntax." "I guess it would be hard to understand ycite() without seeing an example of quote() first," considered Stu. "Exactly," chimed Wil. "I can use other template wrappers like yct<T> elsewhere, but I like quote() as a less painful intro." "In the coming escape demo," Stu wondered, "are you going to use a global inline yescape() method to wrap values in an escape wrapper template? That would be consistent." "I hadn't thought about it yet," realized Wil. "Sounds reasonable. Okay, maybe I'll have another template like this." template <typename T> struct yesc { // mu.h: yesc escape template «
T const& e_t; // a T wrapper requesting escape rather than dump
yesc(T const& t) : e_t(t) { } // just capture pointer value &t
};
template <typename T> yesc<T> // Stu was here
yescape(T const& t) { return yesc<T>(t); } // escape wrapper «
"I like how you credited me there," Stu joked. "Thanks. Sure you don't want to save yesc to mean event string class?"
lisp
"Did you borrow this quote idea from Lisp?" Stu guessed. "Bingo," Wil awarded Stu a beaming look. "It's the same old delayed evaluation meme in another guise." "Uh...," Stu looked for escape. "Let's skip delayed evaluation." "Okay, forget that," Wil said. "I meant to say: I think of a stream as evaluating to its content when you pass it to another stream." "But quote stops a stream from evaluating to content," Stu leaped ahead. "So a quote stream evaluates to itself, thus printing a self description." "I know," Wil looked apologetic. "That's academic and boring. But I hate syntax without a value-oriented theory of meaning."
operators
"Why do you bother with overloading operators?" Stu asked. "I don't want to look up method names to print," explained Wil. "Printing should be standard. I think standard operators help." "Why not just standard method names?" Stu asked. "You do in fact standardize print and dump methods. Why not call them direct?" Wil shrugged. "Fewer characters to type; less distracting syntax; less clutter performing multiple operations on one line," Wil summarized. |
demos « Þ
+ todo + names + fd + iovec + assert + log + run + hex + crc + buf + in + out + quote « Þ + escape + compare + file + deck + cow + arc + blob + tree + slice + rand + time + stat + hash + heap + node + primes + page + book + pile + stack + atomic + lock + mutex + thread + map + meter + list + iter + ctype |