Skip to content

Commit 9697022

Browse files
committed
Impove lambda handling
1 parent 2207312 commit 9697022

File tree

23 files changed

+334
-60
lines changed

23 files changed

+334
-60
lines changed

01_fetch_repos.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,27 @@ def run():
8787
workspace_dir = os.path.abspath(os.path.join(script_dir, ".."))
8888
print(f"Target Workspace: {workspace_dir}\n")
8989

90+
# Suppress 'detached HEAD' advice to keep output clean
91+
subprocess.run(["git", "config", "advice.detachedHead", "false"], check=False)
92+
9093
# 1. Clone Repos
9194
for folder, (url, tag, use_submodules) in repos.items():
9295
target_path = os.path.join(workspace_dir, folder)
9396
if not os.path.exists(target_path):
9497
print(f"[CLONING] {folder} (Tag: {tag if tag else 'HEAD'})...")
95-
cmd = ["git", "clone"]
98+
# Use -c advice.detachedHead=false to silence the warning during clone
99+
cmd = ["git", "-c", "advice.detachedHead=false", "clone", "--quiet"]
96100
if use_submodules: cmd.append("--recurse-submodules")
97101
if tag: cmd.extend(["--branch", tag])
98102
else: cmd.extend(["--depth", "1"])
99103
cmd.extend([url, target_path])
100-
try: subprocess.run(cmd, check=True)
101-
except: print(f" [ERROR] Failed to clone {folder}")
104+
try:
105+
# Capture stderr to suppress normal output; printed only on failure
106+
subprocess.run(cmd, check=True, stderr=subprocess.PIPE)
107+
except subprocess.CalledProcessError as e:
108+
print(f" [ERROR] Failed to clone {folder}")
109+
if e.stderr:
110+
print(e.stderr.decode(errors="replace"))
102111
else:
103112
print(f"[SKIP] {folder} exists.")
104113

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ public:
239239
MsgSender(ITransport& transport, ISerializer<void(std::string)>& ser)
240240
: m_channel(transport, ser)
241241
{
242-
m_channel.Bind(std::function<void(std::string)>([](std::string){}), MSG_ID);
242+
// Bind to a raw lambda (no std::function wrapper needed)
243+
m_channel.Bind([](std::string msg) { MsgOut(msg); }, MSG_ID);
243244
}
244245
245246
void Send(const std::string& msg) { m_channel(msg); } // fire-and-forget

docs/DETAILS.md

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -379,19 +379,19 @@ if (result.has_value())
379379

380380
### Lambdas
381381

382-
Lambdas work the same way. Use `+` to convert a stateless lambda to a function pointer, or wrap capturing lambdas in `std::function`.
382+
Lambdas work the same way. Pass any lambda directly to `MakeDelegate()` — no `std::function` wrapper or `+` conversion needed.
383383

384384
```cpp
385385
// Stateless lambda — synchronous
386-
auto echo = MakeDelegate(+[](const std::string& s) { std::cout << s; });
386+
auto echo = MakeDelegate([](const std::string& s) { std::cout << s; });
387387
echo("hello");
388388

389389
// Capturing lambda — asynchronous, fire-and-forget
390390
int threshold = 10;
391391
auto check = MakeDelegate(
392-
std::function<void(int)>([threshold](int v) {
392+
[threshold](int v) {
393393
if (v > threshold) std::cout << "Over threshold\n";
394-
}),
394+
},
395395
workerThread
396396
);
397397
check(15);
@@ -458,12 +458,12 @@ Button btn;
458458
459459
// Subscriber A: synchronous (called on the emitting thread)
460460
dmq::ScopedConnection connA = btn.OnPressed.Connect(
461-
MakeDelegate(+[](int id) { std::cout << "Sync: " << id; })
461+
MakeDelegate([](int id) { std::cout << "Sync: " << id; })
462462
);
463463
464464
// Subscriber B: asynchronous (called on workerThread)
465465
dmq::ScopedConnection connB = btn.OnPressed.Connect(
466-
MakeDelegate(+[](int id) { std::cout << "Async: " << id; }, workerThread)
466+
MakeDelegate([](int id) { std::cout << "Async: " << id; }, workerThread)
467467
);
468468
469469
btn.Press(1); // connA called synchronously, connB queued on workerThread
@@ -475,14 +475,14 @@ btn.Press(1); // connA called synchronously, connB queued on workerThread
475475
dmq::Signal<void(int)> OnData;
476476

477477
// Stateless lambda
478-
dmq::ScopedConnection c1 = OnData.Connect(MakeDelegate(+[](int v) {
478+
dmq::ScopedConnection c1 = OnData.Connect(MakeDelegate([](int v) {
479479
std::cout << "Got: " << v << "\n";
480480
}));
481481

482-
// Capturing lambda
482+
// Capturing lambda — no std::function wrapper needed
483483
int factor = 3;
484484
dmq::ScopedConnection c2 = OnData.Connect(MakeDelegate(
485-
std::function<void(int)>([factor](int v) { std::cout << v * factor; })
485+
[factor](int v) { std::cout << v * factor; }
486486
));
487487

488488
OnData(10); // both slots called
@@ -867,12 +867,10 @@ auto delegateMember = MakeDelegate(&testClass, &TestClass::MemberFunc);
867867
delegateMember(&testStruct);
868868
```
869869
870-
Bind a lambda function is easy using `std::function`.
870+
Bind a lambda directly — no `std::function` wrapper needed.
871871
872872
```cpp
873-
std::function<bool(int)> rangeLambda = MakeDelegate(
874-
+[](int v) { return v > 2 && v <= 6; }
875-
);
873+
auto rangeLambda = MakeDelegate([](int v) { return v > 2 && v <= 6; });
876874
bool inRange = rangeLambda(6);
877875
```
878876

@@ -988,14 +986,14 @@ Stack arguments passed by pointer/reference do not be thread-safe. The reason is
988986
A blocking delegate must specify a timeout in milliseconds or `WAIT_INFINITE`. Unlike a non-blocking asynchronous delegate, which is guaranteed to be invoked, if the timeout expires on a blocking delegate, the function is not invoked. Use `IsSuccess()` to determine if the delegate succeeded or not.
989987

990988
```cpp
991-
std::function LambdaFunc1 = [](int i) -> int
992-
{
993-
cout << "Called LambdaFunc1 " << i << std::endl;
994-
return ++i;
995-
};
996-
997989
// Asynchronously invoke lambda on workerThread1 and wait for the return value
998-
auto lambdaDelegate1 = MakeDelegate(LambdaFunc1, workerThread1, WAIT_INFINITE);
990+
auto lambdaDelegate1 = MakeDelegate(
991+
[](int i) -> int {
992+
cout << "Called LambdaFunc1 " << i << std::endl;
993+
return ++i;
994+
},
995+
workerThread1, WAIT_INFINITE
996+
);
999997
int lambdaRetVal2 = lambdaDelegate1(123);
1000998
```
1001999
@@ -1004,7 +1002,7 @@ Delegates are callable and therefore may be passed to the standard library. The
10041002
```cpp
10051003
std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
10061004
1007-
auto CountLambda = +[](int v) -> int
1005+
auto CountLambda = [](int v) -> int
10081006
{
10091007
return v > 2 && v <= 6;
10101008
};
@@ -1549,12 +1547,11 @@ void TestFunc(TestStruct* data);
15491547
Occasionally, you may not want the delegate library to copy your arguments. Instead, you just want the destination thread to have a pointer to the original copy. A `std::shared_ptr` function argument does not copy the object pointed to when using an asynchronous delegate target function.
15501548

15511549
```cpp
1552-
std::function lambFunc = [](std::shared_ptr<TestStruct> s) {};
1553-
auto del = MakeDelegate(lambFunc, workerThread1);
1550+
auto del = MakeDelegate([](std::shared_ptr<TestStruct> s) {}, workerThread1);
15541551
std::shared_ptr<TestStruct> sp = std::make_shared<TestStruct>();
15551552

