views:

113

answers:

6

I have an object that I want to be able to stream. But I want to be able to stream it in different ways by using different formats, or should I say ways to describe this object. And I wonder how this is supposed to be solved with streams.

What I want is to be able to use a generic format and use some kind of format adapter to transform the generic format into the preferred format.

I also want to be able to separate the format from the implementation of Item, so I do not have to change Item each time a new format is added or changed.

this code illustrate approximately what I want.

Item item;
std::cout << "generic formatted:" << item;
std::cout << "custom formatted:" << CustomItemFormat() << item;

but this might not be possible or practical.

how is the streaming library intended to be used facing such problems?

+1  A: 

Yes it's possible, the magic word you're looking for is stream manipulators.

Check out this question: http://stackoverflow.com/questions/799599/c-custom-stream-manipulator-that-changes-next-item-on-stream

therefromhere
I knew about stream manipulators, but not how they could solve this problem for me. I will have to take a look at this approach.
daramarak
It looks like that it is possible to do it this way, but It looks to me that my problem isn't supposed to be solved like this. It is more to manipulate that stream itself, instead of the output of the object. Perhaps an decorator is the correct way to do this...
daramarak
A: 

What is so bad about Item.CustomItemFormat()?

Alternatively there are some design patterns aimed at solving this problem, namely the visitor pattern.

You could also have CustomItemFormat(Item)?

I dont think that asking streams to solve this problem is the right orientation, you should delegate the task of showing itself to the Item class

Eric
The item should be able to show itself, but it should not have to know about its formats. That would mean that each time a new format arrives, Items has to be changed. A decorator pattern could be used, although it will somewhat unpractical to use when handling many items, or perhaps other kind of items, that also uses a given format.
daramarak
How would an item be able to show itself if it wouldn't know which format to show itself in?
sbi
As noted above, the visitor pattern should solve this. As long as it knows what to show, it don't need to know anything about the format.
daramarak
+1  A: 

You need to write a stream manipulator which stores information in the stream that is then used by operator<<(std::ostream&,const Item&). See the beginning of this answer for how to store user data in a stream.

sbi
Problem here is that the Item (or the streaming operator of Item) would have to know about all formats. I want to seperate the format an the Item, because I know that the Item has a lot of different formats, but I do not know which I will have to use.
daramarak
A: 

The more I think about it I am starting to wonder if I am approaching this the wrong way. Perhaps I should have a generic format interface and setting the format for the item, so the item can output itself using this format.

Does this make sense?

daramarak
+3  A: 

Personally I would write a set of formatters.
The formatters have to know the internals of the object they are formatting but making them friends should not be a big deal.

class X
{ friend std::ostream& operator<<(std::ostream& str,XML_Format const& formatter);
  friend std::ostream& operator<<(std::ostream& str,Json_Format const& formatter);
  friend std::ostream& operator<<(std::ostream& str,Fred_Format const& formatter);
  public: int value() const {return 5;}
};
struct XML__Foram   { X const& print; XML_Format(X const& v):   print(v) {} };
struct Json_Format  { X const& print; Json_Format(X const& v):  print(v) {} };
struct Fred_Format  { X const& print; Fred_Format(X const& v):  print(v) {} };

std::ostream& operator<<(std::ostream& str,XML_Format const& formatter)
{
     return str << "<XObj>" << formatter.print.value() << "</XObj>";
}
std::ostream& operator<<(std::ostream& str,Json_Format const& formatter)
{
     return str << "{XObj:{" << formatter.print.value() << "}}";
}
std::ostream& operator<<(std::ostream& str,Fred_Format const& formatter)
{
     return str << "Killl Kill Kill. Friday 13th";
}

int main()
{
     X   obj;
     std::cout << XML_Format(obj) << std::endl;
}
Martin York
Yes this is almost what I was thinking of. I do not like the strong coupling between the format and the object though. It would like to change the implementation of the class that need the format without having to change the formats.
daramarak
Rather than custom formatters and using `friend`, I use the `Visitor` pattern and create custom *Writers*. So without having to add new `operator <<` for each new format, I just derive from the *Writer* interface. Thus I don't have modify a working class.
Thomas Matthews
@Thomas: Iys 6 of one half a dozen of the other. My methods write a new stream operator. Your method write a new Writter interface. The coupling is the same.
Martin York
A: 

Try using the Visitor design pattern:

struct Object_Writer_Interface
{
  virtual void write_member_i(int value) = 0;
  virtual void write_member_c(char value) = 0;
};

struct Object
{
    int i;
    char c;
    void write(Object_Writer_Interface * p_writer)
    {
        if (p_writer)
        {
            p_writer->write_member_i(i);
            p_writer->write_member_c(c);
        }
    }
};

struct Stream_Object_Writer
  : public Object_Writer_Interface
{
    Stream_Object_Writer(std::ostream& out)
       : m_out(out)
       { ; }
    void write_member_i(int value)
    {
        m_out << i << '\n';
    }
    void write_member_c(char c)
    {
        m_out << "'" << c << "'\n";
    }
    std::ostream& m_out;
};


int main(void)
{
    Object  o;
    o.i = 5;
    o.c = 'M';

    // Create a writer for std::cout
    Stream_Object_Writer writer(std::cout);

    // Write the object to std::cout using the writer
    o.write(&writer);

    return EXIT_SUCCESS;
}

With this design, to write the objects to a socket, you derive a class from Object_Writer_Interface to handle the socket. And similarly if you want to serialize the Object.

I'm currently using this technique for writing objects to GUI list boxes, database records and Xml files. Unlike the technique for overloading operator <<, I didn't have to modify the principle class when developing new Writers.

Thomas Matthews
Well it looks like another Java developer is trying to write C++
Martin York
@Martin York, Could you elaborate on that comment. What in this solution do you disagree on?
daramarak
All I said was it looked like a Java solution to the problem (ie It is not very C++ like). Two Problems. 1) Because it is not C++ like it will be hard to integrate with the libraries the try and be C++ like (STL, boost jump to mind). 2) there is a tight coupling between Object and Object_Writer_Interface. Any change to the implementation of Object will require the interface and all its dependents to be re-written.
Martin York
@Martin York, Why is this not C++ like? This is a standard implementation, and I have implemented it years before I learned Java. In my implementations, and all the implementations I've worked on, the Writers change more often than the Objects do. When adding members to an object, both the `operator <<` and the above designs must change. The point of this design is that new *Writers* do not cause a change to the Object vs. the `operator <<` method. Please give example of a C++ implementation if this isn't.
Thomas Matthews
Try using this method with any of the standard algorithms. Such as std::copy, using the std::istream_iterators or any of the other things that happen in the STL.
Martin York