最近在研究怎么把boost跟UE4做结合使用,目前已经成功的将一些好用的boost library应用在专案上了,就先来整理一下相关该注意的技术事项吧。
首先我尝试导入的是boost的log机制,虽然UE4本身已经有LOG系统了,但稍微survey了一下,看起来其实还是不太完整,而且log写起来其实还蛮费劲的;如果能把boost.log导进来的话,不仅输出log的时候会更方便,而且又能够利用boost.log本身上提供的许多强大的功能。
其实boost.log还蛮容易跟其他引擎做结合的,我们只需要写一个backend,并将其所绑定的sink加入到boost.log系统里面就行了。而这个backend要做的事情也很简单,它只需要负责把传进来的讯息再原封不动的传给UE4原本的log系统。听起来是不是很简单?先让我们来看一下下面这段code:
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
namespace keywords = boost::log::keywords;
void HorizonCore::initDefaultLog ( const std::string& fileLogFullPath,
horizon::HorizonLogImpFunc_t&& logFunc,
boost::log::trivial::severity_level logFilterLevel,
boost::log::trivial::severity_level fileLogFilterLevel){
logging::core::get ()-> remove_all_sinks ();
// typedef boost::log::sinks::synchronous_sink<HorizonLogBasicBackend> HorizonLogSink;
typedef boost:: log ::sinks::synchronous_sink<HorizonLogBasicFormatedBackend> HorizonLogSink;
auto sink = boost::make_shared<HorizonLogSink>();
sink-> locked_backend ()-> setLogMessageImplemen (std::forward<horizon::HorizonLogImpFunc_t>(logFunc));
boost::log::core::get ()-> add_sink (sink);
boost::log::add_common_attributes ();
auto logFormat = expr::format ( " %1%: [%2%] [%3%]: %4% " )
% expr::attr< unsigned int >( " RecordID " )
% expr::attr< boost:: log ::trivial::severity_level >( " Severity " )
% expr::attr< boost::posix_time::ptime >( " TimeStamp " )
% expr::smessage;
// % expr::attr< unsigned int >("Function");
sink-> set_formatter (logFormat);
sink-> set_filter (logging::trivial::severity >= logFilterLevel);
logging::core::get ()-> add_global_attribute ( " RecordID " , attrs::counter< unsigned int >());
logging::core::get ()-> add_global_attribute ( " TimeStamp " , attrs::local_clock ());
logging::add_file_log (
keywords::file_name = fileLogFullPath
, keywords::auto_flush = true
, keywords::filter = expr::attr< boost:: log ::trivial::severity_level >( " Severity " ) >= fileLogFilterLevel
, keywords::format = (logFormat)
);
}
把sink加入到log系统的方式如下:
typedef boost::log::sinks::synchronous_sink< Horizo nLogBasicFormatedBackend > Horizo nLogSink;
boost::log::core::get()->add_sink(sink);
红色的部份是我们自己写的backend,其实作如下:
# define HORIZON_LOG_BUFFER_LEN 4 * 1024 // 4KB
using HorizonLogImpFunc_t = std::function< void ( const std::string&)>;
class HorizonLogBasicFormatedBackend : public boost ::log::sinks::basic_formatted_sink_backend < char > {
public:
void consume ( const boost::log::record_view& rec, string_type const & msg);
inline void setLogMessageImplemen (HorizonLogImpFunc_t&& impl) { logMessageImplement = std::move (impl); };
private:
void logMessage ( const boost::log::record_view& rec, const std::string& msg);
private:
HorizonLogImpFunc_t logMessageImplement;
};
void HorizonLogBasicFormatedBackend::logMessage ( const boost::log::record_view& rec, const std::string& msg){
// std::cout << msg;
if (msg. size () > HORIZON_LOG_BUFFER_LEN){
logMessageImplement (msg. substr ( 0 , HORIZON_LOG_BUFFER_LEN - 1 ));
logMessage (rec, msg. substr (HORIZON_LOG_BUFFER_LEN - 1 , msg. size ()));
}
else {
logMessageImplement (msg);
logMessageImplement ( " \n " );
}
}
void HorizonLogBasicFormatedBackend::consume ( const boost::log::record_view& rec, string_type const & msg){
const std::string msgStr = msg;
logMessage (rec, msgStr);
}
这个backend留有一个接口:
inline void setLogMessageImplemen(Horizo nLogImpFunc_t&& impl);
目的就是留一个log的实作方式给引擎方去设定,我们需要做的就是从UE4丢一个std::function到backend就行了。设定方式如下:
void logMessageImplement ( const std::string& msg) {
FString str = UTF8_TO_TCHAR (msg. c_str ());
UE_LOG (LogTemp, Log, TEXT ( " %s " ), *str);
// FPlatformMisc::LowLevelOutputDebugString(TEXT("HorizonLogBasicBackend test"));
// FPlatformMisc::LowLevelOutputDebugString(UTF8_TO_TCHAR(msg.c_str()));
}
FString savedPath = FPaths::ConvertRelativePathToFull(FPaths::GameLogDir());
std::string logFilePath = std::string(TCHAR_TO_UTF8(*savedPath)) + " HorizonLog.txt " ;
horizon::HorizonCore::GetInstance ()->initDefaultLog(logFilePath, std::bind(logMessageImplement, std::placeholders::_1));
然后由于UE4中的check macro跟boost中的有冲突,因此在include boost 相关的header之前需先做下面这件事:
#pragma push_macro(“check")
#undef check
//include your boost header here
#pragma pop_macro(“check")
接下来我们就可以很方便的使用boost所提供的log机制了: