From 0ac617f3940054595da4660c423a9c5d235a5bb5 Mon Sep 17 00:00:00 2001 From: Matt Koscumb Date: Mon, 26 Jun 2023 11:18:12 -0700 Subject: [PATCH 1/5] Add noexcept on non throwing methods identified by static analysis --- lib/api/LogManagerImpl.cpp | 8 ++++---- lib/api/LogSessionData.cpp | 2 +- lib/include/public/LogSessionData.hpp | 2 +- lib/offline/LogSessionDataProvider.cpp | 4 ++-- lib/offline/LogSessionDataProvider.hpp | 6 +++--- lib/pal/desktop/NetworkDetector.cpp | 2 +- lib/pal/desktop/NetworkDetector.hpp | 2 +- lib/system/TelemetrySystemBase.hpp | 10 +++++----- lib/utils/StringUtils.cpp | 10 +++++----- lib/utils/StringUtils.hpp | 6 +++--- lib/utils/Utils.cpp | 2 +- lib/utils/Utils.hpp | 2 +- lib/utils/annex_k.hpp | 8 ++++---- tests/common/Common.cpp | 4 ++-- tests/common/Common.hpp | 4 ++-- tests/common/MockIRuntimeConfig.hpp | 2 +- tests/functests/MultipleLogManagersTests.cpp | 2 +- 17 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/api/LogManagerImpl.cpp b/lib/api/LogManagerImpl.cpp index 6a98e6406..f0537f425 100644 --- a/lib/api/LogManagerImpl.cpp +++ b/lib/api/LogManagerImpl.cpp @@ -836,7 +836,7 @@ namespace MAT_NS_BEGIN return; } - auto itDataInspector = std::find_if(m_dataInspectors.begin(), m_dataInspectors.end(), [&dataInspector](const std::shared_ptr& currentInspector) + auto itDataInspector = std::find_if(m_dataInspectors.begin(), m_dataInspectors.end(), [&dataInspector](const std::shared_ptr& currentInspector) noexcept { return strcmp(dataInspector->GetName(), currentInspector->GetName()) == 0; }); @@ -859,7 +859,7 @@ namespace MAT_NS_BEGIN void LogManagerImpl::RemoveDataInspector(const std::string& name) { LOCKGUARD(m_dataInspectorGuard); - auto itDataInspector = std::find_if(m_dataInspectors.begin(), m_dataInspectors.end(), [&name](const std::shared_ptr& inspector){ + auto itDataInspector = std::find_if(m_dataInspectors.begin(), m_dataInspectors.end(), [&name](const std::shared_ptr& inspector) noexcept { return strcmp(inspector->GetName(), name.c_str()) == 0; }); @@ -872,7 +872,7 @@ namespace MAT_NS_BEGIN std::shared_ptr LogManagerImpl::GetDataInspector(const std::string& name) noexcept { LOCKGUARD(m_dataInspectorGuard); - auto it = std::find_if(m_dataInspectors.begin(), m_dataInspectors.end(), [&name](const std::shared_ptr& inspector){ + auto it = std::find_if(m_dataInspectors.begin(), m_dataInspectors.end(), [&name](const std::shared_ptr& inspector) noexcept{ return strcmp(inspector->GetName(), name.c_str()) == 0; }); @@ -941,7 +941,7 @@ namespace MAT_NS_BEGIN if (m_pause_state != PauseState::Pausing) { return; } - m_pause_cv.wait(lock, [this]() -> bool { + m_pause_cv.wait(lock, [this]() noexcept -> bool { return m_pause_state != PauseState::Pausing; }); } diff --git a/lib/api/LogSessionData.cpp b/lib/api/LogSessionData.cpp index 9ea280f6a..34f6538b2 100644 --- a/lib/api/LogSessionData.cpp +++ b/lib/api/LogSessionData.cpp @@ -10,7 +10,7 @@ using namespace std; namespace MAT_NS_BEGIN { - uint64_t LogSessionData::getSessionFirstTime() const + uint64_t LogSessionData::getSessionFirstTime() const noexcept { return m_sessionFirstTimeLaunch; } diff --git a/lib/include/public/LogSessionData.hpp b/lib/include/public/LogSessionData.hpp index 024f2272f..0a6737f36 100644 --- a/lib/include/public/LogSessionData.hpp +++ b/lib/include/public/LogSessionData.hpp @@ -29,7 +29,7 @@ namespace MAT_NS_BEGIN /// Gets the time that this session began. /// /// A 64-bit integer that contains the time. - uint64_t getSessionFirstTime() const; + uint64_t getSessionFirstTime() const noexcept; /// /// Gets the SDK unique identifier. diff --git a/lib/offline/LogSessionDataProvider.cpp b/lib/offline/LogSessionDataProvider.cpp index 1615d84e7..4155c3d54 100644 --- a/lib/offline/LogSessionDataProvider.cpp +++ b/lib/offline/LogSessionDataProvider.cpp @@ -49,7 +49,7 @@ namespace MAT_NS_BEGIN } } - LogSessionData* LogSessionDataProvider::GetLogSessionData() + LogSessionData* LogSessionDataProvider::GetLogSessionData() noexcept { return m_logSessionData.get(); } @@ -153,7 +153,7 @@ namespace MAT_NS_BEGIN return true; } - uint64_t LogSessionDataProvider::convertStrToLong(const std::string& s) + uint64_t LogSessionDataProvider::convertStrToLong(const std::string& s) noexcept { uint64_t res = 0ull; char *endptr = nullptr; diff --git a/lib/offline/LogSessionDataProvider.hpp b/lib/offline/LogSessionDataProvider.hpp index 78dbab792..70e0eeb15 100644 --- a/lib/offline/LogSessionDataProvider.hpp +++ b/lib/offline/LogSessionDataProvider.hpp @@ -21,7 +21,7 @@ namespace MAT_NS_BEGIN { public: LogSessionDataProvider( - IOfflineStorage* offlineStorage) + IOfflineStorage* offlineStorage) noexcept : m_offlineStorage(offlineStorage), m_storageType(SessionStorageType::DatabaseStore), @@ -41,7 +41,7 @@ namespace MAT_NS_BEGIN void CreateLogSessionData(); void ResetLogSessionData(); void DeleteLogSessionData(); - LogSessionData *GetLogSessionData(); + LogSessionData *GetLogSessionData() noexcept; protected: void CreateLogSessionDataFromFile(); @@ -55,7 +55,7 @@ namespace MAT_NS_BEGIN std::string const m_cacheFilePath; SessionStorageType m_storageType; std::unique_ptr m_logSessionData; - uint64_t convertStrToLong(const std::string&); + uint64_t convertStrToLong(const std::string&) noexcept; void writeFileContents(const std::string&, uint64_t, const std::string&); void remove_eol(std::string& ); }; diff --git a/lib/pal/desktop/NetworkDetector.cpp b/lib/pal/desktop/NetworkDetector.cpp index 90c3e5724..ee99b7bc3 100644 --- a/lib/pal/desktop/NetworkDetector.cpp +++ b/lib/pal/desktop/NetworkDetector.cpp @@ -330,7 +330,7 @@ namespace MAT_NS_BEGIN return true; } - bool NetworkDetector::RegisterAndListen() + bool NetworkDetector::RegisterAndListen() noexcept { // ??? HRESULT hr = pNlm->QueryInterface(IID_IUnknown, (void**)&pSink); diff --git a/lib/pal/desktop/NetworkDetector.hpp b/lib/pal/desktop/NetworkDetector.hpp index a3f976d6a..66f8b6ae8 100644 --- a/lib/pal/desktop/NetworkDetector.hpp +++ b/lib/pal/desktop/NetworkDetector.hpp @@ -153,7 +153,7 @@ namespace MAT_NS_BEGIN /// Register and listen to network state notifications /// /// - bool RegisterAndListen(); + bool RegisterAndListen() noexcept; /// /// Reset network state listener to uninitialized state diff --git a/lib/system/TelemetrySystemBase.hpp b/lib/system/TelemetrySystemBase.hpp index e2c9189f2..e9ff10d6b 100644 --- a/lib/system/TelemetrySystemBase.hpp +++ b/lib/system/TelemetrySystemBase.hpp @@ -36,11 +36,11 @@ namespace MAT_NS_BEGIN { m_isPaused(false), stats(*this, taskDispatcher) { - onStart = []() { return true; }; - onStop = []() { return true; }; - onPause = []() { return true; }; - onResume = []() { return true; }; - onCleanup = []() { return true; }; + onStart = []() noexcept { return true; }; + onStop = []() noexcept { return true; }; + onPause = []() noexcept { return true; }; + onResume = []() noexcept { return true; }; + onCleanup = []() noexcept { return true; }; }; /// diff --git a/lib/utils/StringUtils.cpp b/lib/utils/StringUtils.cpp index 87461e224..bcc031cd5 100644 --- a/lib/utils/StringUtils.cpp +++ b/lib/utils/StringUtils.cpp @@ -31,7 +31,7 @@ namespace MAT_NS_BEGIN } } - bool StringUtils::AreAllCharactersAllowlisted(const string& stringToTest, const string& allowlist) + bool StringUtils::AreAllCharactersAllowlisted(const string& stringToTest, const string& allowlist) noexcept { return (stringToTest.find_first_not_of(allowlist) == string::npos); } @@ -77,7 +77,7 @@ namespace MAT_NS_BEGIN { std::string result = str; std::transform(str.begin(), str.end(), result.begin(), - [](unsigned char c) { return (char)::tolower(c); }); + [](unsigned char c) noexcept { return (char)::tolower(c); }); return result; } @@ -85,7 +85,7 @@ namespace MAT_NS_BEGIN { std::string result = str; std::transform(str.begin(), str.end(), result.begin(), - [](unsigned char c) { return (char)::toupper(c); }); + [](unsigned char c) noexcept { return (char)::toupper(c); }); return result; } @@ -103,7 +103,7 @@ namespace MAT_NS_BEGIN return str; } - const char* priorityToStr(EventPriority priority) + const char* priorityToStr(EventPriority priority) noexcept { switch (priority) { @@ -130,7 +130,7 @@ namespace MAT_NS_BEGIN } } - const char* latencyToStr(EventLatency latency) + const char* latencyToStr(EventLatency latency) noexcept { switch (latency) { diff --git a/lib/utils/StringUtils.hpp b/lib/utils/StringUtils.hpp index 464f551f1..dc7c42bc8 100644 --- a/lib/utils/StringUtils.hpp +++ b/lib/utils/StringUtils.hpp @@ -17,7 +17,7 @@ namespace MAT_NS_BEGIN namespace StringUtils { void SplitString(const std::string& s, const char separator, std::vector& parts); - bool AreAllCharactersAllowlisted(const std::string& stringToTest, const std::string& allowlist); + bool AreAllCharactersAllowlisted(const std::string& stringToTest, const std::string& allowlist) noexcept; } std::string toString(char const* value); @@ -44,9 +44,9 @@ namespace MAT_NS_BEGIN std::string sanitizeIdentifier(const std::string& str); - const char* priorityToStr(EventPriority priority); + const char* priorityToStr(EventPriority priority) noexcept; - const char* latencyToStr(EventLatency latency); + const char* latencyToStr(EventLatency latency) noexcept; bool replace(std::string& str, const std::string& from, const std::string& to); diff --git a/lib/utils/Utils.cpp b/lib/utils/Utils.cpp index 52e58b179..687a3bf5b 100644 --- a/lib/utils/Utils.cpp +++ b/lib/utils/Utils.cpp @@ -74,7 +74,7 @@ namespace MAT_NS_BEGIN { #endif } - bool IsRunningInApp() + bool IsRunningInApp() noexcept { #ifdef _WINRT_DLL // Win 10 UWP typedef LONG (*LPFN_GPFN)(UINT32*, PWSTR); diff --git a/lib/utils/Utils.hpp b/lib/utils/Utils.hpp index 78c47201a..ce249b9ab 100644 --- a/lib/utils/Utils.hpp +++ b/lib/utils/Utils.hpp @@ -67,7 +67,7 @@ namespace MAT_NS_BEGIN { long GetCurrentProcessId(); /* Detects if current process is running in a packaged app*/ - bool IsRunningInApp(); + bool IsRunningInApp() noexcept; std::string GetTempDirectory(); std::string GetAppLocalTempDirectory(); diff --git a/lib/utils/annex_k.hpp b/lib/utils/annex_k.hpp index 4e9fef9ed..5aa4b73af 100644 --- a/lib/utils/annex_k.hpp +++ b/lib/utils/annex_k.hpp @@ -45,7 +45,7 @@ namespace MAT_NS_BEGIN class BoundCheckFunctions { private: -static bool oneds_buffer_region_overlap(const char *buffer1, size_t buffer1_len, const char *buffer2, size_t buffer2_len) +static bool oneds_buffer_region_overlap(const char *buffer1, size_t buffer1_len, const char *buffer2, size_t buffer2_len) noexcept { if (buffer2 >= buffer1) { @@ -70,7 +70,7 @@ static bool oneds_buffer_region_overlap(const char *buffer1, size_t buffer1_len, // - returns zero if str is a null pointer // - returns strsz if the null character was not found in the first strsz bytes of str. -static size_t oneds_strnlen_s(const char *str, size_t strsz) +static size_t oneds_strnlen_s(const char *str, size_t strsz) noexcept { if ( str == NULL) { @@ -89,7 +89,7 @@ static size_t oneds_strnlen_s(const char *str, size_t strsz) // - count is greater than RSIZE_MAX // - count is greater or equal destsz, but destsz is less or equal strnlen_s(src, count), in other words, truncation would occur // - overlap would occur between the source and the destination strings -static errno_t oneds_strncpy_s(char * restrict dest, rsize_t destsz, const char *restrict src, rsize_t count) +static errno_t oneds_strncpy_s(char * restrict dest, rsize_t destsz, const char *restrict src, rsize_t count) noexcept { #if (defined __STDC_LIB_EXT1__) || ( defined _MSC_VER) return strncpy_s(dest, destsz, src, count); @@ -148,7 +148,7 @@ static errno_t oneds_strncpy_s(char * restrict dest, rsize_t destsz, const char // (if both dest and destsz are valid)) static errno_t oneds_memcpy_s( void *restrict dest, rsize_t destsz, - const void *restrict src, rsize_t count ) + const void *restrict src, rsize_t count ) noexcept { #if (defined __STDC_LIB_EXT1__) || ( defined _MSC_VER) return memcpy_s(dest, destsz, src, count); diff --git a/tests/common/Common.cpp b/tests/common/Common.cpp index 006b0c13a..c20f06b5c 100644 --- a/tests/common/Common.cpp +++ b/tests/common/Common.cpp @@ -273,7 +273,7 @@ namespace testing { return fname; } - void LogMemUsage(const char* label) + void LogMemUsage(const char* label) noexcept { #ifdef DEBUG_PERF #ifdef _WIN32 @@ -300,7 +300,7 @@ namespace testing { #endif } - void LogCpuUsage(const char* label) + void LogCpuUsage(const char* label) noexcept { #ifdef DEBUG_PERF static int64_t lastTime = GetUptimeMs(); diff --git a/tests/common/Common.hpp b/tests/common/Common.hpp index 754040ff8..da7f2903e 100644 --- a/tests/common/Common.hpp +++ b/tests/common/Common.hpp @@ -75,9 +75,9 @@ namespace testing { LogMemUsage(label); \ LogCpuUsage(label); - void LogMemUsage(const char* label); + void LogMemUsage(const char* label) noexcept; - void LogCpuUsage(const char* label); + void LogCpuUsage(const char* label) noexcept; void InflateVector(std::vector &in, std::vector &out, bool isGzip = false); } // namespace testing diff --git a/tests/common/MockIRuntimeConfig.hpp b/tests/common/MockIRuntimeConfig.hpp index 4a7509c84..a52ef8e8d 100644 --- a/tests/common/MockIRuntimeConfig.hpp +++ b/tests/common/MockIRuntimeConfig.hpp @@ -19,7 +19,7 @@ namespace testing { class MockIRuntimeConfig : public MAT::RuntimeConfig_Default /* MAT::IRuntimeConfig */ { protected: - std::unique_ptr& GetStaticConfig() + std::unique_ptr& GetStaticConfig() noexcept { static std::unique_ptr staticConfig; return staticConfig; diff --git a/tests/functests/MultipleLogManagersTests.cpp b/tests/functests/MultipleLogManagersTests.cpp index 9c7c833a7..040a6c9fc 100644 --- a/tests/functests/MultipleLogManagersTests.cpp +++ b/tests/functests/MultipleLogManagersTests.cpp @@ -39,7 +39,7 @@ using namespace MAT; class RequestHandler : public HttpServer::Callback { public: - RequestHandler(int id) : m_count(0), m_id(id){} + RequestHandler(int id) noexcept: m_count(0), m_id(id){} int onHttpRequest(HttpServer::Request const& request, HttpServer::Response& /*response*/) override { From 2ac4ecc7bc89b511200b8544ac69c7a145eb6299 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Tue, 16 Jun 2026 20:18:37 -0500 Subject: [PATCH 2/5] LogSessionDataProvider: harden convertStrToLong (reset errno, reject negatives) Addresses the Copilot low-confidence (suppressed) review note on convertStrToLong: - strtoll's errno was checked without being reset first, so a stale errno from an earlier call could trip false "conversion failed" handling. errno is now cleared before the call. - a negative input wrapped silently into a large uint64_t. The value is now parsed into a signed temp and rejected (returns 0 + warns) if negative. - the imprecise res==LONG_MAX overflow heuristic is replaced by a direct errno==ERANGE check plus an explicit no-conversion / trailing-character check. noexcept is preserved (no throwing operations). Pre-existing logic, folded in here since this PR already annotates the same function. Verified: NDK aarch64-linux-android23 -fsyntax-only clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/offline/LogSessionDataProvider.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/offline/LogSessionDataProvider.cpp b/lib/offline/LogSessionDataProvider.cpp index 4155c3d54..e18e32139 100644 --- a/lib/offline/LogSessionDataProvider.cpp +++ b/lib/offline/LogSessionDataProvider.cpp @@ -157,21 +157,24 @@ namespace MAT_NS_BEGIN { uint64_t res = 0ull; char *endptr = nullptr; - res = std::strtoll(s.c_str(), &endptr, 10); - if (errno == ERANGE && (res == LONG_MAX || res == 0 )) + errno = 0; + long long parsed = std::strtoll(s.c_str(), &endptr, 10); + if (errno == ERANGE) { - LOG_WARN ("Converted value falls out of uint64_t range."); - res = 0; - } - else if ( 0 != errno && 0 == res ) + LOG_WARN ("Converted value falls out of range."); + } + else if (endptr == s.c_str() || std::strlen(endptr) > 0) { - LOG_WARN("Conversion cannot be performed."); + LOG_WARN ("Conversion cannot be performed."); } - else if (std::strlen(endptr) > 0) + else if (parsed < 0) { - LOG_WARN ("Conversion cannot be performed. Alphanumeric characters present"); - res = 0; - } + LOG_WARN ("Converted value is negative; rejecting."); + } + else + { + res = static_cast(parsed); + } return res; } From c58c82e4aeb7d177d4147d599aaf5cfacee1e337 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 01:23:01 -0500 Subject: [PATCH 3/5] LogSessionDataProvider: parse with strtoull to match uint64_t return type Follow-up to the Copilot review on convertStrToLong: the function returns uint64_t but parsed via std::strtoll (signed long long), which constrains the accepted range to LLONG_MAX and mixes signed/unsigned. Switch to std::strtoull so parsing matches the return type and the full uint64_t range is accepted. strtoull silently wraps a leading '-', so negatives are now rejected explicitly (first non-space char check) before parsing, preserving the earlier negative-rejection behavior. noexcept preserved. Verified: NDK aarch64-linux-android23 -fsyntax-only clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/offline/LogSessionDataProvider.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/offline/LogSessionDataProvider.cpp b/lib/offline/LogSessionDataProvider.cpp index e18e32139..d0c051e70 100644 --- a/lib/offline/LogSessionDataProvider.cpp +++ b/lib/offline/LogSessionDataProvider.cpp @@ -157,8 +157,16 @@ namespace MAT_NS_BEGIN { uint64_t res = 0ull; char *endptr = nullptr; + // strtoull silently wraps a leading '-' into a large value, so reject + // negative input explicitly before parsing. + size_t firstNonSpace = s.find_first_not_of(" \t\n\r\f\v"); + if (firstNonSpace != std::string::npos && s[firstNonSpace] == '-') + { + LOG_WARN ("Converted value is negative; rejecting."); + return 0; + } errno = 0; - long long parsed = std::strtoll(s.c_str(), &endptr, 10); + unsigned long long parsed = std::strtoull(s.c_str(), &endptr, 10); if (errno == ERANGE) { LOG_WARN ("Converted value falls out of range."); @@ -167,10 +175,6 @@ namespace MAT_NS_BEGIN { LOG_WARN ("Conversion cannot be performed."); } - else if (parsed < 0) - { - LOG_WARN ("Converted value is negative; rejecting."); - } else { res = static_cast(parsed); From 39f6646efc8e76b9a136d012b752a5c9fbbacf79 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 02:00:55 -0500 Subject: [PATCH 4/5] LogSessionDataProvider: mark remove_eol noexcept remove_eol only inspects and shrinks the string in place (empty(), operator[], length(), and erase() at a validated position) -- none of which allocate or throw -- so it is genuinely non-throwing. Extends this PR's noexcept coverage to the one remaining sibling helper in the session-data classes that is safely non-throwing (parse()/writeFileContents()/the std::string ctor allocate, so they correctly stay un-annotated). Verified NDK aarch64-linux-android23 -fsyntax-only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/offline/LogSessionDataProvider.cpp | 2 +- lib/offline/LogSessionDataProvider.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/offline/LogSessionDataProvider.cpp b/lib/offline/LogSessionDataProvider.cpp index d0c051e70..1c0523810 100644 --- a/lib/offline/LogSessionDataProvider.cpp +++ b/lib/offline/LogSessionDataProvider.cpp @@ -200,7 +200,7 @@ namespace MAT_NS_BEGIN } } - void LogSessionDataProvider::remove_eol(std::string& result) + void LogSessionDataProvider::remove_eol(std::string& result) noexcept { if (!result.empty() && result[result.length() - 1] == '\n') { diff --git a/lib/offline/LogSessionDataProvider.hpp b/lib/offline/LogSessionDataProvider.hpp index 6cb47192f..3b45a9fc8 100644 --- a/lib/offline/LogSessionDataProvider.hpp +++ b/lib/offline/LogSessionDataProvider.hpp @@ -57,7 +57,7 @@ namespace MAT_NS_BEGIN std::unique_ptr m_logSessionData; static uint64_t convertStrToLong(const std::string&) noexcept; static void writeFileContents(const std::string&, uint64_t, const std::string&); - void remove_eol(std::string& ); + void remove_eol(std::string& ) noexcept; }; } MAT_NS_END From f495b47b06756b064b11a530adaa47ef6d278cdc Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 17:40:15 -0500 Subject: [PATCH 5/5] EventProperties: add noexcept move constructor and move assignment EventProperties is pimpl (single EventPropertiesStorage* m_storage) with a user-declared copy ctor/assign + virtual dtor, so the compiler generated no move operations -- every pass/return/vector-growth deep-copied the whole property map via `new EventPropertiesStorage(*copy.m_storage)`. Add O(1) noexcept move ctor + move assignment that transfer the storage pointer. The dtor is already null-safe (delete nullptr); copy-assignment is now also null-safe so a moved-from object can be reassigned. Backward-compatible API addition (the single-pointer layout is unchanged). Verified: NDK aarch64-linux-android23 -fsyntax-only clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/include/public/EventProperties.hpp | 12 ++++++++++++ lib/system/EventProperties.cpp | 27 +++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/include/public/EventProperties.hpp b/lib/include/public/EventProperties.hpp index a2aa3ecda..ef7f3b705 100644 --- a/lib/include/public/EventProperties.hpp +++ b/lib/include/public/EventProperties.hpp @@ -60,6 +60,18 @@ namespace MAT_NS_BEGIN /// EventProperties& operator=(EventProperties const& copy); + /// + /// The EventProperties move constructor. Transfers ownership of the + /// underlying storage (O(1)); the moved-from object is left empty and is + /// only valid to destroy or reassign. + /// + EventProperties(EventProperties&& move) noexcept; + + /// + /// The EventProperties move-assignment operator. + /// + EventProperties& operator=(EventProperties&& move) noexcept; + /// /// Constructs an EventProperties object from a map of string to EventProperty.
/// You must supply a non-empty name whenever you supply any custom properties for the event via EventProperties. diff --git a/lib/system/EventProperties.cpp b/lib/system/EventProperties.cpp index ebe62ff58..2ade77741 100644 --- a/lib/system/EventProperties.cpp +++ b/lib/system/EventProperties.cpp @@ -90,8 +90,33 @@ namespace MAT_NS_BEGIN { EventProperties& EventProperties::operator=(EventProperties const& copy) { - *m_storage = *copy.m_storage; + // m_storage may be null if this object was moved-from; reallocate then. + if (m_storage == nullptr) + { + m_storage = new EventPropertiesStorage(*copy.m_storage); + } + else + { + *m_storage = *copy.m_storage; + } + + return *this; + } + + EventProperties::EventProperties(EventProperties&& move) noexcept + : m_storage(move.m_storage) + { + move.m_storage = nullptr; + } + EventProperties& EventProperties::operator=(EventProperties&& move) noexcept + { + if (this != &move) + { + delete m_storage; + m_storage = move.m_storage; + move.m_storage = nullptr; + } return *this; }