I would like to learn C++ better (currently my C++ is limited to it's C subset *cough* ...), thus I decided to try to "C++ -ify" a useful logging function of mine from C to C++, which (I think) is best explained in code:
#include <stdarg.h>
#include <stdio.h>
enum msg_type {
LOG_DBG,
LOG_INF,
LOG_WRN,
LOG_ERR
};
int printf_log(enum msg_type mt, const char *fmt_string, ...)
{
va_list args;
va_start(args, fmt_string);
switch (mt) {
case LOG_DBG:
printf("[D] ");
break;
case LOG_INF:
printf("[I] ");
break;
case LOG_WRN:
printf("[W] ");
break;
case LOG_ERR:
printf("[E] ");
break;
default:
break;
}
int res = vprintf(fmt_string, args);
va_end(args);
return res;
}
int main()
{
int i = 0;
printf_log(LOG_DBG, "i is %d\n", i);
i++;
printf_log(LOG_INF, "i is %d\n", i);
i++;
printf_log(LOG_WRN, "i is %d\n", i);
}
This should output:
[D] i is 0
[I] i is 1
[W] i is 2
My idea of a C++ version of this is to have something like:
#include <log.h>
int main()
{
int i = 0;
log::log(log::dbg)<<"i is " << i << "\n";
i++;
log::log(log::inf)<< "i is " << i << "\n";
i++;
log::log(log::wrn)<< "i is " << i << "\n";
}
with the same result.
I especially would like to avoid any parsing of the arguments (possibly except log::inf
or similar) but just let them pass right onto cout
.
But I've not really an idea where to start and all things regarding this either expect more knowledge of C++ or want you to implement you whole own streambuf
or similar.
My idea is basically the same so I tried my best and this is the code that I've produced using different resources on the Internet:
#include <iostream>
using std::cout;
class Log {
public:
enum msg_type {
dbg =1,
inf,
wrn,
err
};
Log(enum msg_type mt)
{
switch (mt) {
case dbg:
cout << "[D] ";
break;
case inf:
cout << "[I] ";
break;
case wrn:
cout << "[W] ";
break;
case err:
cout << "[E] ";
break;
default:
break;
}
}
template<typename T>
const Log& operator<<(const T& t)
{
std::cout << t;
return *this;
}
};
int main()
{
int i = 0;
Log(Log::dbg)<< "i is " << i++ << "\n";
Log(Log::inf)<< "i is " << i++ << "\n";
Log(Log::inf)<< "i is " << i++ << "\n";
}
Obviously it doesn't work but I've no idea on what the error messages try to tell me.
G++:
main.cc: In function ‘int main()’:
main.cc:46:34: error: passing ‘const Log’ as ‘this’ argument discards qualifiers [-fpermissive]
Log(Log::dbg)<< "i is " << i++ << "\n";
^
main.cc:35:14: note: in call to ‘const Log& Log::operator<<(const T&) [with T = int]’
const Log& operator<<(const T& t)
^
main.cc:46:40: error: passing ‘const Log’ as ‘this’ argument discards qualifiers [-fpermissive]
Log(Log::dbg)<< "i is " << i++ << "\n";
^
main.cc:35:14: note: in call to ‘const Log& Log::operator<<(const T&) [with T = char [2]]’
const Log& operator<<(const T& t)
^
Clang:
main.cc:46:30: error: invalid operands to binary expression ('const Log' and 'int')
Log(Log::dbg)<< "i is " << i++ << "\n";
~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~
main.cc:35:14: note: candidate function not viable: 'this' argument has type
'const Log', but method is not marked const
const Log& operator<<(const T& t)
^
1 error generated.
The easiest way of course would just doing a macro replacing any occurrence of my logging "fake-ostream" with eg. cout << [I] <<
but this is far less flexible.
Does anyone have any ideas on how to do this correctly and can me hint to resources which describe this thoroughly?
Any help is appreciated!
The Log
object is constant, so you can only call constant member functions.
You say that a member function is constant by adding the const
keyword after the declaration.
So your class should look like this (abbreviated):
class Log {
public:
...
template<typename T>
const Log& operator<<(const T& t) const
// ^^^^^
// Note const keyword here
{
...
}
...
};