문제

In C++ how do you detect programmatically the end of a sequence of the form:

object << argument1 << argument2 << argument3;

Let's say I have overloaded operator<< like this

Object& operator<<(Object& object, Argument& argument) {
...
}

The above expression will be evaluated like this:

(((object << argument1) << argument2) << argument3);

For the sake of the argument let's say object is a logger. I stream data into the logger. Once the last argument (argument3 in this case, but the number of arguments is not fixed of course) has been streamed into object, I want object to assemble the message and post it. How can I do this without having to write something like this:

object << argument1 << argument2 << argument3 << EOM;

Thank you. Any input welcome.

도움이 되었습니까?

해결책

As other answers mention, using an "end-of-message" marker is a good design.

But if you really need what you asked for, you can do it like this: You implement the EOM idea like you normally would, and then add this class:

class AutoEnd
{
private:
    mutable Object & m_obj;

public:
    explicit AutoEnd (Object & obj) : m_obj (obj) {}
    ~AutoEnd () {m_obj << EOM;}

    Object & obj () const {return m_obj;}
};

template <typename T>
AutoEnd const & operator << (AutoEnd const & ae, T && arg)
{
    ae.obj() << std::forward<T>(arg);
    return ae;
}

And you can use it like this:

Object object;
...
AutoEnd(object) << argument1 << argument2 << argument3;

which will do what you want. Some notes:

  • You can obviously still use the explicit EOM method after adding AutoEnd. This gives you greater flexibility to use whichever method where it makes sense.
  • The key in this technique is the destructor for AutoEnd, which will be called at the end of the line it was instantiated in.
  • You can implement it better than above. Most importantly, it's a good idea to make the constructor of AutoEnd to be private or somehow signal the user that they should not create normal instances of AutoEnd, because they are useless that way and they keep references to you Objects and may try to use them after they are destroyed.
  • You can only use this technique safely if operator << for Object doesn't throw an exception, because it's called inside a destructor and destructors should not throw.
  • Since AutoEnd is designed to be used as a temporary object, and you can only work with const & of temporary objects easily, operator << gets and returns a constant reference, and the stored Object reference is declared as mutable. In my opinion, this is not a bad design, since AutoEnd objects don't have any internal state (apart from the Object &) and the correctness of calling operator << on that needs to be assured elsewhere (i.e. AutoEnd doesn't need to care about that.)
  • You should make AutoEnd non-copyable.

다른 팁

You cannot (unless object gets destroyed after each line). It is quite common to have something like EOM.

Just think, why sequence notation is possible. It's happened, because operator<< returns reference to stream. You can write:

ostream& s = object << argument1;
s << argument2
s << argument3
...

And where is real end of sequence? Only you know, because you design and code it. So EOM is good common practice - use it.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top