提问人:nass 提问时间:12/21/2016 更新时间:12/21/2016 访问量:852
g++ 未定义对成员函数的引用,但仅在编译发布时
g++ undefined reference to member function, but only when compiling for release
问:
我有一个日志类通过我的应用程序使用。它基于这篇文章: http://www.drdobbs.com/cpp/logging-in-c/201804215?pgno=1cli
基本部件是
- 一个处理日志级别、获取当前时间等的基日志类,它还保存一个 ostringstream,但无处可输出。我已将日志类拆分为“Log”和“Log2”,前者没有模板部分,后者在其析构函数中声明并定义了 ostringstream 的转储。
- 一个输出类,其中转储日志类的 ostringstream 实际上被发送到输出设备。目前是这个类。
Output2STDOUT
- 一个方便的单行宏,可以轻松地将日志记录放在我的代码中。
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
所以我很迷茫,不知道如何解决这个问题。
谢谢!
答:
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 文件中,因此只有在编译该文件时才可见,并且由于它们未从该文件中使用,编译器会删除它们。
评论