Deriving my own stream class

D

Dan Smithers

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;
}
 
K

kasthurirangan.balaji

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.
 
D

Dan Smithers

Thanks Balaji,

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
 
B

Bernd Strieder

Hello,

Dan said:
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;
}
 
J

James Kanze

(e-mail address removed) wrote:

[...]
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.
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.
 
D

Dan Smithers

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
 
B

Bernd Strieder

Hello,

Dan said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top