1556-
// Invoke lambFunc and TestStruct instance is not copied
1557-
del(sp);
1553+
// Invoke lambda and TestStruct instance is not copied
1554+
del(sp);
15581555
```
15591556
15601557
### Array Argument Heap Copy

example/sample-code/AllTargets.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ namespace Example
4343
};
4444

4545
int stackVal = 100;
46-
std::function<void(int)> LambdaCapture = [stackVal](int i) {
46+
auto LambdaCapture = [stackVal](int i) {
4747
std::cout << "LambdaCapture " << i + stackVal << " " << ++callCnt << endl;
4848
};
4949

50-
std::function<void(int)> LambdaNoCapture = [](int i) {
50+
auto LambdaNoCapture = [](int i) {
5151
std::cout << "LambdaNoCapture " << i << " " << ++callCnt << endl;
5252
};
5353

54-
std::function<void(int)> LambdaForcedCapture = +[](int i) {
54+
auto LambdaForcedCapture = +[](int i) {
5555
std::cout << "LambdaForcedCapture " << i << " " << ++callCnt << endl;
5656
};
5757

example/sample-code/AsyncCallback.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,9 @@ namespace Example
164164
monitor->Init(sensor, callback_thread);
165165

166166
// 4. Setup Lambda (ScopedConnection)
167-
std::function<void(int, const std::string&)> lambdaFunc =
168-
[](int val, const std::string& src) {
167+
auto lambdaConnection = sensor.OnData.Connect(MakeDelegate([](int val, const std::string& src) {
169168
cout << "[Lambda] Got " << val << " on thread " << Thread::GetCurrentThreadId() << endl;
170-
};
171-
auto lambdaConnection = sensor.OnData.Connect(MakeDelegate(lambdaFunc, callback_thread));
169+
}, callback_thread));
172170

173171
// 5. Produce Data
174172
cout << "\n--- Producing Batch 1 ---" << endl;

example/sample-code/BindingProperty.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ namespace Example
5959
PropertyBinding() : workerThread("Example"), property1(0), property2(0), property3(0) {
6060
workerThread.CreateThread();
6161

62-
std::function<void(int)> lambda2 = [this](int new_value) {
62+
auto lambda2 = [this](int new_value) {
6363
// Whenever property1 changes, set property2 to double the value
6464
property2.set(new_value * 2);
6565
};
6666

6767
// Bind property2 to property1's changes (synchonous binding)
6868
property1.bind(MakeDelegate(lambda2));
6969

70-
std::function<void(int)> lambda3 = [this](int new_value) {
70+
auto lambda3 = [this](int new_value) {
7171
const std::lock_guard<std::mutex> lk(lock);
7272
// Whenever property1 changes, set property3 to triple the value
7373
property3.set(new_value * 3);

example/sample-code/Observer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ namespace Example
105105
subject.attach(MakeDelegate(observerB, &ConcreteObserverB::update));
106106
subject.attach(MakeDelegate(SubjectFree));
107107
subject.attach(MakeDelegate(SubjectLambda));
108-
subject.attach(MakeDelegate(std::function(SubjectLambda2)));
108+
subject.attach(MakeDelegate(SubjectLambda2));
109109

110110
// Change the subject's state and notify observers
111111
std::cout << "Setting state to 20:" << std::endl;

example/sample-code/RemoteCommunication.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ namespace Example
9191
, m_channel(transport, m_serializer)
9292
{
9393
// Sender-side bind: no-op target (only the signature matters for dispatch)
94-
m_channel.Bind(std::function<void(Data)>([](Data) {}), id);
94+
// Bind a raw lambda (no std::function wrapper needed)
95+
m_channel.Bind([](Data msg) { std::cout << "Remote lambda data: " << msg.x << std::endl; }, id);
9596

9697
m_thread.CreateThread();
9798

example/sample-projects/databus-shapes/client/main.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ int main() {
5555
static Serializer<void(ShapeMsg)> serializer;
5656

5757
auto shapeHandler = [](const std::string& topic, ShapeMsg msg) {
58-
// std::cout << "DEBUG: Received " << topic << " x=" << msg.x << " y=" << msg.y << std::endl;
5958
{
6059
std::lock_guard<std::mutex> lock(g_state.mutex);
6160
g_state.shapes[topic] = msg;
@@ -65,11 +64,9 @@ int main() {
6564
}
6665
};
6766

68-
// Use explicit std::function to match RegisterHandler signature
69-
using HandlerFunc = std::function<void(ShapeMsg)>;
70-
group->RegisterHandler<ShapeMsg>(SystemTopic::SquareId, serializer, HandlerFunc([&](ShapeMsg m) { shapeHandler(SystemTopic::Square, m); }));
71-
group->RegisterHandler<ShapeMsg>(SystemTopic::CircleId, serializer, HandlerFunc([&](ShapeMsg m) { shapeHandler(SystemTopic::Circle, m); }));
72-
group->RegisterHandler<ShapeMsg>(SystemTopic::TriangleId, serializer, HandlerFunc([&](ShapeMsg m) { shapeHandler(SystemTopic::Triangle, m); }));
67+
group->RegisterHandler<ShapeMsg>(SystemTopic::SquareId, serializer, [&](ShapeMsg m) { shapeHandler(SystemTopic::Square, m); });
68+
group->RegisterHandler<ShapeMsg>(SystemTopic::CircleId, serializer, [&](ShapeMsg m) { shapeHandler(SystemTopic::Circle, m); });
69+
group->RegisterHandler<ShapeMsg>(SystemTopic::TriangleId, serializer, [&](ShapeMsg m) { shapeHandler(SystemTopic::Triangle, m); });
7370

7471
// 3. Network processing thread
7572
std::atomic<bool> running{true};

example/sample-projects/databus/client/main.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,13 @@ int main() {
9090
dmq::DataBus::RegisterSerializer<CommandMsg>(SystemTopic::CommandMsg, commandSerializer);
9191

9292
// 7. Monitor reliability signal to detect server response status
93-
using StatusSignal = std::function<void(dmq::DelegateRemoteId, uint16_t, TransportMonitor::Status)>;
94-
auto statusConn = monitor.OnSendStatus.Connect(dmq::MakeDelegate(StatusSignal([](dmq::DelegateRemoteId id, uint16_t seq, TransportMonitor::Status status) {
93+
auto statusConn = monitor.OnSendStatus.Connect(dmq::MakeDelegate([](dmq::DelegateRemoteId id, uint16_t seq, TransportMonitor::Status status) {
9594
if (status == TransportMonitor::Status::TIMEOUT) {
9695
std::cerr << "!!! ALERT: Server not responding to command (RemoteID: " << id << " Seq: " << seq << ")" << std::endl;
9796
} else {
9897
std::cout << "Command ACK received (Seq: " << seq << ")" << std::endl;
9998
}
100-
})));
99+
}));
101100

102101
// 8. Local Subscription to DataBus
103102
auto conn = dmq::DataBus::Subscribe<DataMsg>(SystemTopic::DataMsg, [](DataMsg msg) {

0 commit comments

Comments
 (0)