nass
nass

Reputation: 1495

g++ undefined reference to member function, but only when compiling for release

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

  1. a base log class that handles log levels, grabs current time etc. it also holds an ostringstream, but has nowhere to output it to. I have split the log class in "Log" which has no template parts and "Log2" which declares and defines the dumping of the ostringstream in its destructor.
  2. an output class, where the dumping the ostringstream of the log class is actually sent to the output device. Currently Output2STDOUT is this class.
  3. a handy one liner macro to easily place logging in my code.

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 :

So I am abit lost and do not know how to proceed to fix this.

Thank you!

Upvotes: 0

Views: 1050

Answers (1)

Phil1970
Phil1970

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

Related Questions