Skip to content

Commit 9cae1c5

Browse files
committed
Update DelegateMQ library
1 parent 88e42ec commit 9cae1c5

File tree

17 files changed

+358
-450
lines changed

17 files changed

+358
-450
lines changed

DelegateMQ/External.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ endif()
2626
if(DMQ_TRANSPORT STREQUAL "DMQ_TRANSPORT_NNG")
2727
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
2828
set_and_check(NNG_INCLUDE_DIR "${DMQ_ROOT_DIR}/../../../nng/include")
29+
set_and_check(NNG_LIBRARY_DIR "${DMQ_ROOT_DIR}/../../../nng/install/lib")
2930
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
3031
set_and_check(NNG_INCLUDE_DIR "/usr/local/include/nng")
32+
set_and_check(NNG_LIBRARY_DIR "/usr/local/include/nng")
3133
endif()
3234
endif()
3335

DelegateMQ/delegate/Delegate.h

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -322,25 +322,27 @@ class DelegateMember<TClass, RetType(Args...)> : public Delegate<RetType(Args...
322322
m_func = reinterpret_cast<MemberFunc>(func);
323323
}
324324

325-
/// @brief Bind a member function to the delegate.
326-
/// @details This method associates a member function (`func`) with the delegate.
325+
/// @brief Bind a member function to a raw pointer.
326+
/// @details Wraps the raw object pointer in a `std::shared_ptr` with a no-op
327+
/// deleter, ensuring the delegate references the object without taking ownership
328+
/// or attempting to delete it. The caller must ensure the object outlives the delegate.
327329
/// Once the function is bound, the delegate can be used to invoke the function.
328-
/// @param[in] object The target object instance.
329-
/// @param[in] func The member function to bind to the delegate. This function must
330-
/// match the signature of the delegate.
330+
/// @param[in] object The target object instance (raw pointer).
331+
/// @param[in] func The const member function to bind.
331332
void Bind(ObjectPtr object, MemberFunc func) {
332333
static_assert(!std::is_const<TClass>::value, "Cannot bind non-const function to const object.");
333334
auto deleter = [](TClass*) {}; // No-op deleter
334335
m_object = std::shared_ptr<TClass>(object, deleter); // Not deleted when out of scope
335336
m_func = func;
336337
}
337338

338-
/// @brief Bind a const member function to the delegate.
339-
/// @details This method associates a member function (`func`) with the delegate.
339+
/// @brief Bind a const member function to a raw pointer.
340+
/// @details Wraps the raw object pointer in a `std::shared_ptr` with a no-op
341+
/// deleter, ensuring the delegate references the object without taking ownership
342+
/// or attempting to delete it. The caller must ensure the object outlives the delegate.
340343
/// Once the function is bound, the delegate can be used to invoke the function.
341-
/// @param[in] object The target object instance.
342-
/// @param[in] func The function to bind to the delegate. The member function to
343-
/// bind to the delegate. This function must match the signature of the delegate.
344+
/// @param[in] object The target object instance (raw pointer).
345+
/// @param[in] func The const member function to bind.
344346
void Bind(ObjectPtr object, ConstMemberFunc func) {
345347
auto deleter = [](TClass*) {}; // No-op deleter
346348
m_object = std::shared_ptr<TClass>(object, deleter); // Not deleted when out of scope

DelegateMQ/delegate/DelegateAsync.h

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
/// * `std::function` compares the function signature type, not the underlying object instance.
4343
/// See `DelegateFunction<>` class for more info.
4444
///
45-
/// Code within `<common_code>` and `</common_code>` is updated using sync_src.py. Manually update
45+
/// Code within `<common_code>` and `</common_code>` is updated using src_dup.py. Manually update
4646
/// the code within the `DelegateFreeAsync` `common_code` tags, then run the script to
4747
/// propagate to the remaining delegate classes to simplify code maintenance.
4848
///
@@ -72,6 +72,17 @@ namespace trait
7272

7373
template <typename T>
7474
struct is_shared_ptr_reference<const std::shared_ptr<T>* > : std::true_type {};
75+
76+
// Helper trait to check if a type is a double pointer (e.g., int**)
77+
template <typename T>
78+
struct is_double_pointer {
79+
// Remove 'const', 'volatile', and references first
80+
using RawT = std::remove_cv_t<std::remove_reference_t<T>>;
81+
82+
static constexpr bool value =
83+
std::is_pointer_v<RawT> &&
84+
std::is_pointer_v<std::remove_pointer_t<RawT>>;
85+
};
7586
}
7687

7788
/// @brief Stores all function arguments suitable for non-blocking asynchronous calls.
@@ -153,7 +164,7 @@ class DelegateFreeAsync<RetType(Args...)> : public DelegateFree<RetType(Args...)
153164
/// @brief Move constructor that transfers ownership of resources.
154165
/// @param[in] rhs The object to move from.
155166
DelegateFreeAsync(ClassType&& rhs) noexcept :
156-
BaseType(rhs), m_thread(rhs.m_thread), m_priority(rhs.m_priority) {
167+
BaseType(std::move(rhs)), m_thread(rhs.m_thread), m_priority(rhs.m_priority) {
157168
rhs.Clear();
158169
}
159170

@@ -210,6 +221,7 @@ class DelegateFreeAsync<RetType(Args...)> : public DelegateFree<RetType(Args...)
210221
BaseType::operator=(std::move(rhs));
211222
m_thread = rhs.m_thread; // Use the resource
212223
m_priority = rhs.m_priority;
224+
rhs.Clear();
213225
}
214226
return *this;
215227
}
@@ -427,13 +439,13 @@ class DelegateMemberAsync<TClass, RetType(Args...)> : public DelegateMember<TCla
427439
/// @brief Move constructor that transfers ownership of resources.
428440
/// @param[in] rhs The object to move from.
429441
DelegateMemberAsync(ClassType&& rhs) noexcept :
430-
BaseType(rhs), m_thread(rhs.m_thread), m_priority(rhs.m_priority) {
442+
BaseType(std::move(rhs)), m_thread(rhs.m_thread), m_priority(rhs.m_priority) {
431443
rhs.Clear();
432444
}
433445

434446
DelegateMemberAsync() = default;
435447

436-
/// @brief Bind a const member function to the delegate.
448+
/// @brief Bind a member function to the delegate.
437449
/// @details This method associates a member function (`func`) with the delegate.
438450
/// Once the function is bound, the delegate can be used to invoke the function.
439451
/// @param[in] object The target object instance.
@@ -445,7 +457,7 @@ class DelegateMemberAsync<TClass, RetType(Args...)> : public DelegateMember<TCla
445457
BaseType::Bind(object, func);
446458
}
447459

448-
/// @brief Bind a member function to the delegate.
460+
/// @brief Bind a const member function to the delegate.
449461
/// @details This method associates a member function (`func`) with the delegate.
450462
/// Once the function is bound, the delegate can be used to invoke the function.
451463
/// @param[in] object The target object instance.
@@ -457,7 +469,7 @@ class DelegateMemberAsync<TClass, RetType(Args...)> : public DelegateMember<TCla
457469
BaseType::Bind(object, func);
458470
}
459471

460-
/// @brief Bind a const member function to the delegate.
472+
/// @brief Bind a member function to the delegate.
461473
/// @details This method associates a member function (`func`) with the delegate.
462474
/// Once the function is bound, the delegate can be used to invoke the function.
463475
/// @param[in] object The target object instance.
@@ -469,7 +481,7 @@ class DelegateMemberAsync<TClass, RetType(Args...)> : public DelegateMember<TCla
469481
BaseType::Bind(object, func);
470482
}
471483

