最近在研究怎么把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机制了: