Reputation: 1495
I have a log class used throught my cli
application. It is based on this post: http://www.drdobbs.com/cpp/logging-in-c/201804215?pgno=1
The basic parts are
Output2STDOUT
is this class.log.hpp looks like
#define STDOUT_LOG(level,text) \
{ \
if (level > STDOUTLog::ReportingLevel() || !Output2STDOUT::Stream()) ; \
else STDOUTLog().Get(level) << text ; \
}
class Output2STDOUT
{
public:
static FILE*& Stream();
static void Output(const std::string& msg);
};
class Log
{
public:
Log();
virtual ~Log(){}
std::ostringstream& Get(TLogLevel level = logINFO);
public:
static TLogLevel& ReportingLevel();
static std::string ToString(TLogLevel level);
static TLogLevel FromString(const std::string& level);
protected:
std::ostringstream os;
private:
Log(const Log&);
Log& operator =(const Log&);
};
template <typename T>
class Log2 : public Log
{
public:
~Log2()
{
os << std::endl;
T::Output(os.str());
}
};
class STDOUTLog : public Log2<Output2STDOUT> {};
template class Log2<Output2STDOUT>;
and log.cpp looks like
#include <sys/time.h>
#include "log.hpp"
inline std::string NowTime()
{
char buffer[11];
time_t t;
time(&t);
//tm r = {0};
tm r = {0,0,0,0,0,0,0,0,0,0,0};
strftime(buffer, sizeof(buffer), "%X", localtime_r(&t, &r));
struct timeval tv;
gettimeofday(&tv, 0);
char result[100] = {0};
sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000);
return result;
}
Log::Log()
{
os.setf(std::ios::fixed);
os.precision(8);
}
std::ostringstream& Log::Get(TLogLevel level)
{
os << "- " << NowTime();
os << " " << ToString(level) << ": ";
return os;
}
TLogLevel& Log::ReportingLevel()
{
static TLogLevel reportingLevel = logDEBUG4;
return reportingLevel;
}
std::string Log::ToString(TLogLevel level)
{
static const char * const buffer[] = {"ERROR", "WARNING", "INFO", "DEBUG", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4"};
return buffer[level];
}
TLogLevel Log::FromString(const std::string& level)
{
if (level == "DEBUG4")
return logDEBUG4;
if (level == "DEBUG3")
return logDEBUG3;
if (level == "DEBUG2")
return logDEBUG2;
if (level == "DEBUG1")
return logDEBUG1;
if (level == "DEBUG")
return logDEBUG;
if (level == "INFO")
return logINFO;
if (level == "WARNING")
return logWARNING;
if (level == "ERROR")
return logERROR;
//Log<T>().Get(logWARNING) << "Unknown logging level '" << level << "'. Using INFO level as default.";
return logINFO;
}
//----------------------------------------------------------------------
inline FILE*& Output2STDOUT::Stream()
{
static FILE* pStream = stdout;
return pStream;
}
inline void Output2STDOUT::Output(const std::string& msg)
{
FILE* pStream = Stream();
if (!pStream)
return;
fprintf(pStream, "%s", msg.c_str());
fflush(pStream);
}
and in any other file I #include "log.hpp"
and use it as
STDOUT_LOG(logWARNING, "this is a log message of level WARNING.");
The code above compiles fine in debug mode, but not in release. I use a custom makefile and the only difference is that I delete the -g
and add a -O2
at the options.
The linking command looks like:
g++ -O2 -Wall -Wextra -pedantic -std=c++11 -DOSC_COM -DENOSE_ON_SOCKET -I../oscpack obj_rel/indicators.opp obj_rel/threadedinput.opp obj_rel/signals.opp obj_rel/threadedserialport.opp obj_rel/core_enose.opp obj_rel/main_enose.opp obj_rel/serialport.opp obj_rel/cppthread.opp obj_rel/sensor.opp obj_rel/timer.opp obj_rel/log.opp obj_rel/threadedrrdupd.opp obj_rel/threaded_tcp_client.opp obj_rel/bufferedwriter.opp obj_rel/alarm.opp obj_rel/messenger.opp -o "enalu_rel" -L../oscpack -pthread -lgsl -lgslcblas -lm -lrrd_th -lconfig++ -loscpack
so the log.opp is indeed there. But I still get from all files that use the macro
indicators.cpp:(.text+0x3709): undefined reference to `Output2STDOUT::Stream()'
indicators.cpp:(.text+0x384c): undefined reference to `Output2STDOUT::Output(std::string const&)'
Could you help me identify what is wrong here? I mean :
Output2STDOUT is not a template class so the symbols are in log.opp
$ nm obj_rel/log.opp |grep Stream
0000000000000000 u _ZGVZN13Output2STDOUT6StreamEvE7pStream
0000000000000000 u _ZZN13Output2STDOUT6StreamEvE7pStream
$ nm obj_rel/log.opp |grep Output
0000000000000000 u _ZGVZN13Output2STDOUT6StreamEvE7pStream
0000000000000500 T _ZN10Output2OSC6OutputERKSs
0000000000000000 W _ZN4Log2I13Output2STDOUTED0Ev
0000000000000000 W _ZN4Log2I13Output2STDOUTED1Ev
0000000000000000 W _ZN4Log2I13Output2STDOUTED2Ev
0000000000000000 n _ZN4Log2I13Output2STDOUTED5Ev
0000000000000000 V _ZTI4Log2I13Output2STDOUTE
0000000000000000 V _ZTS4Log2I13Output2STDOUTE
0000000000000000 V _ZTV4Log2I13Output2STDOUTE
0000000000000000 u _ZZN13Output2STDOUT6StreamEvE7pStream
So I am abit lost and do not know how to proceed to fix this.
Thank you!
Upvotes: 0
Views: 1050
Reputation: 2624
You probably need to remove inline
from both definitions:
FILE*& Output2STDOUT::Stream()
{
static FILE* pStream = stdout;
return pStream;
}
void Output2STDOUT::Output(const std::string& msg)
{
FILE* pStream = Stream();
if (!pStream)
return;
fprintf(pStream, "%s", msg.c_str());
fflush(pStream);
}
What is probably happening is that when you compile the DEBUG version the compiler does not inline functions and does not remove unused functions for the generated code.
But when compiling the RELEASE version, as these inline function are in a CPP file, there are only visible when compiling that file and as they are not used from that file, the compiler remove them.
Upvotes: 3