ãã¡ã³ããŒæ©èœã®
ãªããã¬ãŒããšããåºçç©ã«è§ŠçºãããŠã
ã¡ã¿ãã€ã¯ãå
¬éããŠããªãã¬ã¯ã·ã§ã³ã®ããã«å
¬éããããšã«ããŸããã ãã ããåçŽãªãã®ã³ã°ããå§ããŠãåè¿°ã®åºçç©ãç¶ããŸãããã
ãã®ã³ã°ãå®è£
ãããšããããèªäœã®ã¿ã¹ã¯ã¯æ¬¡ã®ããã«èšå®ãããŸããã
- æçµçãªå®è£
ããåãé¢ãããã«ããã¡ã¿ã¬ãã«ãã§åé¡ã解決ãã
- ãã®ã³ã°çšã®ããã³ããšã³ãAPIã¯ã·ã³ãã«ã§ééçã§ãªããã°ãªããŸãã
- 1ã€ã®å®æ°ã䜿çšããŠãã³ã³ãã€ã«æ®µéã§äžèŠãªãã°ã¬ãã«ããªãã«ããæ©èœããããŸãã äŸïŒLOG_NOTICEããäžã®ãã¹ãŠãçµæã®ãã€ããªã«å
¥ããªã
ããã³ããšã³ãã¯æ¬¡ã®ããã«ãªããŸãã
1. CConnectionã³ã³ã¹ãã©ã¯ã¿ãŒã§ã次ã®ãã°ãèšé²ããŸãã
TestLog::Log<LOG_NOTICE>() << *this << "connection created";
2. CConnectionã®ç¶æ¿å
public TLogHeader<'c', CConnection>
3.
CRTPã䜿çšãããšãCConnectionã«ã¯æ¬¡ã®ãããªãã®ãããããšã
ããããŸãã
using this_t = CConnection; int m_id; using m_id_t = TParamAccessor<this_t, decltype(this_t::m_id), &this_t::m_id>; std::string m_name; using m_name_t = TParamAccessor<this_t, decltype(this_t::m_name), &this_t::m_name>; char m_state; using m_state_t = TParamAccessor<this_t, decltype(this_t::m_state), &this_t::m_state>; using log_header_param_list_t = std::tuple<m_id_t, m_name_t, m_state_t>;
4.ãããŠããã®ããŒã¿ããã°è¡ã«å€æããŸãã
c:23:test_conn 1:a:connection created
ã³ãã³ããªã¹ããããŠããå ŽæïŒc-TLogHeaderãã³ãã¬ãŒãã®æåã®ãã©ã¡ãŒã¿ãŒã次ã«å€m_idãm_nameãm_state
ç§ã®å Žåãããã¯ãã¹ãŠrsyslogã«ããã«æžã蟌ãŸããããã§MongoDBã®é©åãªãã£ãŒã«ãã«è§£æãããŸãïŒããããŒã®ã¿ã€ããã€ãŸãã³ãã³ã®åã®æåã®ãã©ã¡ãŒã¿ãŒã«ãã£ãŠç°ãªããŸãïŒã
çŸåšããã®ã¢ãããŒãã¯ãè² è·ã®é«ãã·ã¹ãã ã§ã®ç§ã®çç£ã§è¡ãããŠãããããæ§é ç³ã¯ããŒã¯ã«éããç«ã«ãšã£ãŠã¯ããè¯ãç¶æ
ã«ãªããŸãã æ ãªå Žå
ã¯ãhttpïŒ//coliru.stacked-crooked.com/ã«å®éã®äŸããããŸã 250è¡ã®ã³ãŒãã®ç«ã®äžã«ãããŸããã説æã«ã¯ããããã®æåããããŸãã
é³èš³ãšè±èªã®çšèªãããããããå Žåã¯ç³ãèš³ãããŸããã ãã®èšäºã¯å®å
šãªã®ã£ã°ã§ããã20幎éã«ããã£ãŠãã·ã¢ã®çšèªã«é
ãããšã£ãŠããŸããã ãããã£ãŠãæç®ãžã®åç
§ã¯ãããŸããããããæç¹ã§åœŒãã¯stackoverflow.comã§ç§ãå©ããŸãããããªã³ã¯ãäžããããšã¯ãç§ãç解ããŠããããã«ãç ã®ãŠãŒã¶ãŒãšããŠç§ã®å¿åæ§ãçããããšãæå³ããŸãã
ã ããã
ããŒã1-ãã®ã³ã°
ç°¡åãªããšããå§ããŸããã
TestLog::Log<LOG_NOTICE>() << *this << "connection created";
ãã®å Žåã®ããã³ãã¬ãŒãTestLog ::ãã°ãã¯åã§ãïŒãã ããåãè¿ãé¢æ°ãšããŠå®è£
ããããšãã§ããŸãïŒã ã芧ã®ãšããããã®ã¿ã€ãã®äžæãªããžã§ã¯ããäœæãããŸãã ãããŠãã¹ã³ãŒããé¢ãããšãªããžã§ã¯ããç Žæ£ãããããšãç¥ã£ãŠããã®ã§ããã®ã¿ã€ãã®å Žå㯠';'ã«ãªãããšã¯æããã§ãã ãããã£ãŠãåäœã¢ãã«ã¯åçŽã§ã-ããŒã¿ããã®ã¿ã€ãã«ããŒãžãããã®åŸãã¹ãã©ã¯ã¿ãåŒã³åºãããŸãã
ããã¯TSinkã¯ã©ã¹ã«å®è£
ãããŠããŸãã template<int logLevel, bool isNull> struct TSink { ~TSink() { std::cerr << "std::err log line flushed: \"" << stream.str() << " \"" << std::endl; } std::stringstream stream; template<typename T> TSink<logLevel, isNull>& operator << (T&& val) { stream << val; return *this; } }; template<int logLevel> struct TSink<logLevel, true> { template<typename T> TSink<logLevel, true>& operator << (T&& val) { return *this; } };
ããã§ã¯ãã1ã€ã®å®æ°ã䜿çšããŠãã³ã³ãã€ã«æ®µéã§äžèŠãªãã°ã¬ãã«ããªãã«ã§ããããã«ããããã«ãéšåçãªå°éåãå¿
èŠã§ãã äŸïŒLOG_NOTICEããäžã®ãã¹ãŠããçµæã®ãã€ããªã«å
¥ããªãããã«ããŠãã ããã ããã¯ã³ã³ãã€ã©ãŒããã®ãã¬ãŒã³ãã§ãïŒã³ãŒããéã³ã³ãã€ã«ããŠãåºåã«äœããããã確èªããããšããæ°æã¡ã«çããŠããªãã®ã§ãç§ã¯100ïŒ
確信ãæãŠãŸãããããã«ã¡ã¯ïŒïŒã ã¢ã€ãã¢ã¯ç°¡åã§ãïŒã¹ããªãŒã ãžã®æžã蟌ã¿æŒç®åã¯ããŒã¿ãæäœããŸãããäžæãªããžã§ã¯ããäœæãããããåé€ããåã«è¡ããã¹ãŠã®ããšãèæ
®ãããšãææ°ã®ã³ã³ãã€ã©ãŒããã¹ãŠãæé©åããŸãã
ãããŠãããã¯cerrã®ä»£ããã«syslogã«ããŒãžããå Žåã®å€èŠ³ã§ã template<int logLevel, bool isNull> struct TSink { ~TSink() { syslog(logLevel, stream.str().c_str(), "junk"); } std::stringstream stream; template<typename T> TSink<logLevel, isNull>& operator << (T&& val) { stream << val; return *this; } }; template<int logLevel> struct TSink<logLevel, true> { template<typename T> TSink<logLevel, true>& operator << (T&& val) { return *this; } };
TSinkã¯ãç¹å®ã®ãã°ã¢ãžã¥ãŒã«ïŒããšãã°ãæšæºåºåã¹ããªãŒã ããã¡ã€ã«ãsyslogãªã©ïŒã«é¢é£ä»ããããæçµçãªå®è£
ã§ããããšã«æ³šæããŠãã ããã åŸã§èª¬æããTLogã¯ã©ã¹èªäœã¯ãã©ã®ããã«å®è£
ãããããç¥ãããæ¢ã«ç¹å®ã®ã·ã³ã¯å®è£
ãåç
§ããç¹æ§ãåãåããŸãã
TestLog :: Logãšã¯äœããèŠãŠã¿ãŸãããã
ããã¯ïŒ
static const int OUR_LOG_LEVEL = LOG_DEBUG; using TestLog = TLog<OUR_LOG_LEVEL, TLogTraitsStdErr>;
ãããŠãå®éã«ã¯ãTLogã®å®è£
template<int logUpTo, template<int> class log_traits> struct TLog : public log_traits<logUpTo> { using trats_t = log_traits<logUpTo>; template<int logLevel, bool isNull> using sink_type = typename trats_t::template sink_type<logLevel, isNull>; template<int neededLogLevel, int logLevel> struct TGetLogOfLevel { using type = typename std::conditional< neededLogLevel == logLevel, sink_type<neededLogLevel, (neededLogLevel > logUpTo)>, typename TGetLogOfLevel<neededLogLevel, logLevel + 1>::type >::type; }; template<int neededLogLevel> struct TGetLogOfLevel<neededLogLevel, LOG_DEBUG + 1> { using type = void; }; template<int neededLogLevel> using Log = typename TGetLogOfLevel<neededLogLevel, 0>::type; };
ããã³ãã¬ãŒãå
¥åãã§ã2ã€ã®ãã©ã¡ãŒã¿ãŒãååŸããŸããæå®ãããããã°ã©ã ã¢ã»ã³ããªã®æ倧ãã°ã¬ãã«ïŒOUR_LOG_LEVELïŒãšãåŸã§ç¶æ¿ãããã³ãã¬ãŒãã¿ã€ãlog_traitsã§ãã
ã³ãŒããããããããã«ãlog_traitsã¿ã€ãã«ã¯2ã€ã®èŠä»¶ããããŸãã
- ãã®åã¯int logUpToãåãå
¥ããå¿
èŠããããŸãïŒããã¯æåã®TLogãã©ã¡ãŒã¿ãŒãã転éãããŸãïŒ
- ã¿ã€ãã¯ãå¥ã®ãã³ãã¬ãŒãã¿ã€ãsink_type <int logLevelãbool isNull>ã®å®çŸ©å
ã«ãªããã°ãªããŸãããããã§ãlogLevelã¯çŸåšã®ãã®ã³ã°æäœã®ã¬ãã«ã§ãããisNull-çŸåšã®ã¬ãã«ããŸã£ããèšé²ããŸãïŒTSink <logLevelãtrue>ã®éšåçãªç¹æ®åãåç
§ïŒ
ãã¹ãŠãéåžžã«åçŽã«å
éšã§æ©èœããŸããååž°ã¡ã¿é¢æ°TGetLogOfLevelïŒneededLogLevelãlogLevelïŒãåŒã³åºããçµæã§ããLogã¿ã€ããå®çŸ©ããŸãã
ããããã¹ãŠã«åºã¥ããŠãlog_traitsã®æçµçãªå®è£
ãè¡ããŸãã
ãããè¡ãã«ã¯ãäžèšã®log_traitsèŠä»¶ãèæ
®ããŠãå®æããTSinkãæ°ããTLogTraitsã¯ã©ã¹ã«ã©ããããŸãã ã€ãŸã ã¿ã€ãtraits_typeãå®çŸ©ããŸã
template<int logLevel, bool isNull> using sink_type = TSink<logLevel, isNull>;
ãŸããOpenLogé¢æ°ãè¿œå ãããã¹ãã©ã¯ã¿ïŒãã°ïŒã§éããŸãã
TLogTraitsStdErrã³ãŒãã¯æ¬¡ã®ãšããã§ã template<int logUpTo> struct TLogTraitsStdErr { void Open(const char* logName, int facility) { std::cerr << "std::err log opened with logName: " << logName << std::endl; } ~TLogTraitsStdErr() { std::cerr << "std::err log closed" << std::endl; } template<int logLevel, bool isNull> struct TSink { ~TSink() { std::cerr << "std::err log line flushed: \"" << stream.str() << " \"" << std::endl; } std::stringstream stream; template<typename T> TSink<logLevel, isNull>& operator << (T&& val) { stream << val; return *this; } }; template<int logLevel> struct TSink<logLevel, true> { template<typename T> TSink<logLevel, true>& operator << (T&& val) { return *this; } }; template<int logLevel, bool isNull> using sink_type = TSink<logLevel, isNull>; };
TLogTraitsSyslogãšã®é¡æšã«ãã£ãŠ template<int logUpTo> struct TLogTraitsSyslog { static void Open(const char* logName, int facility) { openlog(logName, LOG_PID | LOG_NDELAY, facility); setlogmask(LOG_UPTO(logUpTo)); } ~TLogTraitsSyslog() { closelog(); } template<int logLevel, bool isNull> struct TSink { ~TSink() { syslog(logLevel, stream.str().c_str(), "junk"); } std::stringstream stream; template<typename T> TSink<logLevel, isNull>& operator << (T&& val) { stream << val; return *this; } }; template<int logLevel> struct TSink<logLevel, true> { template<typename T> TSink<logLevel, true>& operator << (T&& val) { return *this; } }; template<int logLevel, bool isNull> using sink_type = TSink<logLevel, isNull>; };
ãããŠããã¡ããããã®ç«ããã¹ãããŸã
ãã¹ãã±ãŒã¹ãæžã static const int OUR_LOG_LEVEL = LOG_DEBUG; using TestLog = TLog<OUR_LOG_LEVEL, TLogTraitsStdErr>; struct CConnection { CConnection() { TestLog::Log<LOG_NOTICE>() << "connection created"; } ~CConnection() { TestLog::Log<LOG_NOTICE>() << "connection destroyed"; } void DoSomething() { TestLog::Log<LOG_DEBUG>() << "connection is doing something"; } }; class CServer : public TestLog { public: CServer() { TestLog::Open("test_log", 1); }; int Run() { TestLog::Log<LOG_DEBUG>() << "Creating connection"; CConnection test_conn1; test_conn1.DoSomething(); return 0; } }; int main(int argc, char** argv) { CServer server; return server.Run(); }
次ã®åºåãã³ã³ãã€ã«ããŠååŸããŸãã
clang++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out std::err log opened with logName: test_log std::err log line flushed: "Creating connection " std::err log line flushed: "connection created " std::err log line flushed: "connection is doing something " std::err log line flushed: "connection destroyed " std::err log closed
ãããŠã圌ãšäžç·ã«ãã¬ã€ããŸãïŒãã®ãªãœãŒã¹ãäœãšåŒã¶ã¹ãããããããŸããïŒ
Coliruãã®çµæã25è¡ã®TLogã¡ã¿ã¯ã©ã¹ãšãlog_traitsã®ç¹å®ã®å®è£
ããšã«40è¡ãååŸããŸããã ãŸããèŠä»¶ãæºããããŠããŸããäžèŠãªãã®ã³ã°ããªãã«ããŸããã ãã ïŒ -ã¯ãã®ããã«ã éæïŒ -ã¡ã¿ã³ãŒããèªãã®ã¯é£ãããããŸããã
ç§ã®èŠ³ç¹ããã¯KISSã
ã©ãïŒ ããã«ã¡ã¯ç³2ïŒ
ããŒã2-åå°
ãã¹ãã±ãŒã¹ã§CConnectionãæ¡åŒµãã
åãã«ãé¢çœããã³ããã£ãããšãæãåºããŠãã ããã
TLogHeader <'c'ãCConnection>ã¯ã CRTPã䜿çšããŠãCConnectionããããæã£ãŠããããšãç¥ã£ãŠããŸãã using this_t = CConnection; int m_id; using m_id_t = TParamAccessor<this_t, decltype(this_t::m_id), &this_t::m_id>; std::string m_name; using m_name_t = TParamAccessor<this_t, decltype(this_t::m_name), &this_t::m_name>; char m_state; using m_state_t = TParamAccessor<this_t, decltype(this_t::m_state), &this_t::m_state>; using log_header_param_list_t = std::tuple<m_id_t, m_name_t, m_state_t>;
ãããé«åºŠãªCConnectionã§ã struct CConnection : public TLogHeader<'c', CConnection> { CConnection(int _id, const std::string& _name) : m_id(_id), m_name(_name), m_state('a') { TestLog::Log<LOG_NOTICE>() << *this << "connection created"; m_state = 'b'; } ~CConnection() { TestLog::Log<LOG_NOTICE>() << *this << "connection destroyed"; } void DoSomething() { TestLog::Log<LOG_DEBUG>() << *this << "connection is doing something"; m_state = 'c'; } using this_t = CConnection; int m_id; using m_id_t = TParamAccessor<this_t, decltype(this_t::m_id), &this_t::m_id>; std::string m_name; using m_name_t = TParamAccessor<this_t, decltype(this_t::m_name), &this_t::m_name>; char m_state; using m_state_t = TParamAccessor<this_t, decltype(this_t::m_state), &this_t::m_state>; using log_header_param_list_t = std::tuple<m_id_t, m_name_t, m_state_t>; };
ãã¡ãããããã¯ãã¹ãŠãPARAMïŒintãm_idïŒãªã©ã®åçŽãªãã¯ãã§ã©ããã§ããŸãããæ£æ°ã®ããã«ã¯ããŸããã
TParamAccessorã¯ãã¯ã©ã¹å€æ°ãžã®åç
§ãè¿ã1ã€ã®éçGetRefé¢æ°ãå®çŸ©ãããŠããåã§ãã
ãã®ããã« struct TParamAccessorDefaultTraits { }; template <typename _object_t, typename _value_type, _value_type _object_t::*member_t, typename _traits_t = TParamAccessorDefaultTraits> struct TParamAccessor : public _traits_t { using traits_t = _traits_t; using object_t = _object_t; using value_type = _value_type; static value_type& GetRef(object_t& data) { return data.*member_t; } };
ãããã£ãŠããããsink_typeã¹ããªãŒã ã«ããã·ã¥ããã«ã¯ãCConnection :: log_header_param_list_tãä»ããŠTLogHeader <'c'ãCConnection>ããç¶æ¿ããsink_type << TParamAccessor :: GetRefïŒãªããžã§ã¯ãïŒãåŒã³åºãå¿
èŠããããŸãã
èŠåïŒã¡ã¿ãªãã·ã§ã³for_eachãèŠããŸã§ãããã§ã¯ç解ããã®ãå°ãé£ããã§ãããã
TLogHeaderã¯ã©ã¹ãååŸããŸã template<char moduleName, typename object_t> struct TLogHeader { template<size_t idx, typename accessor_t> struct TLogHeaderWriter { using type = TLogHeaderWriter<idx, accessor_t>; static void call(typename accessor_t::object_t& obj, std::stringstream& out) { typename accessor_t::value_type& val = accessor_t::GetRef(obj); out << val << ":"; } }; template<typename sink_type> friend sink_type& operator << (sink_type& sink, object_t& val) { std::stringstream header; using writers = typename for_each<typename object_t::log_header_param_list_t>::template instantiate<TLogHeaderWriter>; for_each<writers>::call(*static_cast<object_t*>(&val), header); sink << moduleName << ":" << header.str(); return sink; } };
TLogHeaderã¯ãã¹ããªãŒã ãžã®ãªãŒããŒããŒãæžã蟌ã¿æŒç®åãšãã1ã€ã®é¢æ°ã®ã¿ãèšè¿°ããŸãã
ããã§ã¯ã2ã€ã®è£
ãã§for_eachã確èªããŸãã
- TLogHeaderWriterã®ãã¹ãŠã®ã€ã³ã¹ã¿ã³ã¹<from object_t :: log_header_param_list_t ::åã¿ã€ãå
ãã>ãã¿ãã«ãšããŠè¿ãããã©ã€ã¿ãŒã®ã¿ã€ãã決å®ããã¡ã¿é¢æ°åŒã³åºãïŒ
- ã©ã€ã¿ãŒã§å®çŸ©ããããã¹ãŠã®ã¿ã€ãã®éçåŒã³åºãé¢æ°ã®åŒã³åºã
for_eachã®ã¡ã¿ä»®èª¬ãè¡ããŸããã
ãããè¡ãã«ã¯ãTLogHeaderãåçŽåããŠã¡ã¿é¢æ°ã®ã¿ãåŒã³åºããŸã template<char moduleName, typename object_t> struct TLogHeader { template<size_t idx, typename accessor_t> struct TLogHeaderWriter { using type = TLogHeaderWriter<idx, accessor_t>; }; template<typename sink_type> friend sink_type& operator << (sink_type& sink, object_t& val) { using writers = typename for_each<typename object_t::log_header_param_list_t>::template instantiate<TLogHeaderWriter>; return sink; } };
ãããŠãã¡ã¿é¢æ°ãæžããŸãã
å
責äºé
ïŒãªããªã ç°¡åã«ããããã«ãæçµã³ãŒããã«ãããããã¹ãããŸããã§ããã æåŸã®ãã®ã¯ãåŸã§ã©ã€ãã§ãã¹ãã§ããŸãã
念ã®ãããç§ã«ãšã£ãŠæãæ確ãªçšèªã¯ããã¡ã¿æ©èœãšã¯ãããã°ã©ã ãã³ã³ãã€ã«ãã段éã§å®è¡ãããæ©èœã§ããããã®çµæã¯æ°ããã¿ã€ãã§ããããšããããšã§ãïŒãã¹ãŠèªåã®èšèã§ïŒ
ç§ã¯ã³ãŒãã§ã³ã¡ã³ãããŸã ããã«å°ãæ··ä¹±ããŸãã
For_eachã³ãŒã //////////////////////////////////////////////////////////////// // append to tuple //- new_t // tuple template argument pack, template<typename new_t, typename... other_t> struct append_to_tuple { using type = std::tuple<other_t..., new_t>; }; template<typename new_t, typename... other_t> struct append_to_tuple<new_t, std::tuple<other_t...>> { using type = std::tuple<other_t..., new_t>; }; //////////////////////////////////////////////////////////////// // for_each_impl // for_each - , func_t instatiate, //, tuple append_to_tuple // , , template<size_t i, typename... args_t> struct for_each_impl { using this_type = typename std::tuple_element<i, std::tuple<args_t...>>::type; using prev_type = for_each_impl<i - 1, args_t...>; template<template<size_t, typename> class func_t> using instantiate = typename append_to_tuple< typename func_t<i, this_type>::type, typename prev_type::template instantiate<func_t> >::type; }; // // template<typename... args_t> struct for_each_impl<0, args_t...> { using this_type = typename std::tuple_element<0, std::tuple<args_t...>>::type; template<template<size_t, typename> class func_t> using instantiate = std::tuple<typename func_t<0, this_type>::type>; }; /////////////////////////////////////////////////////////////// // for_each // . // for_each_impl, // + tuple< > tuple<> template<typename... args_t> struct for_each { using prev_type = for_each_impl<sizeof...(args_t) - 1, args_t...>; template<template<size_t, typename> class func_t> using instantiate = typename prev_type::template instantiate<func_t>; }; template<typename... args_t> struct for_each<std::tuple<args_t...>> : public for_each<args_t...> { }; template<> struct for_each<std::tuple<>> { template<template<size_t, typename> class func_t> using instantiate = std::tuple<>; };
ãããã£ãŠãwriters = typename for_each <typename object_t :: log_header_param_list_t> :: template instantiate;ã䜿çšããŠååŸããŸããã äœå®¶ãå±éããå ŽæïŒ
std::tuple< TLogHeaderWriter<0, TParamAccessor<CConnection, decltype(CConnection::m_id), &CConnection::m_id>>, TLogHeaderWriter<1, TParamAccessor<CConnection, decltype(CConnection::m_name), &CConnection::m_name>>, TLogHeaderWriter<2, TParamAccessor<CConnection, decltype(CConnection::m_state), &CConnection::m_state>> >;
ãããã®åãééããGetRefãåŒã³åºããŠããã¹ããªãŒã ã«æžã蟌ãããšã¯æ®ã£ãŠããŸãã
for_eachã®å®è¡æã®ä»®èª¬ãç«ãŠãŸããã
ãããŠããã«ãæ¡åŒµãããfor_eachãèŠãŠãã ãããããã¯ãéçé¢æ°åŒã³åºãã«åŒã³åºããè¿œå ããŸãïŒæ³šæãã䟡å€ãããã ãã§ãæ®ãã¯å€æŽãããŸããïŒã
ãããŠããã«ãã-æåŸã®ã³ãŒã //////////////////////////////////////////////////////////////// // for_each_impl template<size_t i, typename... args_t> struct for_each_impl { using this_type = typename std::tuple_element<i, std::tuple<args_t...>>::type; using prev_type = for_each_impl<i - 1, args_t...>; template<template<size_t, typename> class func_t> using instantiate = typename append_to_tuple< typename func_t<i, this_type>::type, typename prev_type::template instantiate<func_t> >::type; template<typename object_t, typename... call_args_t> static void call(object_t&& obj, call_args_t&&... args) { prev_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); this_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); } }; template<typename... args_t> struct for_each_impl<0, args_t...> { using this_type = typename std::tuple_element<0, std::tuple<args_t...>>::type; template<template<size_t, typename> class func_t> using instantiate = std::tuple<typename func_t<0, this_type>::type>; template<typename object_t, typename... call_args_t> static void call(object_t&& obj, call_args_t&&... args) { this_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); } template<typename object_t> static void call(object_t&& obj) { this_type::call(std::forward<object_t>(obj)); } }; /////////////////////////////////////////////////////////////// // for_each template<typename... args_t> struct for_each { using prev_type = for_each_impl<sizeof...(args_t) - 1, args_t...>; template<template<size_t, typename> class func_t> using instantiate = typename prev_type::template instantiate<func_t>; template<typename object_t, typename... call_args_t> static object_t call(object_t&& obj, call_args_t&&... args) { prev_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); return obj; } template<typename object_t> static object_t call(object_t&& obj) { prev_type::call(std::forward<object_t>(obj)); return obj; } }; template<typename... args_t> struct for_each<std::tuple<args_t...>> : public for_each<args_t...> { }; template<> struct for_each<std::tuple<>> { template<template<size_t, typename> class func_t> using instantiate = std::tuple<>; template<typename object_t, typename... call_args_t> static object_t call(object_t&& obj, call_args_t&&... args) { return obj; } template<typename object_t> static object_t call(object_t&& obj) { return obj; } };
ããã§ã³ã¡ã³ãããå¿
èŠã¯ç¹ã«ãªããšæããŸããåŒã³åºãã¯å°ãªããšã1ã€ã®ãã©ã¡ãŒã¿ãŒãåãåããŸããããã¯ãããã·ãªããžã§ã¯ãã§ãããäœæ¥ã®æåŸã«æ»ããæ®ãã®ãã©ã¡ãŒã¿ãŒã¯åŒã³åºãåŒã³åºãã«è»¢éãããŸãã
ç§ãã¡ã®å Žåãããã¯TLogHeaderWirter :: callïŒã§ãã
static void call(typename accessor_t::object_t& obj, std::stringstream& out) { typename accessor_t::value_type& val = accessor_t::GetRef(obj); out << val << ":"; }
where accessor_t :: object_t = CConnection
ãããŠãã¡ãããå®ç§ãªè»¢éã¯éèŠã§ãïŒ
ããŒã3-çµè«
å
šã³ãŒã #include <cstdlib> #include <iostream> #include <sstream> #include <syslog.h> static const int OUR_LOG_LEVEL = LOG_DEBUG; // = LOG_NOTICE; // log up to LOG_DEBUG output: //std::err log opened with logName: test_log //std::err log line flushed: "s:1:test server:Creating connection " //std::err log line flushed: "c:23:test_conn 1:a:connection created " //std::err log line flushed: "c:23:test_conn 1:b:connection is doing something " //std::err log line flushed: "c:23:test_conn 1:c:connection destroyed " //std::err log closed // log up to LOG_NOTICE output: //std::err log opened with logName: test_log //std::err log line flushed: "c:23:test_conn 1:a:connection created " //std::err log line flushed: "c:23:test_conn 1:c:connection destroyed " //std::err log closed // ---------------------------- for_each.h start ----------------------------// #include <tuple> #include <utility> namespace helpers { //////////////////////////////////////////////////////////////// // param accessor struct TParamAccessorDefaultTraits { }; template <typename _object_t, typename _value_type, _value_type _object_t::*member_t, typename _traits_t = TParamAccessorDefaultTraits> struct TParamAccessor : public _traits_t { using traits_t = _traits_t; using object_t = _object_t; using value_type = _value_type; static value_type& GetRef(object_t& data) { return data.*member_t; } }; //////////////////////////////////////////////////////////////// // append to tuple template<typename new_t, typename... other_t> struct append_to_tuple { using type = std::tuple<other_t..., new_t>; }; template<typename new_t, typename... other_t> struct append_to_tuple<new_t, std::tuple<other_t...>> { using type = std::tuple<other_t..., new_t>; }; //////////////////////////////////////////////////////////////// // for_each_impl template<size_t i, typename... args_t> struct for_each_impl { using this_type = typename std::tuple_element<i, std::tuple<args_t...>>::type; using prev_type = for_each_impl<i - 1, args_t...>; template<template<size_t, typename> class func_t> using instantiate = typename append_to_tuple< typename func_t<i, this_type>::type, typename prev_type::template instantiate<func_t> >::type; template<typename object_t, typename... call_args_t> static void call(object_t&& obj, call_args_t&&... args) { prev_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); this_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); } }; template<typename... args_t> struct for_each_impl<0, args_t...> { using this_type = typename std::tuple_element<0, std::tuple<args_t...>>::type; template<template<size_t, typename> class func_t> using instantiate = std::tuple<typename func_t<0, this_type>::type>; template<typename object_t, typename... call_args_t> static void call(object_t&& obj, call_args_t&&... args) { this_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); } template<typename object_t> static void call(object_t&& obj) { this_type::call(std::forward<object_t>(obj)); } }; /////////////////////////////////////////////////////////////// // for_each template<typename... args_t> struct for_each { using prev_type = for_each_impl<sizeof...(args_t) - 1, args_t...>; template<template<size_t, typename> class func_t> using instantiate = typename prev_type::template instantiate<func_t>; template<typename object_t, typename... call_args_t> static object_t call(object_t&& obj, call_args_t&&... args) { prev_type::call(std::forward<object_t>(obj), std::forward<call_args_t>(args)...); return obj; } template<typename object_t> static object_t call(object_t&& obj) { prev_type::call(std::forward<object_t>(obj)); return obj; } }; template<typename... args_t> struct for_each<std::tuple<args_t...>> : public for_each<args_t...> { }; template<> struct for_each<std::tuple<>> { template<template<size_t, typename> class func_t> using instantiate = std::tuple<>; template<typename object_t, typename... call_args_t> static object_t call(object_t&& obj, call_args_t&&... args) { return obj; } template<typename object_t> static object_t call(object_t&& obj) { return obj; } }; } //namespace helpers // ---------------------------- for_each.h end ----------------------------// using namespace helpers; ////////////////////////////////////////////////////////////////////////////////////////// template<int logUpTo> struct TLogTraitsStdErr { static void Open(const char* logName, int facility) { std::cerr << "std::err log opened with logName: " << logName << std::endl; } ~TLogTraitsStdErr() { std::cerr << "std::err log closed" << std::endl; } template<int logLevel, bool isNull> struct TSink { ~TSink() { std::cerr << "std::err log line flushed: \"" << stream.str() << " \"" << std::endl; } std::stringstream stream; template<typename T> TSink<logLevel, isNull>& operator << (T&& val) { stream << val; return *this; } }; template<int logLevel> struct TSink<logLevel, true> { template<typename T> TSink<logLevel, true>& operator << (T&& val) { return *this; } }; template<int logLevel, bool isNull> using sink_type = TSink<logLevel, isNull>; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template<int logUpTo> struct TLogTraitsSyslog { static void Open(const char* logName, int facility) { openlog(logName, LOG_PID | LOG_NDELAY, facility); setlogmask(LOG_UPTO(logUpTo)); } ~TLogTraitsSyslog() { closelog(); } template<int logLevel, bool isNull> struct TSink { ~TSink() { syslog(logLevel, stream.str().c_str(), "junk"); } std::stringstream stream; template<typename T> TSink<logLevel, isNull>& operator << (T&& val) { stream << val; return *this; } }; template<int logLevel> struct TSink<logLevel, true> { template<typename T> TSink<logLevel, true>& operator << (T&& val) { return *this; } }; template<int logLevel, bool isNull> using sink_type = TSink<logLevel, isNull>; }; ////////////////////////////////////////////////////////////////////////////////////////// template<char moduleName, typename object_t> struct TLogHeader { template<size_t idx, typename accessor_t> struct TLogHeaderWriter { using type = TLogHeaderWriter<idx, accessor_t>; static void call(typename accessor_t::object_t& obj, std::stringstream& out) { typename accessor_t::value_type& val = accessor_t::GetRef(obj); out << val << ":"; } }; template<typename sink_type> friend sink_type& operator << (sink_type& sink, object_t& val) { std::stringstream header; using writers = typename for_each<typename object_t::log_header_param_list_t>::template instantiate<TLogHeaderWriter>; for_each<writers>::call(*static_cast<object_t*>(&val), header); sink << moduleName << ":" << header.str(); return sink; } }; ////////////////////////////////////////////////////////////////////////////////////////// template<int logUpTo, template<int> class log_traits = TLogTraitsSyslog> struct TLog : public log_traits<logUpTo> { using trats_t = log_traits<logUpTo>; template<int logLevel, bool isNull> using sink_type = typename trats_t::template sink_type<logLevel, isNull>; template<int neededLogLevel, int logLevel> struct TGetLogOfLevel { using type = typename std::conditional< neededLogLevel == logLevel, sink_type<neededLogLevel, (neededLogLevel > logUpTo)>, typename TGetLogOfLevel<neededLogLevel, logLevel + 1>::type >::type; }; template<int neededLogLevel> struct TGetLogOfLevel<neededLogLevel, LOG_DEBUG + 1> { using type = void; }; template<int neededLogLevel> using Log = typename TGetLogOfLevel<neededLogLevel, 0>::type; }; /////////////////////////////// //testcase using TestLog = TLog<OUR_LOG_LEVEL, TLogTraitsStdErr>; struct CConnection : public TLogHeader<'c', CConnection> { CConnection(int _id, const std::string& _name) : m_id(_id), m_name(_name), m_state('a') { TestLog::Log<LOG_NOTICE>() << *this << "connection created"; m_state = 'b'; } ~CConnection() { TestLog::Log<LOG_NOTICE>() << *this << "connection destroyed"; } void DoSomething() { TestLog::Log<LOG_DEBUG>() << *this << "connection is doing something"; m_state = 'c'; } using this_t = CConnection; int m_id; using m_id_t = TParamAccessor<this_t, decltype(this_t::m_id), &this_t::m_id>; std::string m_name; using m_name_t = TParamAccessor<this_t, decltype(this_t::m_name), &this_t::m_name>; char m_state; using m_state_t = TParamAccessor<this_t, decltype(this_t::m_state), &this_t::m_state>; using log_header_param_list_t = std::tuple<m_id_t, m_name_t, m_state_t>; }; class CServer : public TLogHeader<'s', CServer>, public TestLog { public: CServer() : m_id(1), m_name("test server") { TestLog::Open("test_log", 1); }; int Run() { TestLog::Log<LOG_DEBUG>() << *this << "Creating connection"; CConnection test_conn1(23, "test_conn 1"); test_conn1.DoSomething(); return 0; } using this_t = CServer; int m_id; using m_id_t = TParamAccessor<this_t, decltype(this_t::m_id), &this_t::m_id>; std::string m_name; using m_name_t = TParamAccessor<this_t, decltype(this_t::m_name), &this_t::m_name>; using log_header_param_list_t = std::tuple<m_id_t, m_name_t> ; }; int main(int argc, char** argv) { CServer server; return server.Run(); }
ãããæ¢è£œã®äŸã§ãã2ã€ã®for_eachãªãã·ã§ã³ã®å®è£
ãããã«100è¡è¿œå ããŸããããããŠãäŸãèŠããšãããããå¥ã®ãã¡ã€ã«for_each.hã«ããããšãããããŸãããªããªã
ç§ã¯æŽå²ã®5ã€ã®ã¢ãžã¥ãŒã«ã§ç©æ¥µçã«äœ¿çšããŠããŸãïŒãšã³ãã·ã¹ãã ã®ãšã³ãã£ã¢ã³ãèæ
®ããŠpostgresãšéä¿¡ãããã€ãã£ããã€ããªåœ¢åŒããããã¡ãããã±ãããå±éããã³ã³ãã€ã«æ®µéã§Webãœã±ããã«ã¡ãã»ãŒãžåœ¢åŒãçæãããªã©ãããŠç§ãèšã£ãããã«ïŒç³ïŒã¬ã»ãã·ã§ã³ïŒ
PSã³ãŒãã®äœæã«ã¯åæ¥ããããHabrã®æ¥ã®èšäºã¯...ãã©ããã¯ã¹ã§ãïŒ