Deriving my own stream class

Discussion in 'C++' started by Dan Smithers, Jun 17, 2008.

  1. Dan Smithers

    Dan Smithers Guest

    I want to write my own class derived from the ostream class.

    I have been getting errors with my templates:

    First, I get an error writing a nested template. If I leave the function
    definition inside template class definition (commented out at //1) then
    it compiles and runs fine, but if I declare and define the function
    separately (at //2).

    Is the following syntax supported by g++?
    template<typename charT, typename Traits>
    template<typename T>
    as I get the compiler error
    "mystream.cpp:47: error: too many template-parameter-lists"

    I can't get the use_facet command to work (commented out at //3) as I no
    longer have visibility of ostr for getloc and can't call it on my
    derived class. Is there another way of achieving this?

    I can't get the manipulator function / object combination to compile.
    "mystream_test.cpp:11: error: no matching function for call to
    ‘setfmt(const char [6])’"
    Do I need to explicitly qualify the template?

    Finally, the compiler seems to get confused by the output operator in
    the manipulator (at //4) and issues the following warning:
    "mystream.h:41: warning: friend declaration ‘Ostream&
    operator<<(Ostream&, const osmanip<Ostream, Arg>&)’ declares a
    non-template function
    mystream.h:41: warning: (if this is not what you intended, make sure the
    function template has already been declared and add <> after the
    function name here) -Wno-non-template-friend disables this warning"

    Are these problems due to my own misunderstanding of templates?

    thanks

    dan

    I have written my templates in three file (mystream.h, mystream.cpp and
    mystream_test.cpp) and compiling with g++ 4.2.3

    // mystream.h
    #include <iostream>
    #ifndef __MY_STREAM_H
    #define __MY_STREAM_H

    template <typename charT, typename Traits=std::char_traits<charT> >
    class CMyStream : virtual public std::basic_ostream<charT, Traits>
    {
    public:
    charT *m_fmt;

    public:
    // constructor
    CMyStream(std::basic_ostream<charT, Traits>& ostr, const char *fmt);
    // destructor
    ~CMyStream();
    // change format string
    std::basic_ostream<charT, Traits>& format(const char *fmt);
    // retrieve format string
    charT *format() const;

    // output operator
    template<typename T>
    friend CMyStream& operator<<(CMyStream& ostr, T val); //1
    // {
    // (std::basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    // (std::basic_ostream<charT, Traits>&)ostr << val;
    // }

    };

    template <class Ostream, class Arg>
    class osmanip {
    public:
    osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg);

    protected:
    Ostream& (*pf_)(Ostream&, Arg);
    Arg arg_;

    friend Ostream&
    operator<< (Ostream& ostr, const osmanip<Ostream,Arg>& manip);
    };

    template <class Ostream, class Arg>
    Ostream&
    operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip);

    template <class charT, class Traits>
    inline std::basic_ostream<charT,Traits>&
    sfmt(std::basic_ostream<charT,Traits>& ostr, const char* f);

    template <class charT, class Traits>
    inline osmanip<std::basic_ostream<charT, Traits>, const char*>
    setfmt(const char* fmt);

    #include "mystream.cpp"

    #endif /* __MY_STREAM_H */

    // mystream.cpp
    #include "mystream.h"

    #ifndef __MY_STREAM_CPP
    #define __MY_STREAM_CPP

    using std::basic_ostream;
    using std::use_facet;
    using std::ctype;

    template <typename charT, typename Traits>
    CMyStream<charT, Traits>::CMyStream(basic_ostream<charT, Traits>& ostr,
    const char *fmt = "log")
    : std::eek:stream(ostr.rdbuf())
    {
    m_fmt = new charT[strlen(fmt)];
    use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    m_fmt);
    }

    template <typename charT, typename Traits>
    CMyStream<charT, Traits>::~CMyStream()
    {
    delete[] m_fmt;
    }

    template <typename charT, typename Traits>
    basic_ostream<charT, Traits>&
    CMyStream<charT, Traits>::format(const char *fmt)
    {
    delete[] m_fmt;
    m_fmt = new charT[strlen(fmt)];
    // use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    m_fmt); //3
    return *this;
    }

    template <typename charT, typename Traits>
    charT *
    CMyStream<charT, Traits>::format() const
    {
    charT *p = new charT[Traits::length(m_fmt)];
    Traits::copy(p, m_fmt, Traits::length(m_fmt));
    return p;
    }

    template <typename charT, typename Traits=std::char_traits<charT> >
    template<typename T>
    CMyStream<charT, Traits>& operator<<(CMyStream<charT, Traits>& ostr, //2
    T val)
    {
    (basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    (basic_ostream<charT, Traits>&)ostr << val;
    }

    template <class Ostream, class Arg>
    osmanip<Ostream, Arg>::eek:smanip(Ostream& (*pf)(Ostream&, Arg), Arg arg)
    : pf_(pf) , arg_(arg)
    {
    ;
    }

    //4
    template <class Ostream, class Arg>
    Ostream& operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip)
    {
    (*manip.pf_)(ostr,manip.arg_);
    return ostr;
    }

    template <class charT, class Traits>
    inline basic_ostream<charT,Traits>&
    sfmt(basic_ostream<charT,Traits>& ostr, const char* f)
    {
    CMyStream<charT,Traits>* p;
    try {
    p = dynamic_cast<CMyStream<charT,Traits>*>(&ostr);
    }
    catch (std::bad_cast) {
    return ostr;
    }

    p->format(f);
    return ostr;
    }

    template <class charT,class Traits>
    inline osmanip<basic_ostream<charT, Traits>,const char*>
    setfmt(const char* fmt)
    {
    return osmanip<basic_ostream<charT,Traits>,const char*>(sfmt,fmt);
    }

    #endif /* __MY_STREAM_CPP */

    // mystream_test.cpp
    #include "mystream.h"

    int
    main(int argc, char *argv[])
    {
    CMyStream<char> strm(std::cout);

    strm << "Hello World!" << std::endl;
    strm << "123 " << 123 << std::endl;

    strm << setfmt<char>("ERROR") << "Byeee" << std::endl;

    return 0;
    }
    Dan Smithers, Jun 17, 2008
    #1
    1. Advertising

  2. Dan Smithers

    Guest

    On Jun 17, 4:19 pm, Dan Smithers <> wrote:
    > I want to write my own class derived from the ostream class.
    >
    > I have been getting errors with my templates:
    >
    > First, I get an error writing a nested template. If I leave the function
    > definition inside template class definition (commented out at //1) then
    > it compiles and runs fine, but if I declare and define the function
    > separately (at //2).
    >
    > Is the following syntax supported by g++?
    > template<typename charT, typename Traits>
    > template<typename T>
    > as I get the compiler error
    > "mystream.cpp:47: error: too many template-parameter-lists"
    >
    > I can't get the use_facet command to work (commented out at //3) as I no
    > longer have visibility of ostr for getloc and can't call it on my
    > derived class. Is there another way of achieving this?
    >
    > I can't get the manipulator function / object combination to compile.
    > "mystream_test.cpp:11: error: no matching function for call to
    > ‘setfmt(const char [6])’"
    > Do I need to explicitly qualify the template?
    >
    > Finally, the compiler seems to get confused by the output operator in
    > the manipulator (at //4) and issues the following warning:
    > "mystream.h:41: warning: friend declaration ‘Ostream&
    > operator<<(Ostream&, const osmanip<Ostream, Arg>&)’ declares a
    > non-template function
    > mystream.h:41: warning: (if this is not what you intended, make sure the
    > function template has already been declared and add <> after the
    > function name here) -Wno-non-template-friend disables this warning"
    >
    > Are these problems due to my own misunderstanding of templates?
    >
    > thanks
    >
    > dan
    >
    > I have written my templates in three file (mystream.h, mystream.cpp and
    > mystream_test.cpp) and compiling with g++ 4.2.3
    >
    > // mystream.h
    > #include <iostream>
    > #ifndef __MY_STREAM_H
    > #define __MY_STREAM_H
    >
    > template <typename charT, typename Traits=std::char_traits<charT> >
    > class CMyStream : virtual public std::basic_ostream<charT, Traits>
    > {
    > public:
    >   charT *m_fmt;
    >
    > public:
    >   // constructor
    >   CMyStream(std::basic_ostream<charT, Traits>& ostr, const char *fmt);
    >   // destructor
    >   ~CMyStream();
    >   // change format string
    >   std::basic_ostream<charT, Traits>& format(const char *fmt);
    >   // retrieve format string
    >   charT *format() const;
    >
    >   // output operator
    >   template<typename T>
    >   friend CMyStream& operator<<(CMyStream& ostr, T val); //1
    > //   {
    > //     (std::basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    > //     (std::basic_ostream<charT, Traits>&)ostr << val;
    > //   }
    >
    > };
    >
    > template <class Ostream, class Arg>
    > class osmanip {
    > public:
    >   osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg);
    >
    > protected:
    >   Ostream&     (*pf_)(Ostream&, Arg);
    >   Arg          arg_;
    >
    >   friend Ostream&
    >   operator<< (Ostream& ostr, const osmanip<Ostream,Arg>& manip);
    >
    > };
    >
    > template <class Ostream, class Arg>
    > Ostream&
    > operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip);
    >
    > template <class charT, class Traits>
    > inline std::basic_ostream<charT,Traits>&
    > sfmt(std::basic_ostream<charT,Traits>& ostr, const char* f);
    >
    > template <class charT, class Traits>
    > inline osmanip<std::basic_ostream<charT, Traits>, const char*>
    > setfmt(const char* fmt);
    >
    > #include "mystream.cpp"
    >
    > #endif /* __MY_STREAM_H */
    >
    > // mystream.cpp
    > #include "mystream.h"
    >
    > #ifndef __MY_STREAM_CPP
    > #define __MY_STREAM_CPP
    >
    > using std::basic_ostream;
    > using std::use_facet;
    > using std::ctype;
    >
    > template <typename charT, typename Traits>
    > CMyStream<charT, Traits>::CMyStream(basic_ostream<charT, Traits>& ostr,
    >                                     const char *fmt = "log")
    >   : std::eek:stream(ostr.rdbuf())
    > {
    >   m_fmt = new charT[strlen(fmt)];
    >   use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    > m_fmt);
    >
    > }
    >
    > template <typename charT, typename Traits>
    > CMyStream<charT, Traits>::~CMyStream()
    > {
    >   delete[] m_fmt;
    >
    > }
    >
    > template <typename charT, typename Traits>
    > basic_ostream<charT, Traits>&
    > CMyStream<charT, Traits>::format(const char *fmt)
    > {
    >   delete[] m_fmt;
    >   m_fmt = new charT[strlen(fmt)];
    > //  use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    > m_fmt); //3
    >   return *this;
    >
    > }
    >
    > template <typename charT, typename Traits>
    > charT *
    > CMyStream<charT, Traits>::format() const
    > {
    >   charT *p = new charT[Traits::length(m_fmt)];
    >   Traits::copy(p, m_fmt, Traits::length(m_fmt));
    >   return p;
    >
    > }
    >
    > template <typename charT, typename Traits=std::char_traits<charT> >
    > template<typename T>
    > CMyStream<charT, Traits>& operator<<(CMyStream<charT, Traits>& ostr, //2
    >                                      T val)
    > {
    >   (basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    >   (basic_ostream<charT, Traits>&)ostr << val;
    >
    > }
    >
    > template <class Ostream, class Arg>
    > osmanip<Ostream, Arg>::eek:smanip(Ostream& (*pf)(Ostream&, Arg), Arg arg)
    >   : pf_(pf) , arg_(arg)
    > {
    >   ;
    >
    > }
    >
    > //4
    > template <class Ostream, class Arg>
    > Ostream& operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip)
    > {
    >    (*manip.pf_)(ostr,manip.arg_);
    >    return ostr;
    >
    > }
    >
    > template <class charT, class Traits>
    > inline basic_ostream<charT,Traits>&
    > sfmt(basic_ostream<charT,Traits>& ostr, const char* f)
    > {
    >   CMyStream<charT,Traits>* p;
    >   try {
    >     p = dynamic_cast<CMyStream<charT,Traits>*>(&ostr);
    >   }
    >   catch (std::bad_cast) {
    >       return ostr;
    >   }
    >
    >   p->format(f);
    >   return ostr;
    >
    > }
    >
    > template <class charT,class Traits>
    > inline osmanip<basic_ostream<charT, Traits>,const char*>
    > setfmt(const char* fmt)
    > {
    >   return osmanip<basic_ostream<charT,Traits>,const char*>(sfmt,fmt);
    >
    > }
    >
    > #endif /* __MY_STREAM_CPP */
    >
    > // mystream_test.cpp
    > #include "mystream.h"
    >
    > int
    > main(int argc, char *argv[])
    > {
    >   CMyStream<char> strm(std::cout);
    >
    >   strm << "Hello World!" << std::endl;
    >   strm << "123 " << 123 << std::endl;
    >
    >   strm << setfmt<char>("ERROR") << "Byeee" << std::endl;
    >
    >   return 0;
    >
    >
    >
    > }- Hide quoted text -
    >
    > - Show quoted text -


    Hello,

    If i am correct, setfmt template has two template parameters, while
    one is alone used when called.
    Also, you may want to replace this
    template <typename charT, typename Traits=std::char_traits<charT> >

    with

    template <typename charT, class Traits=std::char_traits<charT> >

    I didn't get why would want to write another stream for char when its
    already available.

    Thanks,
    Balaji.
    , Jun 17, 2008
    #2
    1. Advertising

  3. Dan Smithers

    Dan Smithers Guest

    Thanks Balaji,

    wrote:
    > On Jun 17, 4:19 pm, Dan Smithers <> wrote:
    >> I want to write my own class derived from the ostream class.
    >>
    >> I have been getting errors with my templates:
    >>
    >> First, I get an error writing a nested template. If I leave the function
    >> definition inside template class definition (commented out at //1) then
    >> it compiles and runs fine, but if I declare and define the function
    >> separately (at //2).
    >>
    >> Is the following syntax supported by g++?
    >> template<typename charT, typename Traits>
    >> template<typename T>
    >> as I get the compiler error
    >> "mystream.cpp:47: error: too many template-parameter-lists"
    >>
    >> I can't get the use_facet command to work (commented out at //3) as I no
    >> longer have visibility of ostr for getloc and can't call it on my
    >> derived class. Is there another way of achieving this?
    >>
    >> I can't get the manipulator function / object combination to compile.
    >> "mystream_test.cpp:11: error: no matching function for call to
    >> ‘setfmt(const char [6])’"
    >> Do I need to explicitly qualify the template?
    >>
    >> Finally, the compiler seems to get confused by the output operator in
    >> the manipulator (at //4) and issues the following warning:
    >> "mystream.h:41: warning: friend declaration ‘Ostream&
    >> operator<<(Ostream&, const osmanip<Ostream, Arg>&)’ declares a
    >> non-template function
    >> mystream.h:41: warning: (if this is not what you intended, make sure the
    >> function template has already been declared and add <> after the
    >> function name here) -Wno-non-template-friend disables this warning"
    >>
    >> Are these problems due to my own misunderstanding of templates?
    >>
    >> thanks
    >>
    >> dan
    >>
    >> I have written my templates in three file (mystream.h, mystream.cpp and
    >> mystream_test.cpp) and compiling with g++ 4.2.3
    >>
    >> // mystream.h
    >> #include <iostream>
    >> #ifndef __MY_STREAM_H
    >> #define __MY_STREAM_H
    >>
    >> template <typename charT, typename Traits=std::char_traits<charT> >
    >> class CMyStream : virtual public std::basic_ostream<charT, Traits>
    >> {
    >> public:
    >> charT *m_fmt;
    >>
    >> public:
    >> // constructor
    >> CMyStream(std::basic_ostream<charT, Traits>& ostr, const char *fmt);
    >> // destructor
    >> ~CMyStream();
    >> // change format string
    >> std::basic_ostream<charT, Traits>& format(const char *fmt);
    >> // retrieve format string
    >> charT *format() const;
    >>
    >> // output operator
    >> template<typename T>
    >> friend CMyStream& operator<<(CMyStream& ostr, T val); //1
    >> // {
    >> // (std::basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    >> // (std::basic_ostream<charT, Traits>&)ostr << val;
    >> // }
    >>
    >> };
    >>
    >> template <class Ostream, class Arg>
    >> class osmanip {
    >> public:
    >> osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg);
    >>
    >> protected:
    >> Ostream& (*pf_)(Ostream&, Arg);
    >> Arg arg_;
    >>
    >> friend Ostream&
    >> operator<< (Ostream& ostr, const osmanip<Ostream,Arg>& manip);
    >>
    >> };
    >>
    >> template <class Ostream, class Arg>
    >> Ostream&
    >> operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip);
    >>
    >> template <class charT, class Traits>
    >> inline std::basic_ostream<charT,Traits>&
    >> sfmt(std::basic_ostream<charT,Traits>& ostr, const char* f);
    >>
    >> template <class charT, class Traits>
    >> inline osmanip<std::basic_ostream<charT, Traits>, const char*>
    >> setfmt(const char* fmt);
    >>
    >> #include "mystream.cpp"
    >>
    >> #endif /* __MY_STREAM_H */
    >>
    >> // mystream.cpp
    >> #include "mystream.h"
    >>
    >> #ifndef __MY_STREAM_CPP
    >> #define __MY_STREAM_CPP
    >>
    >> using std::basic_ostream;
    >> using std::use_facet;
    >> using std::ctype;
    >>
    >> template <typename charT, typename Traits>
    >> CMyStream<charT, Traits>::CMyStream(basic_ostream<charT, Traits>& ostr,
    >> const char *fmt = "log")
    >> : std::eek:stream(ostr.rdbuf())
    >> {
    >> m_fmt = new charT[strlen(fmt)];
    >> use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    >> m_fmt);
    >>
    >> }
    >>
    >> template <typename charT, typename Traits>
    >> CMyStream<charT, Traits>::~CMyStream()
    >> {
    >> delete[] m_fmt;
    >>
    >> }
    >>
    >> template <typename charT, typename Traits>
    >> basic_ostream<charT, Traits>&
    >> CMyStream<charT, Traits>::format(const char *fmt)
    >> {
    >> delete[] m_fmt;
    >> m_fmt = new charT[strlen(fmt)];
    >> // use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    >> m_fmt); //3
    >> return *this;
    >>
    >> }
    >>
    >> template <typename charT, typename Traits>
    >> charT *
    >> CMyStream<charT, Traits>::format() const
    >> {
    >> charT *p = new charT[Traits::length(m_fmt)];
    >> Traits::copy(p, m_fmt, Traits::length(m_fmt));
    >> return p;
    >>
    >> }
    >>
    >> template <typename charT, typename Traits=std::char_traits<charT> >
    >> template<typename T>
    >> CMyStream<charT, Traits>& operator<<(CMyStream<charT, Traits>& ostr, //2
    >> T val)
    >> {
    >> (basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    >> (basic_ostream<charT, Traits>&)ostr << val;
    >>
    >> }
    >>
    >> template <class Ostream, class Arg>
    >> osmanip<Ostream, Arg>::eek:smanip(Ostream& (*pf)(Ostream&, Arg), Arg arg)
    >> : pf_(pf) , arg_(arg)
    >> {
    >> ;
    >>
    >> }
    >>
    >> //4
    >> template <class Ostream, class Arg>
    >> Ostream& operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip)
    >> {
    >> (*manip.pf_)(ostr,manip.arg_);
    >> return ostr;
    >>
    >> }
    >>
    >> template <class charT, class Traits>
    >> inline basic_ostream<charT,Traits>&
    >> sfmt(basic_ostream<charT,Traits>& ostr, const char* f)
    >> {
    >> CMyStream<charT,Traits>* p;
    >> try {
    >> p = dynamic_cast<CMyStream<charT,Traits>*>(&ostr);
    >> }
    >> catch (std::bad_cast) {
    >> return ostr;
    >> }
    >>
    >> p->format(f);
    >> return ostr;
    >>
    >> }
    >>
    >> template <class charT,class Traits>
    >> inline osmanip<basic_ostream<charT, Traits>,const char*>
    >> setfmt(const char* fmt)
    >> {
    >> return osmanip<basic_ostream<charT,Traits>,const char*>(sfmt,fmt);
    >>
    >> }
    >>
    >> #endif /* __MY_STREAM_CPP */
    >>
    >> // mystream_test.cpp
    >> #include "mystream.h"
    >>
    >> int
    >> main(int argc, char *argv[])
    >> {
    >> CMyStream<char> strm(std::cout);
    >>
    >> strm << "Hello World!" << std::endl;
    >> strm << "123 " << 123 << std::endl;
    >>
    >> strm << setfmt<char>("ERROR") << "Byeee" << std::endl;
    >>
    >> return 0;
    >>
    >>
    >>
    >> }- Hide quoted text -
    >>
    >> - Show quoted text -

    >
    > Hello,
    >
    > If i am correct, setfmt template has two template parameters, while
    > one is alone used when called.
    > Also, you may want to replace this
    > template <typename charT, typename Traits=std::char_traits<charT> >
    >
    > with
    >
    > template <typename charT, class Traits=std::char_traits<charT> >


    I thought that class and typename were synonymous in this context.
    >
    > I didn't get why would want to write another stream for char when its
    > already available.


    The reason why I'm doing this is that I have a log class that opens a
    file and sends a string together with time information to it. My initial
    implementation used a method log(const string& msg) to do this, but that
    means that I have to pre-format the string.

    ostringstream oss;
    oss << "log message" << log_val ...;
    my_log(oss.str());

    If I make my log class inherit from ostream then I can simply write

    my_log << "log_message" << log_val ...;

    I thought that while I was doing this I would set up a manipulator so
    that I can writethings like
    my_log << setformat(Error) << "error message" << err_code;

    my_log << setformat(Warning) << "warning message << warning_data;

    thanks

    dan
    Dan Smithers, Jun 17, 2008
    #3
  4. Hello,

    Dan Smithers wrote:

    > I want to write my own class derived from the ostream class.
    >
    > I have been getting errors with my templates:
    >
    > First, I get an error writing a nested template. If I leave the
    > function definition inside template class definition (commented out at
    > //1) then it compiles and runs fine, but if I declare and define the
    > function separately (at //2).


    See the corrections below. I've tried to omit the friends where
    possible. I have introduce a new public member.

    >
    > Is the following syntax supported by g++?
    > template<typename charT, typename Traits>
    > template<typename T>
    > as I get the compiler error
    > "mystream.cpp:47: error: too many template-parameter-lists"


    This happens with template methods of template classes only, you want to
    write a free template function, so this should be one list, just
    include the typename T in the first list.


    >
    > I can't get the use_facet command to work (commented out at //3) as I
    > no longer have visibility of ostr for getloc and can't call it on my
    > derived class. Is there another way of achieving this?


    This is due to two-phase lookup. The ostr at that place was not declared
    anyway, but just using getloc() within the stream class does not work
    either, it has to be this->getloc() to tell the compiler that the
    function will be available at instantiation of the template.

    >
    > I can't get the manipulator function / object combination to compile.
    > "mystream_test.cpp:11: error: no matching function for call to
    > ?setfmt(const char [6])?"
    > Do I need to explicitly qualify the template?


    Yes, you have without more changes. The problem is that the template
    parameters for the output osmanip instance cannot be derived from input
    parameters alone. The input is always const char*. You probably will
    have to use a common osmanip for all streams and distinguish the
    streams your manipulator can act on dynamically.

    >
    > Finally, the compiler seems to get confused by the output operator in
    > the manipulator (at //4) and issues the following warning:
    > "mystream.h:41: warning: friend declaration ?Ostream&
    > operator<<(Ostream&, const osmanip<Ostream, Arg>&)? declares a
    > non-template function
    > mystream.h:41: warning: (if this is not what you intended, make sure
    > the function template has already been declared and add <> after the
    > function name here) -Wno-non-template-friend disables this warning"
    >
    > Are these problems due to my own misunderstanding of templates?


    Some I think yes. I have some feeling your are following manuals for
    some pre-standard compiler or streams library. You can expect that some
    things do not work out of the box.

    There have been books written just on getting the stuff with streams and
    locales right, there are cans full of worms and lots of errors to get
    easily caught by. There are existing libraries for logging I would
    consider first before rolling my own.

    I have put all your code into one file with a few comments and tried to
    get it running, see below.

    I hope some of the experts on this can help if I have missed something,
    especially on getting the manipulator right without the need to
    qualify.

    HTH,

    Bernd Strieder




    ################################################################


    // mystream.h
    #include <iostream>
    #ifndef __MY_STREAM_H
    #define __MY_STREAM_H

    template <typename charT, typename Traits=std::char_traits<charT> >
    class CMyStream;

    template <typename charT, typename Traits >
    class CMyStream : virtual public std::basic_ostream<charT, Traits>
    {
    public:
    charT *m_fmt;

    public:
    // constructor
    CMyStream(std::basic_ostream<charT, Traits>& ostr, const char *fmt);
    // destructor
    ~CMyStream();
    // change format string
    std::basic_ostream<charT, Traits>& format(const char *fmt);
    // retrieve format string
    charT *format() const;

    };

    template <class Ostream, class Arg>
    class osmanip;


    template <class Ostream, class Arg>
    Ostream&
    operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip);

    template <class Ostream, class Arg>
    class osmanip {
    public:
    osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg);

    Ostream& manipulate(Ostream&) const;


    protected:
    Ostream& (*pf_)(Ostream&, Arg);
    Arg arg_;

    };


    template <class charT, class Traits>
    inline std::basic_ostream<charT,Traits>&
    sfmt(std::basic_ostream<charT,Traits>& ostr, const char* f);

    template <class charT, class Traits>
    inline osmanip<std::basic_ostream<charT, Traits>, const char*>
    setfmt(const char* fmt);

    //#include "mystream.cpp"`

    #endif /* __MY_STREAM_H */

    // mystream.cpp
    //#include "mystream.h"

    #ifndef __MY_STREAM_CPP
    #define __MY_STREAM_CPP

    using std::basic_ostream;
    using std::use_facet;
    using std::ctype;

    template <typename charT, typename Traits>
    CMyStream<charT, Traits>::CMyStream(basic_ostream<charT, Traits>& ostr,
    const char *fmt = "log")
    : std::eek:stream(ostr.rdbuf())
    {
    m_fmt = new charT[strlen(fmt)];
    use_facet<ctype<charT> >(ostr.getloc()).widen(fmt, fmt+strlen(fmt),
    m_fmt);
    }

    template <typename charT, typename Traits>
    CMyStream<charT, Traits>::~CMyStream()
    {
    delete[] m_fmt;
    }

    template <typename charT, typename Traits>
    basic_ostream<charT, Traits>&
    CMyStream<charT, Traits>::format(const char *fmt)
    {
    delete[] m_fmt;
    m_fmt = new charT[strlen(fmt)];
    use_facet<ctype<charT> >(this->getloc()).widen(fmt, fmt+strlen(fmt),
    m_fmt); //3
    return *this;
    }

    template <typename charT, typename Traits>
    charT *
    CMyStream<charT, Traits>::format() const
    {
    charT *p = new charT[Traits::length(m_fmt)];
    Traits::copy(p, m_fmt, Traits::length(m_fmt));
    return p;
    }

    template <typename T, typename charT, typename Traits>
    CMyStream<charT, Traits>& operator<<(CMyStream<charT, Traits>& ostr, //2
    T val)
    {
    (basic_ostream<charT, Traits>&)ostr << ostr.m_fmt << " ";
    (basic_ostream<charT, Traits>&)ostr << val;
    return ostr;
    }

    template <class Ostream, class Arg>
    osmanip<Ostream, Arg>::eek:smanip(Ostream& (*pf)(Ostream&, Arg), Arg arg)
    : pf_(pf) , arg_(arg)
    {
    ;
    }

    template <class Ostream, class Arg>
    Ostream&
    osmanip<Ostream, Arg>::manipulate(Ostream& ostr) const
    {
    return (*pf_)(ostr,arg_);
    }

    //4
    template <class Ostream, class Arg>
    Ostream& operator<< (Ostream& ostr, const osmanip<Ostream, Arg>& manip)
    {
    // (*manip.pf_)(ostr,manip.arg_);
    // return ostr;
    return manip.manipulate(ostr);
    }

    template <class charT, class Traits>
    inline basic_ostream<charT,Traits>&
    sfmt(basic_ostream<charT,Traits>& ostr, const char* f)
    {
    CMyStream<charT,Traits>* p;
    try {
    p = dynamic_cast<CMyStream<charT,Traits>*>(&ostr);
    }
    catch (std::bad_cast) {
    return ostr;
    }

    p->format(f);
    return ostr;
    }

    template <class charT,class Traits>
    inline osmanip<basic_ostream<charT, Traits>,const char*>
    setfmt(const char* fmt)
    {
    return osmanip<basic_ostream<charT,Traits>,const char*>(sfmt,fmt);
    }

    #endif /* __MY_STREAM_CPP */

    // mystream_test.cpp
    //#include "mystream.h"

    int
    main(int argc, char *argv[])
    {
    CMyStream<char> strm(std::cout);

    strm << "Hello World!" << std::endl;
    strm << "123 " << 123 << std::endl;

    strm << setfmt<char, std::char_traits<char> >("ERROR") << "Byeee" <<
    std::endl;

    return 0;
    }
    Bernd Strieder, Jun 17, 2008
    #4
  5. Dan Smithers

    James Kanze Guest

    On Jun 17, 3:22 pm, Dan Smithers <> wrote:
    > wrote:


    [...]
    > > Also, you may want to replace this
    > > template <typename charT, typename Traits=std::char_traits<charT> >


    > > with


    > > template <typename charT, class Traits=std::char_traits<charT> >


    > I thought that class and typename were synonymous in this context.


    They are. A lot of experts (but not all) prefer using typename
    systematically in this case, to avoid confusion.

    > > I didn't get why would want to write another stream for char
    > > when its already available.


    > The reason why I'm doing this is that I have a log class that
    > opens a file and sends a string together with time information
    > to it.


    That's easily done using a forwarding streambuf, see
    http://kanze.james.neuf.fr/articles/fltrsbf1.html. More
    generally, it is often desirable to make logging configurable,
    etc., which often leads to a wrapper (not derivation) around
    ostream; there's a generic implementation of this in the code at
    my site (along with full implementations of output and input
    filtering streambuf): go to the code section, then look up
    OutputStreamWrapper in the IO subsection.

    > My initial implementation used a method log(const
    > string& msg) to do this, but that means that I have to
    > pre-format the string.


    > ostringstream oss;
    > oss << "log message" << log_val ...;
    > my_log(oss.str());


    > If I make my log class inherit from ostream then I can simply write


    > my_log << "log_message" << log_val ...;


    You can do that with a wrapper as well. And if all you need is
    the time stamp, a filtering streambuf means that you're using an
    istream; you don't need to inherit (or if you do, it's only to
    provide convenience constructors).

    > I thought that while I was doing this I would set up a
    > manipulator so that I can writethings like
    > my_log << setformat(Error) << "error message" << err_code;


    > my_log << setformat(Warning) << "warning message << warning_data;


    This is most often handled by something like:

    log( Log::error ) << "message" ... ;
    log( Log::warning ) << "message" ... ;

    The function "log" determines whether logging at this level is
    active or not, and returns a corresponding OutputStreamWrapper.

    Note that the destructor of the OutputStreamWrapper here can be
    used to force a flush (and ensure that every log message ends
    with a new line, if it collaborates with the filtering
    streambuf), and the class itself can also grab a lock in its
    constructor, and release it in the destructor, to ensure
    synchronization in a multi-threaded environment.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 18, 2008
    #5
  6. Dan Smithers

    Dan Smithers Guest

    Thank you very much Bernd,

    I thought that I had tried using this->getloc() during my trials. I
    guess that either I had dismissed it as being implicit, or tried it at
    the same time as some other "fix" and taken them all out when it didn't
    all work.

    I see that I had made m_fmt public. If I wanted it private, then
    operator<< would need to be a friend, or use an access method. Which is
    better?

    Can you suggest where I should look for good log libraries?

    thanks again

    dan
    Dan Smithers, Jun 18, 2008
    #6
  7. Hello,

    Dan Smithers wrote:

    > Thank you very much Bernd,


    You're welcome.

    >
    > I thought that I had tried using this->getloc() during my trials. I
    > guess that either I had dismissed it as being implicit, or tried it at
    > the same time as some other "fix" and taken them all out when it
    > didn't all work.


    It is pretty hard to learn that template stuff just by trying and
    without working through some textbook. Even worse, you might be faced
    with code from different times, when even the textbooks told different
    things, or when textbooks on some matter did not exist.

    I think what you have tried about iostreams has been tried often enough
    and you can find a lot to read about it, but sometimes that stuff does
    not hold anymore to the word.

    >
    > I see that I had made m_fmt public. If I wanted it private, then
    > operator<< would need to be a friend, or use an access method. Which
    > is better?


    IMO it is better to provide some public methods doing just the work you
    need on those data members. Then you can avoid most of that friend
    business. Those extra public methods could do fine-grained checks, so
    it could be public without posing threats.

    >
    > Can you suggest where I should look for good log libraries?


    Use keywords "C++ logging library" at a search engine, there are loads
    of such libraries. What is good depends on your actual requirements.
    Maybe you can find some projects similar to yours and see how they do.

    With the danger to become OT here, some thoughts on logging:

    If logging is an inherent part of your project, then why not designing
    it in an application specific manner, i.e. you design a logging
    interface not as generic as the iostreams library, but to your needs.
    If you have something to log, then the actual formatting of the message
    is arbitrary at that place, while using that operator<< interface to
    iostreams concerns you with the actual formatting all over. The best
    you can get is a method taking the context info through parameters
    creating the log message and routing it to the right place.

    And sometimes logging has to be done different depending on the platform
    and the language. If you do logging through an application specific
    interface, then porting and translating the logging requires work only
    behind that interface.

    Bernd
    Bernd Strieder, Jun 18, 2008
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Mahesh Devjibhai Dhola

    Problem in deriving custome class from XmlNode

    Mahesh Devjibhai Dhola, Oct 15, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    492
    Mahesh Devjibhai Dhola
    Oct 15, 2004
  2. Gary Rynearson

    Problem deriving from WebControl Class

    Gary Rynearson, Nov 18, 2005, in forum: ASP .Net
    Replies:
    0
    Views:
    423
    Gary Rynearson
    Nov 18, 2005
  3. Matthias Kaeppler
    Replies:
    1
    Views:
    435
    R.F. Pels
    May 22, 2005
  4. Replies:
    4
    Views:
    431
  5. markww
    Replies:
    1
    Views:
    295
    Ivan Vecerina
    Aug 22, 2006
Loading...

Share This Page