custom stream manipulator for class
Asked Answered
B

4

1

I am trying to write a simple audit class that takes input via operator << and writes the audit after receiving a custom manipulator like this:

class CAudit
{
public:
    //needs to be templated
    CAudit& operator << ( LPCSTR data ) {
        audittext << data;
        return *this;
    }

    //attempted manipulator
    static CAudit& write(CAudit& audit) { 
        //write contents of audittext to audit and clear it
        return audit; 
    }

private:
    std::stringstream audittext;
};

//to be used like
CAudit audit;
audit << "Data " << data << " received at " << time << CAudit::write;

I recognise that the overloaded operator in my code does not return a stream object but was wondering if it was still possible to use a manipulator like syntax. Currently the compiler is seeing the '<<' as the binary right shift operator.

Thanks for any input, Patrick

Blakey answered 25/8, 2009 at 14:15 Comment(0)
S
5

To make it work you have to add overload of operator << for functions, than call the function from it:

 class CAudit
 {
  //...other details here as in original question

  CAudit& operator << (CAudit & (*func)(CAudit &))
  {
        return func(*this);
  }
 };

 CAudit audit;
 audit << "some text" << CAudit::write;
Shelley answered 25/8, 2009 at 14:40 Comment(0)
T
2

Binary shift operator and stream operator is the same operator. It is completely legal to overload operator+ for your class to write "Hello world" on std::cout (although it is a very bad idea). The same way C++ standard authors decided to overload operator<< for streams as writing to the stream.
You didn't write clearly what is your problem. My guess is compilation error. The best thing in this case is to quote the error message. If I am right, the problem is, that you defined only operator<< for LPCSTR, and then you want it to work function object on the right side.
You use word "manipulator", but you misunderstand something. Manipulator for a stream (stream from STL) is a function that performs some actions on the stream it is written to. And it works only because of this overload:

ostream& operator<< (ostream& ( *pf )(ostream&));

which takes a function and applies it to a stream.
Similarly you need:

CAudit& operator<< (CAudit& ( *pf )(CAudit& audit))
{
  return (*pf)(audit);
}
Team answered 25/8, 2009 at 14:46 Comment(0)
K
1

Wouldn't this

class CAudit
{
public:
    template< typename T >
    CAudit& operator<<( const T& data )
    {
        audittext << data;
        return *this;
    }

    class write {};

    void operator<<( const write& data )
    {
        /* whatever */
    }

private:
    std::stringstream audittext;
};

do what you want?

Kokoschka answered 25/8, 2009 at 14:35 Comment(1)
Thanks for the answer, looks like it would work in similar way to robson's answer, will probably try using a functor to get rid of the function pointer param.Blakey
G
1

I do something very similar for tracing, but use a stringstream. This ensures that all 3rd party operator << () and manipulators work. I also use the desctructor instead of the customer write manipulator.

class DebugStream
{
public:
    DebugStream(short level, const char * file, int line) {
        sstream << "L" << level << "\t" << file << "\t" << line << "\t";
    }
    ~DebugStream() { write(sstream.str()); }

    std::ostream & stream() { return sstream; }
private:
    std::stringstream sstream;

    DebugStream(const DebugStream &);
    DebugStream & operator=(const DebugStream &);
};

This is then made available with some macros:

#define DBG_ERROR if (1<=dbg_level()) DebugStream(1, __FILE__, __LINE__).stream()
#define DBG_INFO  if (2<=dbg_level()) DebugStream(2, __FILE__, __LINE__).stream()

And the code just uses the macros

DBG_INFO << "print some debug information";

You don't need a specific write manipulator to flush the data to the log file. When the anonymous DebugStream object goes out of scope (once control leaves the line) the the contents are automatically written.

Although I usually avoid macros in this case the use of the if statement means you don't have the overhead of building the trace line unless you actually require it.

Returning the ostream via the stream() method enables this to work for global member functions, as anonymous objects cannot be passed as non-const reference parameters.

Guadalupe answered 25/8, 2009 at 15:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.