472-
/// @brief Bind a member function to the delegate.
484+
/// @brief Bind a const member function to the delegate.
473485
/// @details This method associates a member function (`func`) with the delegate.
474486
/// Once the function is bound, the delegate can be used to invoke the function.
475487
/// @param[in] object The target object instance.
@@ -521,6 +533,7 @@ class DelegateMemberAsync<TClass, RetType(Args...)> : public DelegateMember<TCla
521533
BaseType::operator=(std::move(rhs));
522534
m_thread = rhs.m_thread; // Use the resource
523535
m_priority = rhs.m_priority;
536+
rhs.Clear();
524537
}
525538
return *this;
526539
}
@@ -716,7 +729,7 @@ class DelegateFunctionAsync<RetType(Args...)> : public DelegateFunction<RetType(
716729
/// @brief Move constructor that transfers ownership of resources.
717730
/// @param[in] rhs The object to move from.
718731
DelegateFunctionAsync(ClassType&& rhs) noexcept :
719-
BaseType(rhs), m_thread(rhs.m_thread), m_priority(rhs.m_priority) {
732+
BaseType(std::move(rhs)), m_thread(rhs.m_thread), m_priority(rhs.m_priority) {
720733
rhs.Clear();
721734
}
722735

@@ -773,6 +786,7 @@ class DelegateFunctionAsync<RetType(Args...)> : public DelegateFunction<RetType(
773786
BaseType::operator=(std::move(rhs));
774787
m_thread = rhs.m_thread; // Use the resource
775788
m_priority = rhs.m_priority;
789+
rhs.Clear();
776790
}
777791
return *this;
778792
}

DelegateMQ/delegate/DelegateAsyncWait.h

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
/// * `std::function` compares the function signature type, not the underlying object instance.
4949
/// See `DelegateFunction<>` class for more info.
5050
///
51-
/// Code within `<common_code>` and `</common_code>` is updated using sync_src.py. Manually update
51+
/// Code within `<common_code>` and `</common_code>` is updated using src_dup.py. Manually update
5252
/// the code within the `DelegateFreeAsyncWait` `common_code` tags, then run the script to
5353
/// propagate to the remaining delegate classes to simplify code maintenance.
5454
///
@@ -172,7 +172,7 @@ class DelegateFreeAsyncWait<RetType(Args...)> : public DelegateFree<RetType(Args
172172
/// @brief Move constructor that transfers ownership of resources.
173173
/// @param[in] rhs The object to move from.
174174
DelegateFreeAsyncWait(ClassType&& rhs) noexcept :
175-
BaseType(rhs), m_thread(rhs.m_thread), m_priority(rhs.m_priority), m_timeout(rhs.m_timeout), m_success(rhs.m_success), m_retVal(rhs.m_retVal) {
175+
BaseType(std::move(rhs)), m_thread(rhs.m_thread), m_priority(rhs.m_priority), m_timeout(rhs.m_timeout), m_success(rhs.m_success), m_retVal(rhs.m_retVal) {
176176
rhs.Clear();
177177
}
178178

@@ -302,7 +302,7 @@ class DelegateFreeAsyncWait<RetType(Args...)> : public DelegateFree<RetType(Args
302302
/// Use `IsSuccess()` to check for success before using the return value. Alternatively,
303303
/// use `AsyncInvoke()` and check the `std::optional` return value.
304304
///
305-
/// The `DelegateAsyncWaitMsg` does not duplicated and copy the function arguments into heap
305+
/// The `DelegateAsyncWaitMsg` does not duplicate and copy the function arguments into heap
306306
/// memory. The source thread waits on the destintation thread to complete, therefore argument
307307
/// data is shared between the source and destination threads and simultaneous access is prevented
308308
/// using a mutex.
@@ -338,8 +338,12 @@ class DelegateFreeAsyncWait<RetType(Args...)> : public DelegateFree<RetType(Args
338338
thread->DispatchDelegate(msg);
339339

340340
// Wait for destination thread to execute the delegate function and get return value
341-
if ((m_success = msg->GetSema().Wait(m_timeout)))
341+
if (msg->GetSema().Wait(m_timeout)) {
342+
// Wait succeeded. Now acquire lock to safely read the value.
343+
const std::lock_guard<std::mutex> lock(msg->GetLock());
344+
m_success = true;
342345
m_retVal = delegate->m_retVal;
346+
}
343347
}
344348

345349
// Protect data shared between source and destination threads
@@ -542,13 +546,13 @@ class DelegateMemberAsyncWait<TClass, RetType(Args...)> : public DelegateMember<
542546
/// @brief Move constructor that transfers ownership of resources.
543547
/// @param[in] rhs The object to move from.
544548
DelegateMemberAsyncWait(ClassType&& rhs) noexcept :
545-
BaseType(rhs), m_thread(rhs.m_thread), m_priority(rhs.m_priority), m_timeout(rhs.m_timeout), m_success(rhs.m_success), m_retVal(rhs.m_retVal) {
549+
BaseType(std::move(rhs)), m_thread(rhs.m_thread), m_priority(rhs.m_priority), m_timeout(rhs.m_timeout), m_success(rhs.m_success), m_retVal(rhs.m_retVal) {
546550
rhs.Clear();
547551
}
548552

549553
DelegateMemberAsyncWait() = default;
550554

551-
/// @brief Bind a const member function to the delegate.
555+
/// @brief Bind a member function to the delegate.
552556
/// @details This method associates a member function (`func`) with the delegate.
553557
/// Once the function is bound, the delegate can be used to invoke the function.
554558
/// @param[in] object The target object instance.
@@ -563,7 +567,7 @@ class DelegateMemberAsyncWait<TClass, RetType(Args...)> : public DelegateMember<
563567
BaseType::Bind(object, func);
564568
}
565569

566-
/// @brief Bind a member function to the delegate.
570+
/// @brief Bind a const member function to the delegate.
567571
/// @details This method associates a member function (`func`) with the delegate.
568572
/// Once the function is bound, the delegate can be used to invoke the function.
569573
/// @param[in] object The target object instance.
@@ -578,7 +582,7 @@ class DelegateMemberAsyncWait<TClass, RetType(Args...)> : public DelegateMember<
578582
BaseType::Bind(object, func);
579583
}
580584

581-
/// @brief Bind a const member function to the delegate.
585+
/// @brief Bind a member function to the delegate.
582586
/// @details This method associates a member function (`func`) with the delegate.
583587
/// Once the function is bound, the delegate can be used to invoke the function.
584588
/// @param[in] object The target object instance.
@@ -593,7 +597,7 @@ class DelegateMemberAsyncWait<TClass, RetType(Args...)> : public DelegateMember<
593597
BaseType::Bind(object, func);
594598
}
595599

596-
/// @brief Bind a member function to the delegate.
600+
/// @brief Bind a const member function to the delegate.
597601
/// @details This method associates a member function (`func`) with the delegate.
598602
/// Once the function is bound, the delegate can be used to invoke the function.
599603
/// @param[in] object The target object instance.
@@ -718,7 +722,7 @@ class DelegateMemberAsyncWait<TClass, RetType(Args...)> : public DelegateMember<
718722
/// Use `IsSuccess()` to check for success before using the return value. Alternatively,
719723
/// use `AsyncInvoke()` and check the `std::optional` return value.
720724
///
721-
/// The `DelegateAsyncWaitMsg` does not duplicated and copy the function arguments into heap
725+
/// The `DelegateAsyncWaitMsg` does not duplicate and copy the function arguments into heap
722726
/// memory. The source thread waits on the destintation thread to complete, therefore argument
723727
/// data is shared between the source and destination threads and simultaneous access is prevented
724728
/// using a mutex.
@@ -754,8 +758,12 @@ class DelegateMemberAsyncWait<TClass, RetType(Args...)> : public DelegateMember<
754758
thread->DispatchDelegate(msg);
755759

756760
// Wait for destination thread to execute the delegate function and get return value
757-
if ((m_success = msg->GetSema().Wait(m_timeout)))
761+
if (msg->GetSema().Wait(m_timeout)) {
762+
// Wait succeeded. Now acquire lock to safely read the value.
763+
const std::lock_guard<std::mutex> lock(msg->GetLock());
764+
m_success = true;
758765
m_retVal = delegate->m_retVal;
766+
}
759767
}
760768

761769
// Protect data shared between source and destination threads
@@ -923,7 +931,7 @@ class DelegateFunctionAsyncWait<RetType(Args...)> : public DelegateFunction<RetT
923931
/// @brief Move constructor that transfers ownership of resources.
924932
/// @param[in] rhs The object to move from.
925933
DelegateFunctionAsyncWait(ClassType&& rhs) noexcept :
926-
BaseType(rhs), m_thread(rhs.m_thread), m_priority(rhs.m_priority), m_timeout(rhs.m_timeout), m_success(rhs.m_success), m_retVal(rhs.m_retVal) {
934+
BaseType(std::move(rhs)), m_thread(rhs.m_thread), m_priority(rhs.m_priority), m_timeout(rhs.m_timeout), m_success(rhs.m_success), m_retVal(rhs.m_retVal) {
927935
rhs.Clear();
928936
}
929937

@@ -1053,7 +1061,7 @@ class DelegateFunctionAsyncWait<RetType(Args...)> : public DelegateFunction<RetT
10531061
/// Use `IsSuccess()` to check for success before using the return value. Alternatively,
10541062
/// use `AsyncInvoke()` and check the `std::optional` return value.
10551063
///
1056-
/// The `DelegateAsyncWaitMsg` does not duplicated and copy the function arguments into heap
1064+
/// The `DelegateAsyncWaitMsg` does not duplicate and copy the function arguments into heap
10571065
/// memory. The source thread waits on the destintation thread to complete, therefore argument
10581066
/// data is shared between the source and destination threads and simultaneous access is prevented
10591067
/// using a mutex.
@@ -1089,8 +1097,12 @@ class DelegateFunctionAsyncWait<RetType(Args...)> : public DelegateFunction<RetT
10891097
thread->DispatchDelegate(msg);
10901098

10911099
// Wait for destination thread to execute the delegate function and get return value
1092-
if ((m_success = msg->GetSema().Wait(m_timeout)))
1100+
if (msg->GetSema().Wait(m_timeout)) {
1101+
// Wait succeeded. Now acquire lock to safely read the value.
1102+
const std::lock_guard<std::mutex> lock(msg->GetLock());
1103+
m_success = true;
10931104
m_retVal = delegate->m_retVal;
1105+
}
10941106
}
10951107

10961108
// Protect data shared between source and destination threads

0 commit comments

Comments
 (0)