diff --git a/CMakeLists.txt b/CMakeLists.txt index a25626f..1e96eaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,8 @@ set(TILE_SRCS "tile/rpc/protocol/http/buffer_io.cc" "tile/rpc/protocol/message.cc" # "tile/rpc/server.cc" + "tile/util/config.cc" + "tile/util/ini_file_config.cc" ) if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS)) @@ -366,6 +368,7 @@ if(TILE_BUILD_TESTS) tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc") # tile_add_test(base_internal_time_keeper_test # "tile/base/internal/time_keeper_test.cc") + tile_add_test(tile_util_ini_file_config_test "tile/util/ini_file_config_test.cc") endif(TILE_BUILD_TESTS) if(TILE_BUILD_BENCHMARKS) diff --git a/tile/base/casting.h b/tile/base/casting.h index 3aa4dfd..a5ee6e1 100644 --- a/tile/base/casting.h +++ b/tile/base/casting.h @@ -10,10 +10,32 @@ #include "tile/base/likely.h" namespace tile { +namespace detail { +// Has classof +template struct HasClassofImpl { + template < + typename TT, typename TBase, + typename = enable_if_t())), bool>::value>> + static std::true_type test(int); + template static std::false_type test(...); + + static constexpr bool value = decltype(test(0))::value; +}; +} // namespace detail + template struct CastingTraits { - template static bool RuntimeTypeCheck(const U &val) { + template + static auto RuntimeTypeCheck(const Base &val) + -> enable_if_t::value, bool> { return T::classof(val); } + + template + static auto RuntimeTypeCheck(const Base &val) + -> enable_if_t::value, bool> { + return dynamic_cast(&val) != nullptr; + } }; class Castable { diff --git a/tile/base/internal/logging.h b/tile/base/internal/logging.h index b151218..6c527d1 100644 --- a/tile/base/internal/logging.h +++ b/tile/base/internal/logging.h @@ -637,6 +637,11 @@ public: size_t message_len); }; +class FileLogSink : public LogSink { +public: + FileLogSink(const std::string &file_path_template); +}; + void AddLogSink(LogSink *dest); void RemoveLogSink(LogSink *dest); void SetStderrLogging(LogSeverity min_severity); diff --git a/tile/base/ref_ptr.h b/tile/base/ref_ptr.h index a1c3a1c..35f000b 100644 --- a/tile/base/ref_ptr.h +++ b/tile/base/ref_ptr.h @@ -3,6 +3,8 @@ #pragma once +#include "tile/base/casting.h" +#include "tile/base/down_cast.h" #include "tile/base/internal/logging.h" #include "tile/base/internal/meta.h" #include "tile/base/internal/utility.h" @@ -333,6 +335,12 @@ public: return internal::Exchange(ptr_, nullptr); } + template bool Is() const noexcept { return isa(ptr_); } + + template RefPtr As() const noexcept { + return RefPtr(ref_ptr, down_cast(ptr_)); + } + private: T *ptr_; }; diff --git a/tile/base/string.cc b/tile/base/string.cc index 84e2728..fce7e6e 100644 --- a/tile/base/string.cc +++ b/tile/base/string.cc @@ -83,35 +83,30 @@ std::vector Split(Slice s, Slice delim, bool keep_empty, TILE_CHECK_GE(max_split_parts, 0, "`max_split_parts` must be greater than 0"); - constexpr std::size_t kMaxSplit = -1; + constexpr std::size_t kMaxSplit = std::numeric_limits::max(); std::vector splited; - if (s.empty()) { + if (s.empty() || max_split_parts == 1) { return splited; } auto current = s; assert(!delim.empty()); int split_count = 0; while (max_split_parts == kMaxSplit || split_count < max_split_parts) { - auto pos = current.find(delim); - if (pos != 0 || keep_empty) { - splited.push_back(current.substr(0, pos)); - ++split_count; + auto next_pos = current.find(delim); + auto part = current.substr(0, next_pos); + if (!part.empty() || keep_empty) { + splited.push_back(part); } - if (pos == Slice::npos) { - break; - } + ++split_count; - current = current.substr(pos + delim.size()); - if (current.empty()) { - if (keep_empty) { - splited.push_back(""); - } + if (next_pos == Slice::npos) { break; - } else if (split_count + 1 == max_split_parts) { - // ++split_count; - splited.push_back(current); + } else if (split_count + 1 >= max_split_parts) { + splited.push_back(current.substr(next_pos + delim.size())); break; + } else { + current = current.substr(next_pos + delim.size()); } } @@ -119,22 +114,36 @@ std::vector Split(Slice s, Slice delim, bool keep_empty, } Slice TrimLeft(Slice s, Slice cutset) { - while (!s.empty() && cutset.find(Slice(s.data(), 1)) != Slice::npos) { + return TrimLeft(s, [&cutset](char c) { + return cutset.find(Slice(&c, 1)) != Slice::npos; + }); +} + +Slice TrimRight(Slice s, Slice cutset) { + return TrimRight(s, [&cutset](char c) { + return cutset.find(Slice(&c, 1)) != Slice::npos; + }); +} + +Slice Trim(Slice s, Slice cutset) { + return Trim(s, [&cutset](char c) { + return cutset.find(Slice(&c, 1)) != Slice::npos; + }); +} +Slice TrimLeft(Slice s, std::function pred) { + while (!s.empty() && pred(s[0])) { s.RemovePrefix(1); } return s; } - -Slice TrimRight(Slice s, Slice cutset) { - while (!s.empty() && - cutset.find(Slice(s.data() + s.size() - 1, 1)) != Slice::npos) { +Slice TrimRight(Slice s, std::function pred) { + while (!s.empty() && pred(s[s.size() - 1])) { s.RemoveSuffix(1); } return s; } - -Slice Trim(Slice s, Slice cutset) { - return TrimRight(TrimLeft(s, cutset), cutset); +Slice Trim(Slice s, std::function pred) { + return TrimRight(TrimLeft(s, pred), pred); } template @@ -198,11 +207,23 @@ std::string ToLower(Slice s) { return result; } -bool Equals(Slice a, Slice b) { +bool Equals(Slice a, Slice b, std::size_t len) { + if (a.size() > len) { + a.RemoveSuffix(a.size() - len); + } + if (b.size() > len) { + b.RemoveSuffix(b.size() - len); + } return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } -bool EqualsIgnoreCase(Slice a, Slice b) { +bool EqualsIgnoreCase(Slice a, Slice b, std::size_t len) { + if (a.size() > len) { + a.RemoveSuffix(a.size() - len); + } + if (b.size() > len) { + b.RemoveSuffix(b.size() - len); + } return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), [](char a, char b) { return ToLower(a) == ToLower(b); }); @@ -213,21 +234,27 @@ TryParseTraits::TryParse(Slice s, bool recognizes_alpbabet_symbol, bool ignore_case) { auto num_opt = tile::TryParse(s); if (num_opt) { - if (*num_opt == 0) { + return *num_opt != 0; + } else { + const std::vector true_arr{"true", "yes", "on", "y"}; + const std::vector false_arr{"false", "no", "off", "n"}; + if (s.size() > 5) { return false; - } else if (*num_opt == 1) { - return true; } - return std::nullopt; - } - - if (EqualsIgnoreCase(s, "y") || EqualsIgnoreCase(s, "yes") || - EqualsIgnoreCase(s, "true")) { - return true; - } else if (EqualsIgnoreCase(s, "n") || EqualsIgnoreCase(s, "no") || - EqualsIgnoreCase(s, "false")) { - return false; + auto InArray = [](const std::vector &arr, Slice s) { + for (auto &&e : arr) { + if (EqualsIgnoreCase(e, s)) { + return true; + } + } + return false; + }; + if (InArray(true_arr, s)) { + return true; + } else if (InArray(false_arr, s)) { + return false; + } } return std::nullopt; diff --git a/tile/base/string.h b/tile/base/string.h index 2c79a0f..46b5143 100644 --- a/tile/base/string.h +++ b/tile/base/string.h @@ -34,14 +34,19 @@ std::string Replace(Slice str, Slice from, Slice to, std::size_t count = std::numeric_limits::max()); -Slice TrimLeft(Slice s, Slice cutset = " \t"); -Slice TrimRight(Slice s, Slice cutset = " \t"); -Slice Trim(Slice s, Slice cutset = " \t"); +Slice TrimLeft(Slice s, Slice cutset); +Slice TrimRight(Slice s, Slice cutset); +Slice Trim(Slice s, Slice cutset); +Slice TrimLeft(Slice s, std::function pred = isspace); +Slice TrimRight(Slice s, std::function pred = isspace); +Slice Trim(Slice s, std::function pred = isspace); -std::vector Split(Slice s, char delim, bool keep_empty = false, - std::size_t max_split_parts = -1); -std::vector Split(Slice s, Slice delim, bool keep_empty = false, - std::size_t max_split_parts = -1); +std::vector +Split(Slice s, char delim, bool keep_empty = false, + std::size_t max_split_parts = std::numeric_limits::max()); +std::vector +Split(Slice s, Slice delim, bool keep_empty = false, + std::size_t max_split_parts = std::numeric_limits::max()); std::string Join(const std::vector &parts, Slice delim); std::string Join(const std::vector &parts, Slice delim); @@ -54,8 +59,11 @@ void ToLower(std::string *str); std::string ToUpper(Slice s); std::string ToLower(Slice s); -bool Equals(Slice a, Slice b); -bool EqualsIgnoreCase(Slice a, Slice b); +bool Equals(Slice a, Slice b, + std::size_t len = std::numeric_limits::max()); +bool EqualsIgnoreCase( + Slice a, Slice b, + std::size_t len = std::numeric_limits::max()); // TryParse template struct TryParseTraits; @@ -73,6 +81,10 @@ template struct TryParseTraits { }; // String to bool +// 1. "true" or "false" +// 2. "yes" or "no" +// 3. "on" or "off" +// 4. non-zero or zero template <> struct TryParseTraits { static std::optional TryParse(Slice s, bool recognizes_alpbabet_symbol = true, diff --git a/tile/base/string_test.cc b/tile/base/string_test.cc index 25ba71c..19cdca7 100644 --- a/tile/base/string_test.cc +++ b/tile/base/string_test.cc @@ -102,7 +102,7 @@ TEST(String, TryParseFloatingPoint) { TEST(String, TryParseBool) { ASSERT_FALSE(TryParse("")); ASSERT_FALSE(TryParse("..")); - ASSERT_FALSE(TryParse("2")); + ASSERT_TRUE(TryParse("2")); ASSERT_TRUE(TryParse("1")); ASSERT_TRUE(TryParse("0")); diff --git a/tile/io/event_loop.cc b/tile/io/event_loop.cc index e5b3d09..159bb1c 100644 --- a/tile/io/event_loop.cc +++ b/tile/io/event_loop.cc @@ -24,7 +24,7 @@ void EventLoop::Barrier() {} void EventLoop::Run() {} void EventLoop::Stop() {} void EventLoop::Join() {} -EventLoop *EventLoop::Current() {} +EventLoop *EventLoop::Current() { return nullptr; } void EventLoop::WaitAndRunEvents(std::chrono::milliseconds wait_for) {} void EventLoop::RunUserTasks() {} } // namespace tile diff --git a/tile/sigslot/signal.h b/tile/sigslot/signal.h deleted file mode 100644 index 9f27191..0000000 --- a/tile/sigslot/signal.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef TILE_SIGSLOT_SIGNAL_H -#define TILE_SIGSLOT_SIGNAL_H - -#pragma once -#include "tile/base/internal/meta.h" -#include -#include - -/** - * tile::sigslot::Signal signal; - **/ - -namespace tile { -namespace sigslot { - -class NonLock { -public: - void Lock() {} - void Unlock() {} -}; - -class GlobalLock { - void Lock(); - void Unlock(); -}; - -class ThreadedLock { - void Lock(); - void Unlock(); -}; - -class Connection { -public: -}; - -class Slot { -public: - Slot() = default; - virtual ~Slot() = default; - -private: - std::set connections_; -}; - -template class Signal; -template class Signal { -public: - using SlotType = std::function; - - R Emit(Args... args) const; - R operator()(Args... args) const { Emit(std::forward(args)...); }; - - // function ptr - Connection Connect(R (*func)(Args...)); - - // member function - template Connection Connect(C *clz, R (C::*mem_fn)(Args...)); - - // lambda function - Connection Connect(std::function lambda_fn); - template ()(std::declval()...)), - R>::value>> - Connection Connect(Functor functor); -}; - -} // namespace sigslot -} // namespace tile - -#endif // TILE_SIGSLOT_SIGNAL_H diff --git a/tile/sigslot/sigslot.h b/tile/sigslot/sigslot.h new file mode 100644 index 0000000..912b235 --- /dev/null +++ b/tile/sigslot/sigslot.h @@ -0,0 +1,1682 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// + +#ifndef __SIGSLOT_H__ +#define __SIGSLOT_H__ + +#include +#include + +#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WIN32) +# define _SIGSLOT_HAS_WIN32_THREADS +# include +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + +#ifndef SIGSLOT_EMIT +# ifdef QT_VERSION +# define SIGSLOT_EMIT broadcast +# else +# define SIGSLOT_EMIT emit +# endif +#endif + +namespace sigslot { + +class single_threaded +{ +public: + single_threaded() {} + virtual ~single_threaded() {} + virtual void lock() {} + virtual void unlock() {} +}; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global +{ +public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + {} + + virtual ~multi_threaded_global() + {} + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + +private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } +}; + +class multi_threaded_local +{ +public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + +private: + CRITICAL_SECTION m_critsec; +}; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global +{ +public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + {} + + virtual ~multi_threaded_global() + {} + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + +private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } +}; + +class multi_threaded_local +{ +public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + +private: + pthread_mutex_t m_mutex; +}; +#endif // _SIGSLOT_HAS_POSIX_THREADS + +template +class lock_block +{ +public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } +}; + +template +class has_slots; + +template +class _connection_base0 +{ +public: + virtual ~_connection_base0() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base1 +{ +public: + virtual ~_connection_base1() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type) = 0; + virtual _connection_base1* clone() = 0; + virtual _connection_base1* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base2 +{ +public: + virtual ~_connection_base2() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type) = 0; + virtual _connection_base2* clone() = 0; + virtual _connection_base2* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base3 +{ +public: + virtual ~_connection_base3() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3* clone() = 0; + virtual _connection_base3* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base4 +{ +public: + virtual ~_connection_base4() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4* clone() = 0; + virtual _connection_base4* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base5 +{ +public: + virtual ~_connection_base5() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5* clone() = 0; + virtual _connection_base5* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base6 +{ +public: + virtual ~_connection_base6() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6* clone() = 0; + virtual _connection_base6* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base7 +{ +public: + virtual ~_connection_base7() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7* clone() = 0; + virtual _connection_base7* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _connection_base8 +{ +public: + virtual ~_connection_base8() {} + virtual has_slots* getdest() const = 0; + virtual void SIGSLOT_EMIT(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8* clone() = 0; + virtual _connection_base8* duplicate(has_slots* pnewdest) = 0; +}; + +template +class _signal_base : public mt_policy +{ +public: + virtual void slot_disconnect(has_slots* pslot) = 0; + virtual void slot_duplicate(const has_slots* poldslot, has_slots* pnewslot) = 0; +}; + +// Implements common functionality of signalN classes. signalN classes +// derive from this class. +template +class _signal_base_middle: public _signal_base +{ +public: + + typedef T connections_list; + typedef typename connections_list::iterator iterator; + typedef typename connections_list::const_iterator const_iterator; + + _signal_base_middle() {} + + _signal_base_middle(const _signal_base_middle &s) + : _signal_base(s) + { + lock_block lock(this); + + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + ++it; + } + } + + virtual ~_signal_base_middle() {} + + void disconnect_all() + { + lock_block lock(this); + + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + bool is_empty() + { + lock_block lock(this); + + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + return it == itEnd; + } + +#ifdef _DEBUG + bool connected(has_slots* pclass) + { + lock_block lock(this); + + const_iterator itNext, it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + if ((*it)->getdest() == pclass) + return true; + it = itNext; + } + return false; + } +#endif + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + +protected: + + connections_list m_connected_slots; + +}; + +template +class has_slots : public mt_policy +{ +private: + typedef typename std::set<_signal_base *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + +public: + has_slots() + {} + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base* sender) + { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base* sender) + { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + +private: + sender_set m_senders; +}; + + + +template +class _connection0 : public _connection_base0 +{ +public: + _connection0() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection0() + {} + + virtual _connection_base0* clone() + { + return new _connection0(*this); + } + + virtual _connection_base0* duplicate(has_slots* pnewdest) + { + return new _connection0((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); +}; + +template +class _connection1 : public _connection_base1 +{ +public: + _connection1() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection1() + {} + + virtual _connection_base1* clone() + { + return new _connection1(*this); + } + + virtual _connection_base1* duplicate(has_slots* pnewdest) + { + return new _connection1((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); +}; + +template +class _connection2 : public _connection_base2 +{ +public: + _connection2() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection2() + {} + + virtual _connection_base2* clone() + { + return new _connection2(*this); + } + + virtual _connection_base2* duplicate(has_slots* pnewdest) + { + return new _connection2((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); +}; + +template +class _connection3 : public _connection_base3 +{ +public: + _connection3() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection3() + {} + + virtual _connection_base3* clone() + { + return new _connection3(*this); + } + + virtual _connection_base3* duplicate(has_slots* pnewdest) + { + return new _connection3((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); +}; + +template +class _connection4 : public _connection_base4 +{ +public: + _connection4() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection4() + {} + + virtual _connection_base4* clone() + { + return new _connection4(*this); + } + + virtual _connection_base4* duplicate(has_slots* pnewdest) + { + return new _connection4((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); +}; + +template +class _connection5 : public _connection_base5 +{ +public: + _connection5() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection5() + {} + + virtual _connection_base5* clone() + { + return new _connection5(*this); + } + + virtual _connection_base5* duplicate(has_slots* pnewdest) + { + return new _connection5((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); +}; + +template +class _connection6 : public _connection_base6 +{ +public: + _connection6() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection6() + {} + + virtual _connection_base6* clone() + { + return new _connection6(*this); + } + + virtual _connection_base6* duplicate(has_slots* pnewdest) + { + return new _connection6((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); +}; + +template +class _connection7 : public _connection_base7 +{ +public: + _connection7() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection7() + {} + + virtual _connection_base7* clone() + { + return new _connection7(*this); + } + + virtual _connection_base7* duplicate(has_slots* pnewdest) + { + return new _connection7((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); +}; + +template +class _connection8 : public _connection_base8 +{ +public: + _connection8() + { + m_pobject = NULL; + m_pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection8() + {} + + virtual _connection_base8* clone() + { + return new _connection8(*this); + } + + virtual _connection_base8* duplicate(has_slots* pnewdest) + { + return new _connection8((dest_type *)pnewdest, m_pmemfun); + } + + virtual void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + +private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); +}; + +template +class signal0 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal0 this_type; + typedef std::list<_connection_base0*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal0() + {} + + signal0(const this_type& s) + : base_type(s) + {} + + ~signal0() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block lock(this); + + _connection0* conn = + new _connection0(pclass, pmemfun); + + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT() + { + lock_block lock(this); + + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(); + + it = itNext; + } + } + + void operator()() + { + SIGSLOT_EMIT(); + } +}; + +template +class signal1 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal1 this_type; + typedef std::list<_connection_base1*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal1() + {} + + signal1(const this_type& s) + : base_type(s) + {} + + ~signal1() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block lock(this); + _connection1* conn = + new _connection1(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + SIGSLOT_EMIT(a1); + } +}; + +template +class signal2 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal2 this_type; + typedef std::list<_connection_base2*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal2() + {} + + signal2(const this_type& s) + : base_type(s) + {} + + ~signal2() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block lock(this); + _connection2* conn = new + _connection2(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + SIGSLOT_EMIT(a1, a2); + } +}; + +template +class signal3 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal3 this_type; + typedef std::list<_connection_base3*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal3() + {} + + signal3(const this_type& s) + : base_type(s) + {} + + ~signal3() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block lock(this); + _connection3* conn = + new _connection3(pclass, + pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + SIGSLOT_EMIT(a1, a2, a3); + } +}; + +template +class signal4 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal4 this_type; + typedef std::list<_connection_base4*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal4() + {} + + signal4(const this_type& s) + : base_type(s) + {} + + ~signal4() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block lock(this); + _connection4* + conn = new _connection4(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + SIGSLOT_EMIT(a1, a2, a3, a4); + } +}; + +template +class signal5 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal5 this_type; + typedef std::list<_connection_base5*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal5() + {} + + signal5(const this_type& s) + : base_type(s) + {} + + ~signal5() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block lock(this); + _connection5* conn = new _connection5(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5); + } +}; + + +template +class signal6 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal6 this_type; + typedef std::list<_connection_base6*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal6() + {} + + signal6(const this_type& s) + : base_type(s) + {} + + ~signal6() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block lock(this); + _connection6* conn = + new _connection6(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6); + } +}; + +template +class signal7 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal7 this_type; + typedef std::list<_connection_base7*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal7() + {} + + signal7(const this_type& s) + : base_type(s) + {} + + ~signal7() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block lock(this); + _connection7* conn = + new _connection7(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7); + } +}; + +template +class signal8 : public _signal_base_middle*>, mt_policy> +{ +public: + + typedef signal8 this_type; + typedef std::list<_connection_base8*> connections_list; + typedef _signal_base_middle base_type; + + using base_type::m_connected_slots; + + signal8() + {} + + signal8(const this_type& s) + : base_type(s) + {} + + ~signal8() + { + base_type::disconnect_all(); + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block lock(this); + _connection8* conn = + new _connection8(pclass, pmemfun); + m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void SIGSLOT_EMIT(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + typename connections_list::const_iterator itNext, it = m_connected_slots.begin(); + typename connections_list::const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + SIGSLOT_EMIT(a1, a2, a3, a4, a5, a6, a7, a8); + } +}; + +namespace impl +{ + +struct empty {}; + +} // namespace impl + +// signal can be used instead of the numbered signalN classes. +// For example: +// +// sigslot::signal signal; +// +// instead of +// +// sigslot::signal2 signal; +template +struct signal; + +template<> +struct signal<>: public signal0<> +{}; + +template +struct signal: public signal1 +{}; + +template +struct signal: public signal2 +{}; + +template +struct signal: public signal3 +{}; + +template +struct signal: public signal4 +{}; + +template +struct signal: public signal5 +{}; + +template +struct signal: public signal6 +{}; + +template +struct signal: + public signal7 +{}; + +template +struct signal: public signal8 +{}; + +// Some convenience methods for signal handling. +template +struct has_signals +{ + virtual ~has_signals() {} + + // Connect a signal to a slot on the specified destination object. + template + static inline void connect(Signal &signal, Dst *dst, Sig memfun) + { + signal.connect(dst, memfun); + } + + // Connect a signal to a slot on 'this'. + template + inline void connect(Signal &signal, Sig memfun) + { + Derived* dst = static_cast(this); + connect(signal, dst, memfun); + } +}; + +}; // namespace sigslot + +#endif // __SIGSLOT_H__ \ No newline at end of file diff --git a/tile/util/config.cc b/tile/util/config.cc new file mode 100644 index 0000000..ddd3178 --- /dev/null +++ b/tile/util/config.cc @@ -0,0 +1,113 @@ +#include "tile/util/config.h" +#include "tile/base/thread/unique_lock.h" + +namespace tile { +namespace util { +bool Config::Has(const Slice &key) const { + UniqueLock lock(mutex_); + std::string value; + return GetRaw(key, &value); +} + +void Config::Remove(const Slice &key) {} + +void Config::EnableEvents(bool enable) { events_enabled_ = enable; } +bool Config::EventsEnabled() const { return events_enabled_; } + +Config::Keys Config::keys(const Slice &root) const { + UniqueLock lock(mutex_); + return Enumerate(root); +} + +#define TILE_DEFINE_CONFIG_GETTER(type, name) \ + std::optional Config::Get##name(const Slice &key) const { \ + std::string value; \ + if (GetRaw(key, &value)) { \ + auto opt = TryParseTraits::TryParse(value); \ + if (opt.has_value()) { \ + return *opt; \ + } else { \ + return std::nullopt; \ + } \ + } else { \ + return std::nullopt; \ + } \ + } \ + type Config::Get##name(const Slice &key, type default_value) const { \ + auto opt = Get##name(key); \ + if (opt.has_value()) { \ + return *opt; \ + } else { \ + return default_value; \ + } \ + } + +#define TILE_DEFINE_CONFIG_SETTER(type, name) \ + void Config::Set##name(const Slice &key, type value) { \ + SetRawWithEvent(key, Format("{}", value)); \ + } + +std::optional Config::GetString(const Slice &key) const { + std::string value; + UniqueLock lock(mutex_); + if (GetRaw(key, &value)) { + return value; + } else { + return std::nullopt; + } +} + +std::string Config::GetString(const Slice &key, + std::string default_value) const { + auto opt = GetString(key); + return opt.has_value() ? *opt : default_value; +} + +void Config::SetString(const Slice &key, const std::string &value) { + SetRawWithEvent(key, value); +} +void Config::SetBool(const Slice &key, bool value) { + SetRawWithEvent(key, value ? "true" : "false"); +} + +TILE_DEFINE_CONFIG_GETTER(int, Int) +TILE_DEFINE_CONFIG_GETTER(unsigned int, UInt) +TILE_DEFINE_CONFIG_GETTER(int16_t, Int16) +TILE_DEFINE_CONFIG_GETTER(int32_t, Int32) +TILE_DEFINE_CONFIG_GETTER(int64_t, Int64) +TILE_DEFINE_CONFIG_GETTER(uint16_t, UInt16) +TILE_DEFINE_CONFIG_GETTER(uint32_t, UInt32) +TILE_DEFINE_CONFIG_GETTER(uint64_t, UInt64) +TILE_DEFINE_CONFIG_GETTER(double, Double) +TILE_DEFINE_CONFIG_GETTER(bool, Bool) + +TILE_DEFINE_CONFIG_SETTER(int, Int) +TILE_DEFINE_CONFIG_SETTER(unsigned int, UInt) +TILE_DEFINE_CONFIG_SETTER(int16_t, Int16) +TILE_DEFINE_CONFIG_SETTER(int32_t, Int32) +TILE_DEFINE_CONFIG_SETTER(int64_t, Int64) +TILE_DEFINE_CONFIG_SETTER(uint16_t, UInt16) +TILE_DEFINE_CONFIG_SETTER(uint32_t, UInt32) +TILE_DEFINE_CONFIG_SETTER(uint64_t, UInt64) +TILE_DEFINE_CONFIG_SETTER(double, Double) + +void Config::SetRawWithEvent(const Slice &key, const std::string &value) { + if (events_enabled_) { + OnChanging(key, value); + } + + { + UniqueLock lock(mutex_); + SetRaw(key, value); + } + + if (events_enabled_) { + OnChanged(key, value); + } +} + +Config::~Config() {} + +} // namespace util + +} // namespace tile diff --git a/tile/util/config.h b/tile/util/config.h index 2c4a545..e5ca5b9 100644 --- a/tile/util/config.h +++ b/tile/util/config.h @@ -3,8 +3,12 @@ #pragma once +#include "tile/base/optional.h" #include "tile/base/ref_ptr.h" #include "tile/base/slice.h" +#include "tile/base/string.h" +#include "tile/base/thread/mutex.h" +#include "tile/sigslot/sigslot.h" #include namespace tile { @@ -14,16 +18,42 @@ public: using Keys = std::vector; using Ptr = RefPtr; + // events + // Key, Value + sigslot::signal2 OnChanging; + // Key, Value + sigslot::signal2 OnChanged; + // Key + sigslot::signal1 OnRemoving; + // Key + sigslot::signal1 OnRemoved; + bool Has(const Slice &key) const; + void Remove(const Slice &key); + void EnableEvents(bool enable = true); + bool EventsEnabled() const; + + Keys keys(const Slice &root = "") const; #define TILE_DECLARE_CONFIG_GETTER(type, name) \ - type Get##name(const Slice &key) const; \ + std::optional Get##name(const Slice &key) const; \ type Get##name(const Slice &key, type default_value) const; #define TILE_DECLARE_CONFIG_SETTER(type, name) \ virtual void Set##name(const Slice &key, type value); std::string GetRawString(const Slice &key, const Slice &default_value) const; + // std::stirng GetString() + // int GetInt() + // unsigned int GetUInt() + // int16_t GetInt16() + // int32_t GetInt32() + // int64_t GetInt64() + // uint16_t GetUInt16() + // uint32_t GetUInt32() + // uint64_t GetUInt64() + // double GetDouble() + // bool GetBool() // getters TILE_DECLARE_CONFIG_GETTER(std::string, String) @@ -40,6 +70,17 @@ public: protected: // setters + // SetString(const Slice &key, const std::string &value) + // SetInt(const Slice &key, int value) + // SetUInt(const Slice &key, unsigned int value) + // SetInt16(const Slice &key, int16_t value) + // SetInt32(const Slice &key, int32_t value) + // SetInt64(const Slice &key, int64_t value) + // SetUInt16(const Slice &key, uint16_t value) + // SetUInt32(const Slice &key, uint32_t value) + // SetUInt64(const Slice &key, uint64_t value) + // SetDouble(const Slice &key, double value) + // SetBool(const Slice &key, bool value) TILE_DECLARE_CONFIG_SETTER(const std::string &, String) TILE_DECLARE_CONFIG_SETTER(int, Int) TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt) @@ -55,9 +96,30 @@ protected: #undef TILE_DECLARE_CONFIG_GETTER #undef TILE_DECLARE_CONFIG_SETTER - void Remove(const Slice &key); +protected: + class ScopedLock { + public: + explicit ScopedLock(const Config &config) : config_(config) { + config_.mutex_.Lock(); + } + ~ScopedLock() { config_.mutex_.Unlock(); } - Keys keys(const Slice &key_root = "") const; + private: + const Config &config_; + }; + + virtual bool GetRaw(const Slice &key, std::string *value) const = 0; + virtual void SetRaw(const Slice &key, const Slice &value) = 0; + virtual void RemoveRaw(const Slice &key) = 0; + virtual Keys Enumerate(const Slice &range) const = 0; + void SetRawWithEvent(const Slice &key, const std::string &value); + virtual ~Config(); + + friend class std::default_delete; + +private: + mutable Mutex mutex_; + bool events_enabled_{false}; }; } // namespace util } // namespace tile diff --git a/tile/util/ini_file_config.cc b/tile/util/ini_file_config.cc new file mode 100644 index 0000000..7349245 --- /dev/null +++ b/tile/util/ini_file_config.cc @@ -0,0 +1,144 @@ +#include "tile/util/ini_file_config.h" +#include "tile/base/thread/scoped_lock.h" + +namespace tile { +namespace util { + +IniFileConfig::IniFileConfig() {} +// IniFileConfig::IniFileConfig(std::istream &istr) { load(istr); } +// IniFileConfig::IniFileConfig(const std::string &path) { load(path); } +IniFileConfig::~IniFileConfig() {} + +bool IniFileConfig::load(std::istream &istr) { + Config::ScopedLock lock(*this); + map_.clear(); + section_key_.clear(); + while (!istr.eof()) { + ParseLine(istr); + } + return true; +} +bool IniFileConfig::load(const std::string &path) { + std::ifstream istr(path); + if (istr.good()) { + return load(istr); + } else { + return false; + } +} +bool IniFileConfig::GetRaw(const Slice &key, std::string *value) const { + auto iter = map_.find(key.ToString()); + if (iter != map_.end()) { + *value = iter->second; + return true; + } else { + return false; + } +} +void IniFileConfig::SetRaw(const Slice &key, const Slice &value) { + map_[key] = value; +} + +void IniFileConfig::RemoveRaw(const Slice &key) { + std::string prefix = key; + if (!prefix.empty()) { + prefix.push_back('.'); + } + IStringMap::iterator it = map_.begin(); + IStringMap::iterator cur; + while (it != map_.end()) { + if (EqualsIgnoreCase(cur->first, key) || + EqualsIgnoreCase(cur->first, prefix)) { + it = map_.erase(cur); + } else { + ++it; + } + } +} + +Config::Keys IniFileConfig::Enumerate(const Slice &key) const { + Config::Keys range; + std::set keys; + std::string prefix = key.ToString(); + if (prefix.empty()) { + prefix.push_back('.'); + } + + std::string::size_type psize = prefix.size(); + for (const auto &p : map_) { + auto &key = p.first; + if (EqualsIgnoreCase(key, prefix, psize)) { + std::string::size_type end = key.find('.', psize); + std::string subkey; + if (end == std::string::npos) { + subkey = key.substr(psize); + } else { + subkey = key.substr(psize, end - psize); + } + + if (keys.find(subkey) == keys.end()) { + keys.insert(subkey); + range.push_back(subkey); + } + } + } + return range; +} + +void IniFileConfig::ParseLine(std::istream &istr) { + static const int eof = std::char_traits::eof(); + auto ReadLine = [&](std::string *line) { + line->clear(); + while (true) { + int c = istr.get(); + if (c == eof || c == '\n') { + return c != eof; + } else { + *line += (char)c; + } + } + }; + + std::string raw_line; + while (ReadLine(&raw_line)) { + if (raw_line.empty()) { + continue; + } + Slice line = TrimLeft(raw_line); + if (line.empty() || line[0] == ';' || line[0] == '#') { + // skip empty line + // skip comment line ; or # + continue; + } + + // parse section + if (line[0] == '[') { + auto pos = line.find(']'); + if (pos == Slice::npos) { + section_key_ = Trim(line.substr(1)); + } else { + section_key_ = Trim(line.substr(1, pos - 1)); + } + } else { + auto strs = Split(line, "=", true, 2); + std::string full_key = section_key_; + if (!full_key.empty()) { + full_key.push_back('.'); + } + full_key.append(Trim(strs[0])); + if (strs.size() > 1) { + map_[full_key] = Trim(strs[1]); + } else { + map_[full_key] = ""; + } + } + } +} +bool IniFileConfig::ICompare::operator()(const std::string &s1, + const std::string &s2) const { + auto len = std::min(s1.size(), s2.size()); + return strncmp(s1.c_str(), s2.c_str(), len) < 0; +} + +} // namespace util +} // namespace tile diff --git a/tile/util/ini_file_config.h b/tile/util/ini_file_config.h new file mode 100644 index 0000000..a7a61a3 --- /dev/null +++ b/tile/util/ini_file_config.h @@ -0,0 +1,40 @@ +#ifndef TILE_UTIL_INI_FILE_CONFIG_H +#define TILE_UTIL_INI_FILE_CONFIG_H + +#pragma once + +#include "tile/util/config.h" +#include + +namespace tile { +namespace util { +class IniFileConfig : public Config { +public: + using Ptr = RefPtr; + + IniFileConfig(); + ~IniFileConfig() override; + // IniFileConfig(std::istream &istr); + // IniFileConfig(const std::string &path); + bool load(std::istream &istr); + bool load(const std::string &path); + +protected: + bool GetRaw(const Slice &key, std::string *value) const override; + void SetRaw(const Slice &key, const Slice &value) override; + void RemoveRaw(const Slice &key) override; + Keys Enumerate(const Slice &range) const override; + +private: + void ParseLine(std::istream &istr); + struct ICompare { + bool operator()(const std::string &s1, const std::string &s2) const; + }; + typedef std::map IStringMap; + IStringMap map_; + std::string section_key_; +}; +} // namespace util +} // namespace tile + +#endif // TILE_UTIL_INI_FILE_CONFIG_H diff --git a/tile/util/ini_file_config_test.cc b/tile/util/ini_file_config_test.cc new file mode 100644 index 0000000..b3b8600 --- /dev/null +++ b/tile/util/ini_file_config_test.cc @@ -0,0 +1,46 @@ +#include "tile/util/ini_file_config.h" +#include "gtest/gtest.h" + +const char *kIniFileConfig = R"( +# comment 1 +; comment 2 + # commet 3 + ; commment 4 +a=1 +[sec1] +a=2 +[sec3] +a=3 +[sec2.kk] +a=4 +)"; + +namespace tile { +namespace util { + +static_assert(!detail::HasClassofImpl::value, ""); + +TEST(IniFileConfig, LoadFromIStream) { + std::stringstream ss(kIniFileConfig); + Config::Ptr config = MakeRefCounted(); + ASSERT_FALSE(config->Has("a")); + ASSERT_FALSE(config->Has("sec1.a")); + ASSERT_FALSE(config->Has("sec3.a")); + if (config.Is()) { + IniFileConfig::Ptr ini = config.As(); + ASSERT_TRUE(ini->load(ss)); + } + ASSERT_TRUE(config->Has("a")); + ASSERT_TRUE(config->Has("sec1.a")); + ASSERT_TRUE(config->Has("sec3.a")); + ASSERT_TRUE(config->Has("sec2.kk.a")); + + ASSERT_TRUE(config->GetInt("a")); + ASSERT_TRUE(config->GetInt("sec1.a")); + ASSERT_EQ(1, *config->GetInt("a")); + ASSERT_EQ(2, *config->GetInt("sec1.a")); + ASSERT_EQ(3, *config->GetInt("sec3.a")); + ASSERT_EQ(4, *config->GetInt("sec2.kk.a")); +} +} // namespace util +} // namespace tile