g++ 未定义对成员函数的引用,但仅在编译发布时

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

提问人:nass 提问时间:12/21/2016 更新时间:12/21/2016 访问量:852

问:

我有一个日志类通过我的应用程序使用。它基于这篇文章: http://www.drdobbs.com/cpp/logging-in-c/201804215?pgno=1cli

基本部件是

  1. 一个处理日志级别、获取当前时间等的基日志类,它还保存一个 ostringstream,但无处可输出。我已将日志类拆分为“Log”和“Log2”,前者没有模板部分,后者在其析构函数中声明并定义了 ostringstream 的转储。
  2. 一个输出类,其中转储日志类的 ostringstream 实际上被发送到输出设备。目前是这个类。Output2STDOUT
  3. 一个方便的单行宏,可以轻松地将日志记录放在我的代码中。

log.hpp 如下所示

#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>;

log.cpp 看起来像

#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);
}

并在任何其他文件中 I 并将其用作#include "log.hpp"

STDOUT_LOG(logWARNING, "this is a log message of level WARNING.");

上面的代码在调试模式下编译良好,但在发布模式下编译不行。我使用自定义 makefile,唯一的区别是我删除了 并在选项中添加了 a。-g-O2

链接命令如下所示:

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

所以 log.opp 确实在那里。但是我仍然从所有使用宏的文件中获取

indicators.cpp:(.text+0x3709): undefined reference to `Output2STDOUT::Stream()'
indicators.cpp:(.text+0x384c): undefined reference to `Output2STDOUT::Output(std::string const&)'

你能帮我找出这里的问题吗? 我的意思是:

  • 调试构建并工作正常
  • Output2STDOUT 不是模板类,因此符号位于 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
    

所以我很迷茫,不知道如何解决这个问题。

谢谢!

C++11 未定义引用

评论


答:

3赞 Phil1970 12/21/2016 #1

您可能需要从这两个定义中删除:inline

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

可能发生的情况是,当您编译 DEBUG 版本时,编译器不会内联函数,也不会删除生成的代码中未使用的函数。

但是在编译 RELEASE 版本时,由于这些内联函数位于 CPP 文件中,因此只有在编译该文件时才可见,并且由于它们未从该文件中使用,编译器会删除它们。