choices.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Language" content="en-us">
  5. <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
  6. <title>Type-safe 'printf-like' format class</title>
  7. </head>
  8. <body bgcolor="#FFFFFF" text="#000000">
  9. <h1><img align="middle" alt="boost.png (6897 bytes)" height="86" src=
  10. "../../../boost.png" width="277">Type-safe 'printf-like' <b>format
  11. class</b></h1>
  12. <h2>Choices made</h2>
  13. <p>"Le pourquoi du comment" ( - "the why of the how")</p>
  14. <hr>
  15. <h3>The syntax of the format-string</h3>
  16. <p>Format is a new library. One of its goal is to provide a replacement for
  17. printf, that means format can parse a format-string designed for printf,
  18. apply it to the given arguments, and produce the same result as printf
  19. would have.<br>
  20. With this constraint, there were roughly 3 possible choices for the syntax
  21. of the format-string :</p>
  22. <ol>
  23. <li>Use the exact same syntax of printf. It's well known by many
  24. experienced users, and fits almost all needs. But with C++ streams, the
  25. type-conversion character, crucial to determine the end of a directive,
  26. is only useful to set some associated formatting options, in a C++
  27. streams context (%x for setting hexa, etc..) It would be better to make
  28. this obligatory type-conversion character, with modified meaning,
  29. optional.</li>
  30. <li>extend printf syntax while maintaining compatibility, by using
  31. characters and constructs not yet valid as printf syntax. e.g. : "%1%",
  32. "%[1]", "%|1$d|", .. Using begin / end marks, all sort of extension can
  33. be considered.</li>
  34. <li>Provide a non-legacy mode, in parallel of the printf-compatible one,
  35. that can be designed to fit other objectives without constraints of
  36. compatibilty with the existing printf syntax.<br>
  37. But Designing a replacement to printf's syntax, that would be clearly
  38. better, and as much powerful, is yet another task than building a format
  39. class. When such a syntax is designed, we should consider splitting
  40. Boost.format into 2 separate libraries : one working hand in hand with
  41. this new syntax, and another supporting the legacy syntax (possibly a
  42. fast version, built with safety improvement above snprintf or the
  43. like).</li>
  44. </ol>In the absence of a full, clever, new syntax clearly better adapted to
  45. C++ streams than printf, the second approach was chosen. Boost.format uses
  46. printf's syntax, with extensions (tabulations, centered alignements) that
  47. can be expressed using extensions to this syntax.<br>
  48. And alternate compatible notations are provided to address the weaknesses
  49. of printf's :
  50. <ul>
  51. <li><i>"%<b>N</b>%"</i> as a simpler positional, typeless and optionless
  52. notation.</li>
  53. <li><i>%|spec|</i> as a way to encapsulate printf directive in movre
  54. visually evident structures, at the same time making printf's
  55. 'type-conversion character' optional.</li>
  56. </ul>
  57. <hr>
  58. <h3>Why are arguments passed through an operator rather than a function
  59. call ?</h3><br>
  60. The inconvenience of the operator approach (for some people) is that it
  61. might be confusing. It's a usual warning that too much of overloading
  62. operators gets people real confused.<br>
  63. Since the use of format objects will be in specific contexts ( most often
  64. right after a "cout &lt;&lt; ") and look like a formatting string followed
  65. by arguments indeed :
  66. <blockquote>
  67. <pre>
  68. format(" %s at %s with %s\n") % x % y % z;
  69. </pre>
  70. </blockquote>we can hope it wont confuse people that much.
  71. <p>An other fear about operators, is precedence problems. What if I someday
  72. write <b>format("%s") % x+y</b><br>
  73. instead of <i>format("%s") % (x+y)</i> ??<br>
  74. It will make a mistake at compile-time, so the error will be immediately
  75. detected.<br>
  76. indeed, this line calls <i>tmp = operator%( format("%s"), x)</i><br>
  77. and then <i>operator+(tmp, y)</i><br>
  78. tmp will be a format object, for which no implicit conversion is defined,
  79. and thus the call to operator+ will fail. (except if you define such an
  80. operator, of course). So you can safely assume precedence mistakes will be
  81. noticed at compilation.</p>
  82. <p><br>
  83. On the other hand, the function approach has a true inconvenience. It needs
  84. to define lots of template function like :</p>
  85. <blockquote>
  86. <pre>
  87. template &lt;class T1, class T2, .., class TN&gt;
  88. string format(string s, const T1&amp; x1, .... , const T1&amp; xN);
  89. </pre>
  90. </blockquote>and even if we define those for N up to 500, that is still a
  91. limitation, that C's printf does not have.<br>
  92. Also, since format somehow emulates printf in some cases, but is far from
  93. being fully equivalent to printf, it's best to use a radically different
  94. appearance, and using operator calls succeeds very well in that !
  95. <p><br>
  96. Anyhow, if we actually chose the formal function call templates system, it
  97. would only be able to print Classes T for which there is an</p>
  98. <blockquote>
  99. <pre>
  100. operator&lt;&lt; ( stream, const T&amp;)
  101. </pre>
  102. </blockquote>Because allowing both const and non const produces a
  103. combinatorics explosion - if we go up to 10 arguments, we need 2^10
  104. functions.<br>
  105. (providing overloads on T&amp; / const T&amp; is at the frontier of defects
  106. of the C++ standard, and thus is far from guaranteed to be supported. But
  107. right now several compilers support those overloads)<br>
  108. There is a lot of chances that a class which only provides the non-const
  109. equivalent is badly designed, but yet it is another unjustified restriction
  110. to the user.<br>
  111. Also, some manipulators are functions, and can not be passed as const
  112. references. The function call approach thus does not support manipulators
  113. well.
  114. <p>In conclusion, using a dedicated binary operator is the simplest, most
  115. robust, and least restrictive mechanism to pass arguments when you can't
  116. know the number of arguments at compile-time.</p>
  117. <hr>
  118. <h3>Why operator% rather than a member function 'with(..)'
  119. ??</h3>technically,
  120. <blockquote>
  121. <pre>
  122. format(fstr) % x1 % x2 % x3;
  123. </pre>
  124. </blockquote>has the same structure as
  125. <blockquote>
  126. <pre>
  127. format(fstr).with( x1 ).with( x2 ).with( x3 );
  128. </pre>
  129. </blockquote>which does not have any precedence problem. The only drawback,
  130. is it's harder for the eye to catch what is done in this line, than when we
  131. are using operators. calling .with(..), it looks just like any other line
  132. of code. So it may be a better solution, depending on tastes. The extra
  133. characters, and overall cluttered aspect of the line of code using
  134. 'with(..)' were enough for me to opt for a true operator.
  135. <hr>
  136. <h3>Why operator% rather than usual formatting operator&lt;&lt; ??</h3>
  137. <ul>
  138. <li>because passing arguments to a format object is *not* the same as
  139. sending variables, sequentially, into a stream, and because a format
  140. object is not a stream, nor a manipulator.<br>
  141. We use an operator to pass arguments. format will use them as a
  142. function would, it simply takes arguments one by one.<br>
  143. format objects can not provide stream-like behaviour. When you try to
  144. implement a format object that acts like a manipulator, returning a
  145. stream, you make the user beleive it is completely like a
  146. stream-manipulator. And sooner or later, the user is deceived by this
  147. point of view.<br>
  148. The most obvious example of that difference in behaviour is
  149. <blockquote>
  150. <pre>
  151. cout &lt;&lt; format("%s %s ") &lt;&lt; x;
  152. cout &lt;&lt; y ; // uh-oh, format is not really a stream manipulator
  153. </pre>
  154. </blockquote>
  155. </li>
  156. <li>precedence of % is higher than that of &lt;&lt;. It can be viewd as a
  157. problem, because + and - thus needs to be grouped inside parentheses,
  158. while it is not necessary with '&lt;&lt;'. But if the user forgets, the
  159. mistake is catched at compilation, and hopefully he won't forget
  160. again.<br>
  161. On the other hand, the higher precedence makes format's behaviour very
  162. straight-forward.
  163. <blockquote>
  164. <pre>
  165. cout &lt;&lt; format("%s %s ") % x % y &lt;&lt; endl;
  166. </pre>
  167. </blockquote>is treated exaclt like :
  168. <blockquote>
  169. <pre>
  170. cout &lt;&lt; ( format("%s %s ") % x % y ) &lt;&lt; endl;
  171. </pre>
  172. </blockquote>So using %, the life of a format object does not interfere
  173. with the surrounding stream context. This is the simplest possible
  174. behaviour, and thus the user is able to continue using the stream after
  175. the format object.<br>
  176. <br>
  177. With operator&lt;&lt;, things are much more problematic in this
  178. situation. This line :
  179. <blockquote>
  180. <pre>
  181. cout &lt;&lt; format("%s %s ") &lt;&lt; x &lt;&lt; y &lt;&lt; endl;
  182. </pre>
  183. </blockquote>is understood as :
  184. <blockquote>
  185. <pre>
  186. ( ( ( cout &lt;&lt; format("%s %s ") ) &lt;&lt; x ) &lt;&lt; y ) &lt;&lt; endl;
  187. </pre>
  188. </blockquote>Several alternative implementations chose
  189. operator&lt;&lt;, and there is only one way to make it work :<br>
  190. the first call to
  191. <blockquote>
  192. <pre>
  193. operator&lt;&lt;( ostream&amp;, format const&amp;)
  194. </pre>
  195. </blockquote>returns a proxy, encapsulating both the final destination
  196. (cout) and the format-string information<br>
  197. Passing arguments to format, or to the final destination after
  198. completion of the format are indistinguishable. This is a problem.
  199. <p>I examined several possible implementations, and none is completely
  200. satsifying.<br>
  201. E.g. : In order to catch users mistake, it makes sense to raise
  202. exceptions when the user passes too many arguments. But in this
  203. context, supplementary arguments are most certainly aimed at the final
  204. destination. There are several choices here :</p>
  205. <ul>
  206. <li>You can give-up detection of arity excess, and have the proxy's
  207. template member operator&lt;&lt;( const T&amp;) simply forward all
  208. supplementary arguments to cout.</li>
  209. <li>Require the user to close the format arguments with a special
  210. manipulator, 'endf', in this way :
  211. <blockquote>
  212. <pre>
  213. cout &lt;&lt; format("%s %s ") &lt;&lt; x &lt;&lt; y &lt;&lt; endf &lt;&lt; endl;
  214. </pre>
  215. </blockquote>You can define endf to be a function that returns the
  216. final destination stored inside the proxy. Then it's okay, after
  217. endf the user is calling &lt;&lt; on cout again.
  218. </li>
  219. <li>An intermediate solution, is to adress the most frequent use,
  220. where the user simply wants to output one more manipulator item to
  221. cout (a std::flush, or endl, ..)
  222. <blockquote>
  223. <pre>
  224. cout &lt;&lt; format("%s %s \n") &lt;&lt; x &lt;&lt; y &lt;&lt; flush ;
  225. </pre>
  226. </blockquote>Then, the solution is to overload the operator&lt;&lt;
  227. for manipulators. This way You don't need endf, but outputting a
  228. non-manipulator item right after the format arguments is a mistake.
  229. </li>
  230. </ul><br>
  231. The most complete solution is the one with the endf manipualtor. With
  232. operator%, there is no need for this end-format function, plus you
  233. instantly see which arguments are going into the format object, and
  234. which are going to the stream.
  235. </li>
  236. <li>Esthetically : '%' is the same letter as used inside the
  237. format-string. That is quite nice to have the same letter used for
  238. passing each argument. '&lt;&lt;' is 2 letters, '%' is one. '%' is also
  239. smaller in size. It overall improves visualisation (we see what goes with
  240. what) :
  241. <blockquote>
  242. <pre>
  243. cout &lt;&lt; format("%s %s %s") %x %y %z &lt;&lt; "And avg is" &lt;&lt; format("%s\n") %avg;
  244. </pre>
  245. </blockquote>compared to :
  246. <blockquote>
  247. <pre>
  248. cout &lt;&lt; format("%s %s %s") &lt;&lt; x &lt;&lt; y &lt;&lt; z &lt;&lt; endf &lt;&lt;"And avg is" &lt;&lt; format("%s\n") &lt;&lt; avg;
  249. </pre>
  250. </blockquote>"&lt;&lt;" misleadingly puts the arguments at the same
  251. level as any object passed to the stream.
  252. </li>
  253. <li>python also uses % for formatting, so you see it's not so "unheard
  254. of" ;-)</li>
  255. </ul>
  256. <hr>
  257. <h3>Why operator% rather than operator(), or operator[] ??</h3>
  258. <p>operator() has the merit of being the natural way to send an argument
  259. into a function. And some think that operator[] 's meaning apply well to
  260. the usage in format.<br>
  261. They're as good as operator% technically, but quite ugly. (that's a matter
  262. of taste)<br>
  263. And deepd down, using operator% for passing arguments that were referred to
  264. by "%" in the format string seems much more natural to me than using those
  265. operators.</p>
  266. <hr>
  267. <p><a href="http://validator.w3.org/check?uri=referer"><img border="0" src=
  268. "../../../doc/images/valid-html401.png" alt="Valid HTML 4.01 Transitional"
  269. height="31" width="88"></a></p>
  270. <p>Revised
  271. <!--webbot bot="Timestamp" s-type="EDITED" s-format="%d %B, %Y" startspan -->02 December, 2006<!--webbot bot="Timestamp" endspan i-checksum="38510" --></p>
  272. <p><i>Copyright &copy; 2001 Samuel Krempp</i></p>
  273. <p><i>Distributed under the Boost Software License, Version 1.0. (See
  274. accompanying file <a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or
  275. copy at <a href=
  276. "http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>)</i></p>
  277. </body>
  278. </html>