?
Solved

Adding new flags to control an ostream

Posted on 2004-11-21
21
Medium Priority
?
593 Views
Last Modified: 2008-01-09
Hi all

I have a nice class for tracking units-of-measure along with numerical values.

I have a simple output method accompanying this class which I can use to make the units show whenever a value is outputted to an ostream, as shown below.

My question: instead of using the ios_base::showbase flag (which is really intended for something completely different), can I create a new flag that is guaranteed to work on all platforms, that won't clash with existing flags, and which can safely be set with the 'stream.flags()' method?

What would be the model approach here?

Any suggestions appreciated.

JP

---------------------8<------------------

#define DEFINE_OUTPUT_METHOD(MM,LL,TT,KK,II,UNITS) \
            inline \
            std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
                  double d = *reinterpret_cast<const double*>(&u); \
                  os << d; \
                  os.flags() & std::ios_base::showbase && os << " " << UNITS; \
                  return os; \
            }

DEFINE_OUTPUT_METHOD(0, 1, 0, 0, 0, "m");
DEFINE_OUTPUT_METHOD(0, 0, 1, 0, 0, "s");
DEFINE_OUTPUT_METHOD(0, 1, -1, 0, 0, "m/s");
0
Comment
Question by:jdpipe
  • 7
  • 4
  • 3
  • +2
20 Comments
 
LVL 7

Author Comment

by:jdpipe
ID: 12641963
To clarify, this is how I use the above:

  Length L=5 * metre;
  cerr.flags(ios_base::showbase);
  cerr << "The car's length is "  << L << endl;

This would output:

  The car's length is 5 m
0
 
LVL 13

Expert Comment

by:SteH
ID: 12642994
My approach would be to control the output of that class. Set a flag of that class and overwrite the <<operator () of that class to modify the oputput to your needs. Don't touch the well tested stream classes.
0
 
LVL 13

Expert Comment

by:SteH
ID: 12643009
So your code should look like
Length L = 5, "metre";
L.flag (showunit);
cout << The car's length is " << L << endl;
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 17

Accepted Solution

by:
rstaveley earned 1600 total points
ID: 12643347
You may be looking for xalloc and iword (or if you want to make it more complicated pword).

Here's a way of handling display units with a standard length class.

--------8<--------
#include <iostream>
#include <iomanip>
#include <string>

// Globals for display units
namespace display {

      // Allocate our global ios_base::iword index for display units
      const int index = std::ios_base::xalloc();

      // Metres/Yards
      enum Units {Metres,Yards};

      // Get the ostream's display units
      template<typename C>
        inline Units get_units(std::basic_ostream<C>& os)
      {
            return static_cast<Units>(os.iword(index));
        }

      // Set the ostream's display unitys
        template<typename C>
      inline void set_units(std::basic_ostream<C>& os,Units value)
      {
            os.iword(index) = value;
      }

      // A manipulator to set the verbosity level
      class units {

            const Units value;

            template<typename C>
            friend std::basic_ostream<C>& operator<<(std::basic_ostream<C>& os,const units &u)
            {
                  set_units(os,u.value);
                  return os;
            }

      public:
            units(Units value) : value(value) {}
      };

} // display namespace

// Class for storing length
class StandardLength {

      double length;            // Internally stored as metres
      template<typename C>
            friend std::basic_ostream<C>& operator<<(std::basic_ostream<C>& os,const StandardLength& distance);

public:
      StandardLength() {}
      StandardLength(double standard_length) : length(standard_length) {}

      // Construct a StandardLength in yards
      static StandardLength Yards(double yards_length)
      {
            return StandardLength(yards_length/0.9144);
      }

      // Construct a StandardLength in metres
      static StandardLength Metres(double metres_length)
      {
            return StandardLength(metres_length);
      }

};

// Display the StandardLength
template<typename C>
std::basic_ostream<C>& operator<<(std::basic_ostream<C>& os,const StandardLength& distance)
{
      return os << (display::get_units(os) == display::Yards ? distance.length*0.9144 : distance.length);
}

int main()
{
      StandardLength race_length = StandardLength::Metres(100);
      std::cout
            << "The race is "
            << display::units(display::Yards)
            << race_length
            << " yards, which is "
            << display::units(display::Metres)
            << race_length
            << " metres\n";
}
--------8<--------

BTW... Don't trust my metres/yards conversion - this was a quick hack :-)
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12644614
Thanks for the tips you guys...

STeH, this units class which I have needs to add zero overhead to storage and arithmetic operations, so I don't think that I can use a flag on top of the Units class, unless you had some clever way of doing it with static variables or something... But even then, I might want units being output in one stream, but not in another. For example, if I have a console window with some live results, showing units, but also afile with some tab-separated values. I would want both streams to keep their correct settings.

rstaveley, that stuff looks pretty good. I'm not familiar with xalloc and iword, I'll read up on those, check out your code, and get back to you.

Thanks again
JP
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12644807
> // A manipulator to set the verbosity level

That stray comment, which shouldn't be there, was left over from the facility for which I last used xalloc/iword. I had a flag which I put into the output stream to specify the verbosity of my debug.

I dug this stuff out of Standard C++ IOStreams and Locales by Langer and Kreft, which is worth flipping through, if you've got access to a library.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12647769
Actuall, i don't know why you are using 'showbase' flag:

I compiled using

#define DEFINE_OUTPUT_METHOD(MM,LL,TT,KK,II,UNITS) \
          inline \
          std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
               double d = *reinterpret_cast<const double*>(&u); \
               os << d; \
               /*os.flags() & std::ios_base::showbase && */os << " " << UNITS; \
               return os; \
          }

DEFINE_OUTPUT_METHOD(0, 1, 0, 0, 0, "m");
DEFINE_OUTPUT_METHOD(0, 0, 1, 0, 0, "s");
DEFINE_OUTPUT_METHOD(0, 1, -1, 0, 0, "m/s");

          Velocity v = 60. * kilometre / hour;
         
          Length x = 250. * kilometre;
         
          Time t = x / v;
          Length z = metre + metre;
          Units<0, 0, 0, 0, 0> unitless = 123.4;
          z = unitless * z;

          cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
            // cerr.flags(ios_base::showbase);
            cerr << "The car's length is "  << z << endl;

not using any flag, and the output was fine:

The time taken to travel 250000 m at speed 16.6667 m/s is 15000 s
The car's length is 2 m

Regards, Alex
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12665697
The idea here is to be able to safely define a flag which can be used to turn on and off the display of units. I originally thought I would dodgily re-use the 'showbase' flag usually reserved (I think) for showing 0x (or whatever it is) at the start of binary values.

I think rstaveley's approach with 'iword' is going to be the right one.

JP
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12666300
I vote for rstaveley's approach too ;-)
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12667623
I also, though that below looks simpler and runs on my VC6 system:

enum { showunit = ios_base::showbase };

#define DEFINE_OUTPUT_METHOD(MM,LL,TT,KK,II,UNITS) \
          inline \
          std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
               double d = *reinterpret_cast<const double*>(&u); \
               os << d; \
               os.flags() & showunit && os << " " << UNITS; \
               return os; \
          }

DEFINE_OUTPUT_METHOD(0, 1, 0, 0, 0, "m");
DEFINE_OUTPUT_METHOD(0, 0, 1, 0, 0, "s");
DEFINE_OUTPUT_METHOD(0, 1, -1, 0, 0, "m/s");


int main(int argc, char *argv[]){
   
     Velocity v = 60. * kilometre / hour;
     Length x = 250. * kilometre;
     Time t = x / v;
     Length z = metre + metre;
     Units<0, 0, 0, 0, 0> unitless = 123.4;
     z = unitless * z;
     cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
 
     cerr.flags(showunit);
     cerr << "The car's length is "  << z << endl;
         
     return 0;
}

Regards, Alex
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12668295
The reason why the above works is:

// extract from xiosbase header

        enum _Fmtflags {skipws = 0x0001, unitbuf = 0x0002,
                uppercase = 0x0004, showbase = 0x0008,
                showpoint = 0x0010, showpos = 0x0020,
                left = 0x0040, right = 0x0080, internal = 0x0100,
                dec = 0x0200, oct = 0x0400, hex = 0x0800,
                scientific = 0x1000, fixed = 0x2000, boolalpha = 0x4000,
                adjustfield = 0x01c0, basefield = 0x0e00,
                floatfield = 0x3000, _Fmtmask = 0x7fff, _Fmtzero = 0};
         ...
        _BITMASK(_Fmtflags, fmtflags);  // ==> typedef int fmtflags;
         ...
         fmtflags flags(fmtflags _Nf)
         {fmtflags _Of = _Fmtfl;
           _Fmtfl = _Nf & _Fmtmask;
           return (_Of); }


By switching on and off just before and after Units elements, you could avoid influences on other output elements:

      cerr << "The time taken to travel " 
            << setiosflags(showunit) << x  << resetiosflags(showunit)
           << " at speed " << v << " is " << t << endl;

gives the following output:

The time taken to travel 250000 m at speed 16.6667 is 15000

Regards, Alex
0
 
LVL 6

Expert Comment

by:guitaristx
ID: 12788894
Why not derive yourself a class from ostream, add the new flag in the derived class, and let everything else pass-through to the base class?
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12789035
Would that work, guitaristx?

I don't want to have to change the stream I'm outputting to; I want to be able to write

stringstream s;
s << displayunits << length << endl;

and

cerr << displayunits << speed << endl;

So I wouldn't have thought I'd want to overload the stream classes...

JP
0
 
LVL 13

Assisted Solution

by:SteH
SteH earned 400 total points
ID: 12790703
I doubt that you want to derive a new stream class. Only the << operator needs to be overloaded in the tracking units class.

class track {
 [snip]

 bool bShowUnits; // this can be either static of normal member variable: a static would be good to switch unit output for all class objects.
                          // whereas a normal member allows to change output for each object indiviually.
public:
 friend ostream& operator<< (ostream&, track& tr1);
}

ostream& operator << (ostream& os, track& tr1)
{
  if (true == track::bShowUnits) // or (true == tr1.bShowUnits) for non static member var
     os << tr1.value << " " << tr1.unit;
  else
     os << tr1.value;

  return os;
}

0
 
LVL 6

Expert Comment

by:guitaristx
ID: 12811789
If it was me implementing it, I would:

create a function:

void displayunits(void)
{
     if(app_globals::disp_units == true) app_globals::disp_units = false;
     else app_globals::disp_units == true;
}

And another so that you can use it:

ostream& operator <<(ostream& out, void (*func)(void) )
{
     func();
     return out;
}

Then, create output operators for all the types that you want to assign units to:

ostream& operator<<(ostream& out, const myType&obj)
{
     if(app_globals::disp_units)
     {
            //display with units
            out << obj.value << " " << obj.units;
     }
      else
      {
            //display without units
            out << obj.value;
      }

       return out;
}

There are some assumptions; one being that you've created an output operator for your "units" type.  This would be the easiest way, IMHO.  You might create disp_units as a public static member of your units class.
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12815210
Guitaristx

The units class I'm using here is one modified from http://www.embedded.com/shared/printableArticle.jhtml?articleID=9900094
you can see the code in current form at

http://cvs.sourceforge.net/viewcvs.py/freesteam/freesteam/units.h?rev=1.9&view=markup

The aim is to have zero runtime overhead on the units class. It's purely template related stuff which creates the output operator. Therefore there is no 'obj.units'.

Also, I want the display of units to be individually switched per stream. So I can have one stream open with units being written out, and another with no units being written.

JP
0
 
LVL 6

Expert Comment

by:guitaristx
ID: 12821975
Wow....

Okay, after looking at your code, I'm going to recommend my original suggestion - overload the ostream class.  However, you may run into problems, since by overloading the class, you're making a subclass of ostream.  This being said, most people don't use ostream, they use subclasses of it (ofstream, ostringstream, etc).

What I'm thinking is something like this (and, unfortunately, you'll have to make overloads for all of the ostream-derived classes that you want to use, unless you want to get into multiple-inheritance):

class myofstream : public ofstream
{
protected:
     bool showunits;
public:
     void setShowUnits(bool set);
}

Now, make sure that you have overloaded output operators for your derived stream class(es).

Now, check out this URL:
http://www.informit.com/articles/article.asp?p=171014&seqNum=2
for instructions on how to implement a stream manipulator.  Manipulators are functions that operate on streams using the stream insertion operator (<<).  Now, you should be able to set this up so you can do this:

myofstream str("myfile.txt");
Units x = /*some value*/;
str << showunits(true) << x << endl;
str << showunits(false) << x << endl;

Is this the end result you're looking for?
0
 
LVL 7

Author Comment

by:jdpipe
ID: 12826623
Yeah except I don't want to overload the stream classes, seems like a lot of clutter.

What about rstaverley's xalloc and iword suggestions?

JP
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12827857
Do you need any help with xalloc/iword, JP?
0
 
LVL 7

Author Comment

by:jdpipe
ID: 13127749
Not abandoned, just a work in progress... I am interested in the answer but I haven't had a chance to test this xalloc/iword stuff
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question