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
应用程序中使用了一个日志类。这篇文章的基础是:
基本部分是
output2stout
是此类#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文件中,因此只有在编译该文件时才可见,并且由于这些函数不是从该文件中使用的,编译器会删除它们