I want to derive a stringstream so that I can use the operator<< to construct a message which will then be thrown. The API would look like:
error("some text") << " more text " << 42 << std::endl;
This should do a
throw "some text more text 42"
So what I did is make an errorbuf (inheriting from streambuf) which overloads the 'overflow' method and then create an ostream(&errorbuf). I wonder if I shouldn't instead inherit from basic_ostringstream or something...
You could probably make it easier by doing something like:
class error_builder
{
public:
error_builder(const std::string& pMsg = "")
{
mMsg << pMsg;
}
~error_builder(void)
{
throw std::runtime_error(mMsg.str());
}
template <typename T>
error_builder& operator<<(const T& pX)
{
mMsg << pX;
return *this;
}
private:
std::stringstream mMsg;
};
error_builder("some text") << " more text " << 42 << std::endl;
Note that you shouldn't throw strings like you are, hence I used std::runtime_error. All exceptions should derive from std::exception, which runtime_error does, that way all meaningful exceptions can be caught with const std::exception&.
This works because the temporary lives until the end of the full expression.
Some operators are missing from GMan's solution.
class error {
public:
explicit error(const std::string& m = "") :
msg(m, std::ios_base::out | std::ios_base::ate)
{}
~error() {
if(!std::uncaught_exception()) {
throw std::runtime_error(msg.str());
}
}
template<typename T>
error& operator<<(const T& t) {
msg << t;
return *this;
}
error& operator<<(std::ostream& (*t)(std::ostream&)) {
msg << t;
return *this;
}
error& operator<<(std::ios& (*t)(std::ios&)) {
msg << t;
return *this;
}
error& operator<<(std::ios_base& (*t)(std::ios_base&)) {
msg << t;
return *this;
}
private:
std::ostringstream msg;
};
I'll trot out my favourite macro again here:
#define ATHROW( msg ) \
{ \
std::ostringstream os; \
os << msg; \
throw ALib::Exception( os.str(), __LINE__, __FILE__ ); \
} \
In use:
ATHROW( "Invalid value: " << x << " should be " << 42 );
the exception type is from my own library, but I think you get the idea. This is much simpler than deriving your own stream class, and avoids lots of nasty complications with op<<().
I usually just create my own exception classes. You only have to override what() and can provide as many constructors as you like. To build up the error message, just use vasprintf (if available) or std::ostringstream like above.
Here's an example:
class CustomException : public std::exception {
private:
const std::string message;
public:
CustomException(const std::string &format, ...) {
va_list args;
va_start(args, format);
char *formatted = 0;
int len = vasprintf(&formatted, format.c_str(), args);
if (len != -1) {
message = std::string(formatted);
free(formatted);
} else {
message = format;
}
va_end(args);
}
const char *what() const {
return message.c_str();
}
};
If you don't have vasprintf, you can also use vsnprintf with a buffer on the stack...
Related
I've seen examples that allow you to create a manipulator that inserts delimiters but none of those manipulators are sticky. That is, the manipulator returns a special class that inserts the delimiter, rather than modifying the output stream permanently so that it can do it on its own.
I want to be able to do this:
std::cout << sep(", ");
std::cout << "hello" << "world";
// "hello, world"
At the moment this prints "h, e, l, l, o, w, o, r, l, d" when I need it to be "hello, world". The reason I'm getting the wrong output is because I put the printing in the overflow() method and overflow() is being called for each character. I'm not sure where is the appropriate place to put it.
Sorry about it being verbose. If I knew a simpler way to write it I would. Just start from the bottom and work your way up:
#include <iostream>
#include <cstring>
// index for delimiter
int separator() {
static int idx = std::ios_base::xalloc();
return idx;
}
// index for storage of dynamically-allocated buffer
int rdbuffer() {
static int idx = std::ios_base::xalloc();
return idx;
}
struct custom_separator : std::streambuf {
public:
custom_separator(std::ostream& _stream)
: stream(_stream), buffer(_stream.rdbuf()) {}
int_type overflow(int_type c) {
// has a token been read already
if (token_read) {
char* delim = static_cast<char*>(stream.pword(separator()));
// print delim
buffer->sputn(delim, strlen(delim));
}
token_read = true;
return buffer->sputc(c);
}
private:
std::ostream& stream;
std::streambuf* buffer;
bool token_read = false;
};
// deletes the buffer and the delimiter
void cleanup(std::ios_base::event evt, std::ios_base& str, int idx) {
if (evt == std::ios_base::erase_event) {
delete static_cast<const char*>(str.pword(idx));
delete static_cast<custom_separator*>(str.pword(rdbuffer()));
}
}
std::ostream& set_separator(std::ostream& os, const char* str) {
if (!os.bad()) {
// If a separator string doesn't exist, assign os a buffer that prints one
if (!os.pword(separator())) {
auto buf = new custom_separator(os);
os.rdbuf(buf);
// this is to keep track of buf so we can delete it later
os.pword(rdbuffer()) = static_cast<void*>(buf);
os.register_callback(cleanup, separator());
}
// delete the previous separator
delete static_cast<const char*>(os.pword(separator()));
// store the new one
os.pword(separator()) = (void*)(str);
}
return os;
}
struct sep {
explicit sep(const char* _sep)
: separator(new char[strlen(_sep) + 1]) {
strcpy(separator, _sep);
}
sep(const sep&) = delete;
sep(const sep&&) = delete;
char* separator;
};
std::ostream& operator<<(std::ostream& os, const sep& manip) {
set_separator(os, manip.separator);
return os;
}
int main() {
std::cout << sep(", ");
std::cout << "hello";
std::cout << "world";
// "h, e, l, l, o, w, o, r, l, d"
}
The main issue with overflow() is that I don't know when to detect when the end of a token like "hello" has been read to know when to insert.
Try the following. Additionally, new line break processing (new line symbol) has been added so that the separator is not added during the transfer (after new line).
#include <iostream>
class MyStream
{
public:
struct Sep
{
Sep (const std::string& sep_value = ""):
m_sep(sep_value)
{
}
operator std::string()
{
return m_sep;
}
private:
std::string m_sep;
};
MyStream& operator << (const Sep& sep)
{
m_sep = sep;
m_add_sep = false;
return *this;
}
MyStream& operator << (const std::string& str)
{
if(str.find(MyStream::endl) != std::string::npos)
m_add_sep = false;
operator<< <std::string>(str);
m_add_sep = false;
return *this;
}
template <typename T>
MyStream& operator << (const T& value)
{
if(m_add_sep)
std::cout << static_cast<std::string>(m_sep);
std::cout << value;
m_add_sep = true;
return *this;
}
static const std::string endl;
private:
Sep m_sep;
bool m_add_sep = false;
};
const std::string MyStream::endl = std::string("\n");
int main()
{
MyStream stream;
stream << "hello" << "world" << MyStream::endl; // prints "helloworld"
stream << MyStream::Sep(", ");
stream << "hello" << "world" << MyStream::endl; // prints "hello, world"
stream << 1 << 2;
stream << 3 << MyStream::endl; // both lines prints "1, 2, 3"
stream << MyStream::Sep();
stream << 1 << 2 << 3 << MyStream::endl; // prints "123"
return 0;
}
Here's a possible solution. The separator is the same for all streams.
namespace alt_std
{
struct sep
{
sep(const char* s) { s_ = s; }
friend std::ostream& operator<<(std::ostream& os, const sep& sm)
{
return os;
}
inline static std::string s_{};
};
std::ostream& operator<<(std::ostream& os, const char* s)
{
return std::operator<<(os, s), std::operator<<(os, sep::s_);
}
}
int main()
{
using namespace alt_std;
std::cout << sep(" ") << "hello";
std::cout << "world" << std::endl; // "hello world\n"
std::cout << sep("") << "hel" << "lo"; // "hello"
}
If you want to do it at the stream object level, it's more difficult because stream objects don't have a custom storage space where you can store the value of the separator.
Or you could simply wrap the stream object:
template< typename OS >
struct wrapper
{
wrapper(OS& os, const char* sep) : os_{ os }, sep_{ sep } {}
template< typename T>
friend wrapper& operator<<(wrapper& os, const T& v)
{
os.os_ << v << os.sep_;
return os;
}
OS& os_;
std::string sep_;
};
int main()
{
wrapper os{ std::cout, " " };
os << "hello" << "world";
}
I have a little problem with exception handling in c++. I need to convert a number to string and if this string contains letters or more than 10 characters I need to give error. Here is the exception-handling part;
while ( cin >> input )
{
try
{
convert = castInput( input );
cout << "The number entered was: " << convert;
}
catch ( NonNumber &excp )
{
cout << "INVALID INPUT: " << excp.what();
}
catch ( Overflow &excp )
{
cout << "INVALID INPUT: " << excp.what();
}
cout << "\n\nPlease enter a number (end-of-file to terminate): ";
}
I used stoi function to converting the string to int but I think I need open 2 classes. I don't know why and how becase stoi fucntion own what function already.
I personally find that a regex is no overkill for this. Checking user input with a regex will have no negative impact on performance whatsoever.
But I think that you are more interested in exception handling. You need to read many many pages of a good C++ book to understand exception handling and its use cases.
And maybe, but I do not know, you want simply to catch the standard exceptions thrown by std::stoi. These are:
std::invalid_argument
std::out_of_range
That would be easy with writing
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
std::cout << e.what() << "\n";
}
catch (const std::out_of_range & e) {
std::cout << e.what() << "\n";
}
But maybe you want to learn, how to write own exceptions. For your specific requirements. But please note that your specification, e.g. "more than 10 digits" and "no letter" are maybe not quite what you want. With a machine, where int is 32bit bit, the maximum number that can be converted is: 2147483647. Any number bigger and still having only 10 characters, will throw a std::out_of_range. On the other hand numbers like 123X would be converted by std::stoi to 123. So, maybe your requirements are not so clear.
Anyway, to show you, how you could use own exceptions, I create 2 customized exceptions classes. And to make life easy, I derived those from std::exception (recommended).
See the below example:
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
class StoiException : public std::exception
{
public:
StoiException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }
protected:
void setMessage(const std::string& msg) { message = msg; }
protected:
std::string message{};
};
class NoNumber : StoiException
{
public:
NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
int castInput(std::string& input) {
int result{};
// Check, if there is any letter in the input string
if (std::any_of(input.begin(), input.end(), isalpha)) {
// If so, than throw
throw NoNumber(input);
}
// Check, if string has more than 10 characters
if (input.length() > 10) {
// If so, than throw
throw Overflow(input);
}
result = std::stoi(input);
return result;
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
try {
// Convert
int convert = castInput(input);
// This will only be shown , if there is no exception
std::cout << "\nConverted Number is: " << convert << "\n";
}
// Catch all exceptions
catch (const NoNumber & e) {
std::cout << e.what() << "\n";
}
catch (const Overflow & e) {
std::cout << e.what() << "\n";
}
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
std::cout << e.what() << "\n";
}
catch (const std::out_of_range & e) {
std::cout << e.what() << "\n";
}
}
return 0;
}
Of course you can handle the std::stoi's exception also in your custom converter function. Then only your owwn exceptions are visible.
Please see:
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
class StoiException : public std::exception
{
public:
StoiException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }
protected:
void setMessage(const std::string& msg) { message = msg; }
protected:
std::string message{};
};
class NoNumber : StoiException
{
public:
NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
int castInput(std::string& input) {
int result{};
// Check, if there is any letter in the input string
if (std::any_of(input.begin(), input.end(), isalpha)) {
// If so, than throw
throw NoNumber(input);
}
// Check, if string has more than 10 characters
if (input.length() > 10) {
// If so, than throw
throw Overflow(input);
}
try {
result = std::stoi(input);
}
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
throw NoNumber(input);
}
catch (const std::out_of_range & e) {
throw Overflow(input);
}
return result;
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
try {
// Convert
int convert = castInput(input);
// This will only be shown , if there is no exception
std::cout << "\nConverted Number is: " << convert << "\n";
}
// Catch all exceptions
catch (const NoNumber & e) {
std::cout << e.what() << "\n";
}
catch (const Overflow & e) {
std::cout << e.what() << "\n";
}
}
return 0;
}
But maybe, what you really want to have, is a function, that encapsulates std::stoi and has an additional return value to show, if it worked or not.
Please note: The last solution will also convert "123X" to 123. That is the difference to the previous versions.
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
std::pair<bool, int> castInput(std::string& input) {
bool ok{ false };
int result{};
try {
result = std::stoi(input);
ok = true;
}
// Catch stoi errors
catch (const std::invalid_argument & e) {}
catch (const std::out_of_range & e) {}
return {ok, result};
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
const auto [ok, convert] = castInput(input);
if (ok)
std::cout << "Converted value: " << convert << "\n";
else
std::cout << "String '" << input << "' could not be converted\n";
}
return 0;
}
Why not use Regex ? You just create a regex that check if there is letters or more than 10 chars, depending on the return you process the conversion or throw an custom exception.
This may be the regex you are looking for: ^[0-9]{1,10}$
std::regex reg("^[0-9]{1,10}$");
if (std::regex_match(string, reg))
// Do what you want (process convert)
Here is the regex cpp reference : here
Edit: As commented, regex is overkill for this, so you could simply make some basic checks inside you castInput function and throw NonNumber if you find a character in the string, or throw Overflow if string.len > 10.
I have a macro that does exactly what I want it to do:
#define LOG(x)\
do { if (!cpp::app::g_app) {\
ASSERT("CANNOT LOG IF THERE IS NO CPP APP OBJECT" == 0);\
}\
else \
{ \
std::stringstream s; s << cpp::timing::currentDateTime(); s << '\t'; s << x << std::endl; \
*cpp::app::g_app << s.str(); \
cpp::app::g_app->flush(true);\
} \
} while(0)
#endif
Its really nice, because I can:
LOG("On first log line " << 0 << "Still on first log line")
..and a newline is inserted once the LOG macro is done.
Output looks like:
<date / time> On First log line 0 Still on first log line
... subsequent lines here
My question is how to overload << operator on my logging class to do the same?
If I simply overload << operator and return *this (where my logging case can be converted to ostream) then if I do:
mylogger << "First line " << "Still on first line";
then the output is something like:
<date and time> First line
<date and time> Still on first line.
So, I want to emulate the macro behaviour with the << operator. I want a newline automatically when the whole chain of << operations is complete, just like in the macro. But, since macros are evil, I'd rather convert it to a proper function.
Is this achievable?
Edit: Matt's idea about a helper class is quite nice. Based on his suggestion, I made the following disposable helper class:
class log
{
public:
log() :
m_stream(cpp::app::g_app->stream()){
}
template <typename T>
log& operator << (const T& t)
{
m_ss << t;
return *this;
}
virtual ~log(){
m_stream << cpp::timing::currentDateTime() << "\t" << m_ss.str() << "\r\n";
m_stream.flush();
}
private:
std::ostream& m_stream;
std::stringstream m_ss;
};
Use it like:
log() << "All this text" << " will be on one line in the logfile, with date and time prepended ";
log() << "And this lot falls on line 2, with date and time prepended";
I hope that helps Galik and others who may be wanting the same thing.
A small improvement to the solution offered in the question, that creates fewer temporary objects:
class log
{
public:
log() :
m_stream(cpp::app::g_app->stream()){
}
template <typename T>
std::ostream& operator<< (const T& t)
{
return m_stream << cpp::timing::currentDateTime() << "\t" << t;
}
virtual ~log(){
m_stream << "\r\n";
m_stream.flush();
}
private:
std::ostream& m_stream;
};
The temporary instance of log() will be destroyed at the end of the full-expression even if it isn't returned from operator<<. Might as well get rid of the middle-man and the extra stringstream buffer (which btw, doesn't respect the existing formatting options on the main stream -- this could be good or bad)
I have some code that you may find useful.
Its not heavily tested. I am hoping to expand on this to supply a producer/consumer queue which is why I am not using it at the moment. I am still using a macro like you :)
#include <sstream>
#include <memory>
#include <fstream>
#include <iostream>
#include <ctime>
namespace log {
typedef std::stringstream sss; // <3 brevity
class writer
{
private:
std::ostream& sink;
std::string endl = "\n";
std::string get_stamp()
{
time_t rawtime = std::time(0);
tm* timeinfo = std::localtime(&rawtime);
char buffer[32];
std::strftime(buffer, 32, "%Y-%m-%d %H:%M:%S", timeinfo);
return std::string(buffer);
}
public:
writer(std::ostream& sink): sink(sink) {}
void add_line(sss* ss)
{
sink << get_stamp() << " " << ss->rdbuf() << endl;
}
};
// this is used to build the log string in an sts::ostringstream
// which gets std::moved to each new temporary buffer when <<
// is invoked
struct buffer
{
writer& lw;
sss* ss;
buffer(writer& lw): lw(lw), ss(new sss) {}
buffer(const buffer& buf) = delete;
buffer(buffer&& buf): lw(buf.lw), ss(buf.ss) { buf.ss = nullptr; }
~buffer() { if(ss) lw.add_line(ss); delete ss; }
};
// each << creates a new temporary buffer that std::moves
// the std::ostringstream on to the next
template<typename Type>
buffer operator<<(buffer&& buf, const Type& t)
{
(*buf.ss) << t;
return std::move(buf);
}
// A write to a writer creates a temporary buffer and passes
// the << on to that
template<typename Type>
buffer operator<<(writer& lw, const Type& t)
{
return std::move(buffer(lw) << t);
}
} // log
int main()
{
std::ofstream ofs("output.log");
log::writer fout(ofs); // write to file
log::writer lout(std::cout); // write to std output
lout << "A " << 0.7 << " B";
fout << "wibble: " << 2;
}
The way this works is that writing to a log::writer via << causes a temporary log::buffer to be created. Subsequent writes << to the log::buffer create new temporary log::buffer objects which std::move an internal std::ostringstream between them. Because only the final log::buffer object has a non-null std::ostringstream* (because it was std::moved down when it collapsed) it writes the entire line to the log::writer.
My rather simple take on the situation:
class logger{
private:
unsigned int support_count;
std::ostream& output_stream;
public:
logger(std::ostream& str)
:support_count(0),
output_stream(str)
{}
class support_buffer{
private:
logger& l;
support_buffer(logger& l)
:l(l)
{
l.support_count++;
}
public:
support_buffer(const support_buffer& buf)
:l(buf.l)
{
l.support_count++;
}
~support_buffer(){
l.support_count--;
if (l.support_count==0){
l.output_stream << std::endl;
}
}
template <typename T>
support_buffer& operator<<(const T& t){
l.output_stream << t;
return *this;
}
friend class logger;
};
template <typename T>
support_buffer operator<<(const T& t){
output_stream << "<date/time> " << t;
return support_buffer(*this);
}
friend class support_buffer;
};
int main()
{
logger l(std::cout);
l << "Line 1: " << 0 << "Still on line 1";
l << "Line 2";
return 0;
}
Just create a wrapper class that passes all printed elements to our logger and on destruction of the last one send a new line. If you are sure that your output buffer is only being used by your logger class you could even resign of counting support objects. Just print new line character before a line instead of after it.
I'm adding functions to my (simple) log class to make it usable like a stream.
Currently, after some modifications, I got this (in my cpp):
// blah blah blah...
// note: here String is a defined as: typedef std::string String;
void Log::logMessage( const String& message )
{
logText(); // to be sure we flush the current text if any (when "composing" a message)
addText( message );
logText(); // really log the message and make the current text empty
}
// blah blah blah...
Log& operator<<( Log& log, const std::stringstream& message )
{
log.logMessage( message.str() );
return log;
}
Log& operator<<( Log& log, const String& message )
{
log.addText( message );
return log;
}
Now in my "client" app I'm using this code to check the result (m_log is a valid pointer as you have already guessed):
gcore::Log& log = *m_log;
log << getName() << " : application created.";
log << "This is a test for " << getName();
Now the problem I got is that logText() (and logMessage) is never called because this test code will only call the << operator with String.
What I need is a way to call logText() when the given steam of string is finished :
log << getName() << " : application created.";
would be equivalent to
log.addText( getName() );
log.addText( " : application create." );
log.logText();
I'm not sure how to do this or even if it's possible. My first guess is that it would be possible to use std::endl at the end of the stream like this :
log << getName() << " : application created." << std::endl;
Or something equivalent, but if it's possible to do it without adding objects to the stream, that would be nice.
Any idea?
You can create a temp object and use his destructor to catch the end of the statement:
following code should give you the basic idea
class Log
{
public:
class Sublog
{
public:
Sublog(const std::string& message)
{
std::cout << message;
}
void addText(const std::string& message)
{
std::cout << message;
}
~Sublog()
{
std::cout << std::endl;
}
Sublog& operator<<(const std::string& message )
{
this->addText(message);
return *this;
}
};
};
Log::Sublog operator<<( Log& log, const std::string& message )
{
return Log::Sublog(message);
}
which would be used like this
int main()
{
Log log;
log << "Foo" << "bar";
log << "baz" << "plop";
}
after each semicolon, the destructor of Sublog is called
Klaim: the (working and effective) implementation of this solution in my case :
in the Log header :
/** To allow streaming semantic on logs (used in << operator) .
*/
class LogStreamer
{
public:
LogStreamer( Log& log, const String& text )
: m_log( log )
{
m_log.addText( text );
}
~LogStreamer()
{
m_log.logText();
}
LogStreamer& operator<<( const String& text )
{
m_log.addText( text );
return *this;
}
private:
Log& m_log;
};
GCORE_API LogStreamer operator<<( Log& log, const String& message );
and in the cpp file:
LogStreamer operator<<( Log& log, const String& message )
{
return LogStreamer( log, message );
}
How can I derive a class from cout so that, for example, writing to it
new_cout << "message";
would be equivalent to
cout << __FUNCTION__ << "message" << "end of message" << endl;
class Log
{
public:
Log(const std::string &funcName)
{
std::cout << funcName << ": ";
}
template <class T>
Log &operator<<(const T &v)
{
std::cout << v;
return *this;
}
~Log()
{
std::cout << " [end of message]" << std::endl;
}
};
#define MAGIC_LOG Log(__FUNCTION__)
Hence:
MAGIC_LOG << "here's a message";
MAGIC_LOG << "here's one with a number: " << 5;
#define debug_print(message) (std::cout << __FUNCTION__ << (message) << std::endl)
This has the advantage that you can disable all debug messages at once when you're done
#define debug_print(message) ()
Further from Mykola's response, I have the following implementation in my code.
The usage is
LOG_DEBUG("print 3 " << 3);
prints
DEBUG (f.cpp, 101): print 3 3
You can modify it to use FUNCTION along/in place of LINE and FILE
/// Implements a simple logging facility.
class Logger
{
std::ostringstream os_;
static Logger* instance_;
Logger();
public:
static Logger* getLogger();
bool isDebugEnabled() const;
void log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const;
std::ostringstream& getStream()
{ return os_; }
};
void Logger::log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const
{
std::cout << logLevelEnumToString(l) << "\t(" << fileName << ": " << lineno << ")\t- " << os.str();
os.str("");
}
#define LOG_common(level, cptext) do {\
utility::Logger::getLogger()->getStream() << cptext; \
utility::Logger::getLogger()->log(utility::level, utility::Logger::getLogger()->getStream(), __FILE__, __LINE__); \
} while(0);
enum LogLevelEnum {
DEBUG_LOG_LEVEL,
INFO_LOG_LEVEL,
WARN_LOG_LEVEL,
ERROR_LOG_LEVEL,
NOTICE_LOG_LEVEL,
FATAL_LOG_LEVEL
};
#define LOG_DEBUG(cptext) LOG_common(DEBUG_LOG_LEVEL, cptext)
#define LOG_INFO(cptext) LOG_common(INFO_LOG_LEVEL , cptext)
#define LOG_WARN(cptext) LOG_common(WARN_LOG_LEVEL , cptext)
#define LOG_ERROR(cptext) LOG_common(ERROR_LOG_LEVEL, cptext)
#define LOG_NOTICE(cptext) LOG_common(NOTICE_LOG_LEVEL, cptext)
#define LOG_FATAL(cptext) LOG_common(FATAL_LOG_LEVEL, cptext)
const char* logLevelEnumToString(LogLevelEnum m)
{
switch(m)
{
case DEBUG_LOG_LEVEL:
return "DEBUG";
case INFO_LOG_LEVEL:
return "INFO";
case WARN_LOG_LEVEL:
return "WARN";
case NOTICE_LOG_LEVEL:
return "NOTICE";
case ERROR_LOG_LEVEL:
return "ERROR";
case FATAL_LOG_LEVEL:
return "FATAL";
default:
CP_MSG_ASSERT(false, CP_TEXT("invalid value of LogLevelEnum"));
return 0;
}
}
For logging purposes I use something like
#define LOG(x) \
cout << __FUNCTION__ << x << endl
// ...
LOG("My message with number " << number << " and some more");
The problem with your approach is (as Mykola Golybyew explained) that FUNCTION is processed at compile time and would therefore always print the same name with a non-preprocessor solution.
If it's only for adding endl to your messages, you could try something like:
class MyLine {
public:
bool written;
std::ostream& stream;
MyLine(const MyLine& _line) : stream(_line.stream), written(false) { }
MyLine(std::ostream& _stream) : stream(_stream), written(false) { }
~MyLine() { if (!written) stream << "End of Message" << std::endl; }
};
template <class T> MyLine operator<<(MyLine& line, const T& _val) {
line.stream << _val;
line.written = true;
return line;
}
class MyStream {
public:
std::ostream& parentStream;
MyStream(std::ostream& _parentStream) : parentStream(_parentStream) { }
MyLine getLine() { return MyLine(parentStream); }
};
template <class T> MyLine operator<<(MyStream& stream, const T& _val) {
return (stream.getLine() << _val);
}
int main()
{
MyStream stream(std::cout);
stream << "Hello " << 13 << " some more data";
stream << "This is in the next line " << " 1 ";
return 0;
}
Note, that it's important not to return references from the operator functions. Since the MyLine should only exist as a temporary (for its destructor triggers the writing of the endl), the first object (returned by the getLine() function in MyStream) would be destructed before the second operator<< is called. Therefore the MyLine object is copied in each operator<< creating a new one. The last object gets destructed without being written to and writed the end of the message in its destructor.
Just try it out in the debugger to understand whats going on...
You have to override operator<<(), but you even don't have to subclass std::cout. You may also create a new object or use existing objects like that.
You could also override the operator. It will allow you to call another function or prefix/suffix anything that's going to leave the output buffer with whatever you wish: In your case, you'd have it output a specific string.