diff --git a/README.md b/README.md index ca80a6c..48fa7c4 100644 --- a/README.md +++ b/README.md @@ -554,6 +554,25 @@ See [`example/nested_demo.cpp`](rclcpp_async/example/nested_demo.cpp) for a full | `post(fn)` | `void` | Post a callback to the executor thread (thread-safe) | | `node()` | `Node::SharedPtr` | Access the underlying node | +### Task + +| Method | Returns | Description | +|---|---|---| +| `operator bool()` | `bool` | `true` if the task holds a valid coroutine (not moved-from) | +| `done()` | `bool` | `true` if the coroutine has completed (`false` for null tasks) | +| `cancel()` | `void` | Request cancellation via `CancelledException` | + +```cpp +auto task = ctx.create_task(run(ctx)); + +if (task) { // true -- valid coroutine + // ... +} +if (task.done()) { + // coroutine has finished +} +``` + ## License Apache-2.0 diff --git a/rclcpp_async/include/rclcpp_async/task.hpp b/rclcpp_async/include/rclcpp_async/task.hpp index bab2a66..7ced538 100644 --- a/rclcpp_async/include/rclcpp_async/task.hpp +++ b/rclcpp_async/include/rclcpp_async/task.hpp @@ -118,6 +118,9 @@ struct Task void cancel() { handle.promise().stop_source.request_stop(); } + explicit operator bool() const noexcept { return handle != nullptr; } + bool done() const noexcept { return handle && handle.done(); } + ~Task() { if (handle) { @@ -220,6 +223,9 @@ struct Task void cancel() { handle.promise().stop_source.request_stop(); } + explicit operator bool() const noexcept { return handle != nullptr; } + bool done() const noexcept { return handle && handle.done(); } + ~Task() { if (handle) { diff --git a/rclcpp_async/test/test_task.cpp b/rclcpp_async/test/test_task.cpp index e44b3f4..c190328 100644 --- a/rclcpp_async/test/test_task.cpp +++ b/rclcpp_async/test/test_task.cpp @@ -252,3 +252,38 @@ TEST(TaskT, AwaitLazyStillWorks) ASSERT_TRUE(task.handle.done()); EXPECT_EQ(*task.handle.promise().value, 43); } + +TEST(TaskT, OperatorBoolAndDone) +{ + auto task = returns_42(); + EXPECT_TRUE(static_cast(task)); + EXPECT_FALSE(task.done()); + + task.handle.resume(); + EXPECT_TRUE(static_cast(task)); + EXPECT_TRUE(task.done()); + + // After move, source becomes null + auto task2 = std::move(task); + EXPECT_FALSE(static_cast(task)); + EXPECT_FALSE(task.done()); + EXPECT_TRUE(static_cast(task2)); + EXPECT_TRUE(task2.done()); +} + +TEST(TaskVoid, OperatorBoolAndDone) +{ + auto task = does_nothing(); + EXPECT_TRUE(static_cast(task)); + EXPECT_FALSE(task.done()); + + task.handle.resume(); + EXPECT_TRUE(static_cast(task)); + EXPECT_TRUE(task.done()); + + auto task2 = std::move(task); + EXPECT_FALSE(static_cast(task)); + EXPECT_FALSE(task.done()); + EXPECT_TRUE(static_cast(task2)); + EXPECT_TRUE(task2.done()); +}