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

时间:2016-12-21 00:42:30

标签: c++11 undefined-reference

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

基本部分是

  1. 一个基本日志类,它处理日志级别,抓取当前时间等。它还拥有一个ostringstream,但无处可输出。我已将日志类拆分为" Log"它没有模板部件和" Log2"声明并定义了ostringstream在析构函数中的转储。
  2. 一个输出类,其中转储日志类的ostringstream实际上被发送到输出设备。目前Output2STDOUT是此类。
  3. 一个方便的单线程宏,可以轻松地将记录放入我的代码中。
  4. 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);
    }
    

    以及任何其他文件#include "log.hpp"并将其用作

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

    上面的代码在调试模式下编译良好,但在发布时不编译。我使用自定义makefile,唯一的区别是我删除了-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&)'
    

    你能帮我辨别一下这里有什么问题吗? 我的意思是:

    • debug build并正常工作
    • 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
      

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

    谢谢!

1 个答案:

答案 0 :(得分:3)

您可能需要从两个定义中删除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文件中,因此只有在编译该文件时才可见,并且由于它们未从该文件中使用,编译器会将其删除。