Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The "obvious" solution is to use a manipulator to install a custom <code>std::num_put&lt;char&gt;</code> facet which just formats <code>int</code>s as desired.</p> <p>The above statement may be a bit cryptic although it entirely describes the solution. Below is the code to actually implement the logic. The first ingredient is a special <code>std::num_put&lt;char&gt;</code> <em>facet</em> which is just a class derived from <code>std::num_put&lt;char&gt;</code> and overriding one of its <code>virtual</code> functions. The used facet is a filtering facet which looks at a flag stored with the stream (using <code>iword()</code>) to determine whether it should change the behavior or not. Here is the code:</p> <pre><code>class num_put : public std::num_put&lt;char&gt; { std::locale loc_; static int index() { static int rc(std::ios_base::xalloc()); return rc; } friend std::ostream&amp; twodigits(std::ostream&amp;); friend std::ostream&amp; notwodigits(std::ostream&amp;); public: num_put(std::locale loc): loc_(loc) {} iter_type do_put(iter_type to, std::ios_base&amp; fmt, char fill, long value) const { if (fmt.iword(index())) { fmt.width(2); return std::use_facet&lt;std::num_put&lt;char&gt; &gt;(this-&gt;loc_) .put(to, fmt, '0', value); } else { return std::use_facet&lt;std::num_put&lt;char&gt; &gt;(this-&gt;loc_) .put(to, fmt, fill, value); } } }; </code></pre> <p>The main part is the <code>do_put()</code> member function which decides how the value needs to be formatted: If the flag in <code>fmt.iword(index())</code> is non-zero, it sets the width to <code>2</code> and calls the formatting function with a fill character of <code>0</code>. The width is going to be reset anyway and the fill character doesn't get stored with the stream, i.e., there is no need for any clean-up.</p> <p>Normally, the code would probably live in a separate translation unit and it wouldn't be declared in a header. The only functions really declared in a header would be <code>twodigits()</code> and <code>notwodigits()</code> which are made <code>friend</code>s in this case to provide access to the <code>index()</code> member function. The <code>index()</code> member function just allocates an index usable with <code>std::ios_base::iword()</code> when called the time and it then just returns this index. The <em>manipulators</em> <code>twodigits()</code> and <code>notwodigits()</code> primarily set this index. If the <code>num_put</code> facet isn't installed for the stream <code>twodigits()</code> also installs the facet:</p> <pre><code>std::ostream&amp; twodigits(std::ostream&amp; out) { if (!dynamic_cast&lt;num_put const*&gt;( &amp;std::use_facet&lt;std::num_put&lt;char&gt; &gt;(out.getloc()))) { out.imbue(std::locale(out.getloc(), new num_put(out.getloc()))); } out.iword(num_put::index()) = true; return out; } std::ostream&amp; notwodigits(std::ostream&amp; out) { out.iword(num_put::index()) = false; return out; } </code></pre> <p>The <code>twodigits()</code> manipulator allocates the <code>num_put</code> facet using <code>new num_put(out.getloc())</code>. It doesn't require any clean-up because installing a facet in a <code>std::locale</code> object does the necessary clean-up. The original <code>std::locale</code> of the stream is accessed using <code>out.getloc()</code>. It is changed by the facet. In theory the <code>notwodigits</code> could restore the original <code>std::locale</code> instead of using a flag. However, <code>imbue()</code> can be a relatively expensive operation and using a flag should be a lot cheaper. Of course, if there are lots of similar formatting flags, things may become different...</p> <p>To demonstrate the use of the manipulators there is a simple test program below. It sets up the formatting flag <code>twodigits</code> twice to verify that facet is only created once (it would be a bit silly to create a chain of <code>std::locale</code>s to pass through the formatting:</p> <pre><code>int main() { std::cout &lt;&lt; "some-int='" &lt;&lt; 1 &lt;&lt; "' " &lt;&lt; twodigits &lt;&lt; '\n' &lt;&lt; "two-digits1='" &lt;&lt; 1 &lt;&lt; "' " &lt;&lt; "two-digits2='" &lt;&lt; 2 &lt;&lt; "' " &lt;&lt; "two-digits3='" &lt;&lt; 3 &lt;&lt; "' " &lt;&lt; notwodigits &lt;&lt; '\n' &lt;&lt; "some-int='" &lt;&lt; 1 &lt;&lt; "' " &lt;&lt; twodigits &lt;&lt; '\n' &lt;&lt; "two-digits4='" &lt;&lt; 4 &lt;&lt; "' " &lt;&lt; '\n'; } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload