Disclaimer

The content of this blog is my personal opinion only. Although I am an employee - currently of Nvidia, in the past of other companies such as Iagination Technologies, MIPS, Intellectual Ventures, Intel, AMD, Motorola, and Gould - I reveal this only so that the reader may account for any possible bias I may have towards my employer's products. The statements I make here in no way represent my employer's position, nor am I authorized to speak on behalf of my employer. In fact, this posting may not even represent my personal opinion, since occasionally I play devil's advocate.

See http://docs.google.com/View?id=dcxddbtr_23cg5thdfj for photo credits.

Friday, October 05, 2012

A better solution than to_string(T) versus operator<<( stream,T)

to_string() versus stream operator<< ?  A commonly occurring quandary for C++ programmers.

For large objects it is often just plain more efficient to write directly to a stream, e.g. output, or write to disk, than it is to write first to a string and then print it.

But it is often nice to be able to take the string representation, and then manipulate it.

I.e. sometimes you want to write to a stream.  Sometimes to a string.  And, yes, I know about ostringstream.   Also ostrstream.  (The ostringstream/ostrstream thrashing in the C++ standard s one reason why this issue was not resolved long ago.)

Also...  sometimes the streams << notation is easier to use than the string concatenation operarir +.  Especially since << often has implicit operators to convert to numerucs etc. to string and then print, whereas you usually don't want to overload operator+(string,T) because that can cause bugs.

---

All of that boils down to: should you provide:

string& to_string(const T& val);

or

std::ostream& operator(ostream& ostr, const T& val);

With the additional caveat that one is often misled by


string& T::to_string() const;


whereas

class T {
public: friend std::ostream& operator<<(ostream& ostr,const T& val);
}

is less misleading.

---

Here's a better way that accomplishes both ends:

class T {
public:
     class Formatter {
           const T& m_val;
           XXX m_extra_stuff; // ...extra paramters, like format directives
      public:
           friend::ostream& operator<<(std::stream&, Formatter);
           Formatter(const T& arg_val, XXX arg_extra_stuff = default) 
               : m_val(arg_val), m_extra_stuff(arg_extra_stuf) 
         {}
};

Used 

std::cout << Formatter(t_object) << "\n";

Or the like.

It's a little bit clunkier to get a string, not quite as elegant as

std::string s = t_object->to_string();

Something like:

std::string s;
std::ostringstream(s) << Formatter(t_object) << "\n";

But perhaps some creative overloading will allow

std::string s = Formatter(t_object) << "\n";

or similar.


---

Q: who do I give credit to?

---

For older C-style programming: print to stream, versus to string?  Same issue, but the C++ streams syntax makes it more pleasant.

To String



class Foo {
public:
  unsigned bazz;
public:
  class Formatter {
  private:
    const Foo& m_objref;
  public:
    Formatter(const Foo& arg_objref) : m_objref(arg_objref) {}
    friend std::ostream& operator<<(std::ostream& ostr, const Formatter f) {
      ostr << std::hex;
      ostr << f.m_objref.bazz;
      ostr << std::dec;
      return ostr;
    }
    operator std::string() const {
      std::ostringstream os;
      os << *this;
      return os.str();
    }
    friend std::string operator+(const std::string& s, const Formatter f)
    {
      return s + (std::string)f;
    }
    friend std::string operator+(const Formatter f, const std::string& s)
    {
      return (std::string)f + s;
    }
    friend std::string operator+(const Formatter f1, const Formatter f2)
    {
      return (std::string)f1 + (std::string)f2;
    }
  };
};



int main()
{
  Foo a, b;
  a.bazz = 0xAAA;
  b.bazz = 0xBBB;

  std::cout << "stream: " << Foo::Formatter(a) << Foo::Formatter(b) << "\n";

  {
    std::string s;
    std::ostringstream sostr(s);
    sostr << "ostringstream: " << Foo::Formatter(a) << Foo::Formatter(b) << "\n";
    std::cout << sostr.str();
  }

  {
    std::string s = Foo::Formatter(a);
    std::cout << "string = f: " << s << std::endl;
  }

#if 1
  {
    std::ostringstream s;
    s << Foo::Formatter(a);
    s << Foo::Formatter(b);
    std::cout << "string; <<; <<: p="p" s="s" std::endl="std::endl">  }
  {
    std::string s = Foo::Formatter(a) + Foo::Formatter(b);
    std::cout << "string = f + f: " << s << std::endl;
  }
#endif

  {
    std::string s = Foo::Formatter(a) + Foo::Formatter(b);
    std::cout << "string = f + f: " << s << std::endl;
  }
  {
    std::string s = "string = s + f: " + Foo::Formatter(b);
    std::cout << s << std::endl;
  }
  {
    std::string s =  Foo::Formatter(b) + ": string = f + s";
    std::cout << s << std::endl;
  }
}


This is painful enough that I would like top make it a mixin.

No comments: