C++11 g++;未定义对成员函数的引用,但仅在编译以供发布时使用

C++11 g++;未定义对成员函数的引用,但仅在编译以供发布时使用,c++11,undefined-reference,C++11,Undefined Reference,我在cli应用程序中使用了一个日志类。这篇文章的基础是: 基本部分是 一个处理日志级别、获取当前时间等的基本日志类。它还包含一个ostringstream,但无处可输出。我在“log”中拆分了log类,它没有模板部分,在“Log2”中声明并定义ostringstream在其析构函数中的转储 输出类,其中日志类的ostringstream转储实际上发送到输出设备。当前output2stout是此类 一个方便的单行宏,可以轻松地将日志记录放在我的代码中 log.hpp看起来像 #define STD

我在
cli
应用程序中使用了一个日志类。这篇文章的基础是:

基本部分是

  • 一个处理日志级别、获取当前时间等的基本日志类。它还包含一个ostringstream,但无处可输出。我在“log”中拆分了log类,它没有模板部分,在“Log2”中声明并定义ostringstream在其析构函数中的转储
  • 输出类,其中日志类的ostringstream转储实际上发送到输出设备。当前
    output2stout
    是此类
  • 一个方便的单行宏,可以轻松地将日志记录放在我的代码中
  • 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>;
    
    #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);
    }
    
    上面的代码在调试模式下编译良好,但在发行版中编译不好。我使用自定义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&)'
    
    你能帮我找出这里出了什么问题吗? 我的意思是:

    • 调试构建并运行良好
    • Output2StOut不是模板类,因此符号位于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
      
    所以我有点迷路了,不知道如何着手解决这个问题


    谢谢大家!

    您可能需要从两个定义中删除
    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);
    }
    
    可能发生的情况是,在编译调试版本时,编译器不会内联函数,也不会为生成的代码删除未使用的函数


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

    您可能需要从两个定义中删除
    内联

    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);
    }
    
    可能发生的情况是,在编译调试版本时,编译器不会内联函数,也不会为生成的代码删除未使用的函数

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