0

What is the correct way to implement a printable class in C++ if one wants to avoid processing iostream as much as possible?

This question raises from the interaction between iostream, iosfwd with templates.

1. Traditional approach

The traditional approach as far as I know is:

MyClass.hpp

#include<iostream>

template<class T>
class MyClass{
  ...

  friend std::ostream& operator<<(std::ostream& os, MyClass const& self){...; return os;}
};

However although some classes can be printable, e.g. for debugging, it is not necessarily true that all users will be interested in printing anything. These users will be paying (at compile time) a price for something that will never use. That is to include and compile iostream.

(There is iosfwd but it is not useful for header-only.)

2. Template operator<<

template<class T>
class MyClass{
  ...

  template<Ostream>
  friend Ostream& operator<<(Ostream& os, MyClass const& self){...; return os;}
};

This one I don't have to include iostream in the header file. It also has the problem that operator<< may return not standard objects, like std::ofstream (instead of the base std::ostream).

Users interested in printing will have to include iostream themselves (and know this to some degree.)

Drawbacks is that Ostream can match other things. One can combine this with iosfwd to constrain Ostream (e.g. enable_if<is_base<std::ostream, Ostream>::value>.) Not sure if it works.

3. Separate header MyClass/io.hpp for printing

Add a separate file

MyClass/io.hpp

  #include<iostream>

  template<class T>
  std::ostream& operator<<(std::ostream& os, MyClass<T> const& self){...; return os;}

In this case, users will have to include MyClass/io.hpp to print MyClass (and know that).

Also, it may need friendship to print private members, and for that it will need <iosfwd> in MyClass.h.

I have seen this design in boost/units/io.hpp library.

4. Combine 2 and 3

Go full template

MyClass/io.hpp

  template<class Ostream, class T>
  Ostream& operator<<(Ostream& os, MyClass<T> const& self){...; return os;}

What is the recommended way to interact with std::iostream in when using templates and header-only and avoiding compilation of iostream if possible?

In conclusion, it seems that using separate files can avoid the inclusion of <iostream>. Templates and without need for friendship can avoid iosfwd as well. Restricted templates or need for friendship may still need iosfwd.

alfC
  • 14,261
  • 4
  • 67
  • 118
  • IMHO, to avoid the formatting from `ostream`, format your text to a buffer, then block write the buffer to the output stream of your choice. Thus, you can avoid most of the overhead of `cout` or other output streams. – Thomas Matthews Sep 24 '21 at 22:51
  • @ThomasMatthews, well, any non-trivial pure-string formatting will need `stringstream`, `to_string`, `boost-karma` or a lot of c-like code. Also I would be paying to process a lot of code. – alfC Sep 24 '21 at 22:55
  • You could write your own formatting code rather than using `stringstream`, `to_string` or `boost-karma`. The point being to split formatting from the actual I/O (and buffering). – Thomas Matthews Sep 24 '21 at 22:59
  • Is the compile time cost really worth the hassle of all this? if so, I'd just do #2 with `enable_if`. But I doubt it. – Mooing Duck Sep 24 '21 at 23:02
  • @ThomasMatthews, do you mean printing integers and double? that would be a lot of code. And even if possible, I would by again paying to compile or at least process all this code that would not be often used. (the premise of the question is that one has a library class that is not always printed) – alfC Sep 24 '21 at 23:03
  • Let me turn this one around. You're the one with the compilation issue for the streams. What are you trying to accomplish? The C++ stream hierarchy has lots of places where you can diverge and make the compilation "smaller". – Thomas Matthews Sep 24 '21 at 23:26
  • @ThomasMatthews, I have a very low level library, let's say at the level of ``, some stuff in here can be printed and formatted a bit, specially for debugging. They were not originally printed, so the library didn't depend on `iostream`. Some users of the library and myself, would like to print objects presented by the library library but only once in a while, printing is not the main use but it is of great convenience once in a while or when developing. By hard coding the dependency of `iostream` I am forcing the compilation and processing of a huge library. – alfC Sep 25 '21 at 07:27
  • @ThomasMatthews, `iosfwd` was invented for this purpose, but it not the perfect solution for header-only for the reasons I mention in the question. – alfC Sep 25 '21 at 07:29

0 Answers0