diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp index 565e5adc57..b3f8f2f12e 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/CommandPtr.cpp @@ -4,6 +4,8 @@ #include "frc2/command/CommandPtr.h" +#include + #include "frc2/command/CommandScheduler.h" #include "frc2/command/ConditionalCommand.h" #include "frc2/command/InstantCommand.h" @@ -20,12 +22,21 @@ using namespace frc2; +void CommandPtr::AssertValid() const { + if (!m_ptr) { + throw FRC_MakeError(frc::err::CommandIllegalUse, + "Moved-from CommandPtr object used!"); + } +} + CommandPtr CommandPtr::Repeatedly() && { + AssertValid(); m_ptr = std::make_unique(std::move(m_ptr)); return std::move(*this); } CommandPtr CommandPtr::AsProxy() && { + AssertValid(); m_ptr = std::make_unique(std::move(m_ptr)); return std::move(*this); } @@ -44,6 +55,7 @@ class RunsWhenDisabledCommand : public WrapperCommand { }; CommandPtr CommandPtr::IgnoringDisable(bool doesRunWhenDisabled) && { + AssertValid(); m_ptr = std::make_unique(std::move(m_ptr), doesRunWhenDisabled); return std::move(*this); @@ -67,6 +79,7 @@ class InterruptBehaviorCommand : public WrapperCommand { CommandPtr CommandPtr::WithInterruptBehavior( InterruptionBehavior interruptBehavior) && { + AssertValid(); m_ptr = std::make_unique(std::move(m_ptr), interruptBehavior); return std::move(*this); @@ -74,6 +87,7 @@ CommandPtr CommandPtr::WithInterruptBehavior( CommandPtr CommandPtr::AndThen(std::function toRun, std::span requirements) && { + AssertValid(); return std::move(*this).AndThen(CommandPtr( std::make_unique(std::move(toRun), requirements))); } @@ -81,11 +95,13 @@ CommandPtr CommandPtr::AndThen(std::function toRun, CommandPtr CommandPtr::AndThen( std::function toRun, std::initializer_list requirements) && { + AssertValid(); return std::move(*this).AndThen(CommandPtr( std::make_unique(std::move(toRun), requirements))); } CommandPtr CommandPtr::AndThen(CommandPtr&& next) && { + AssertValid(); std::vector> temp; temp.emplace_back(std::move(m_ptr)); temp.emplace_back(std::move(next).Unwrap()); @@ -95,6 +111,7 @@ CommandPtr CommandPtr::AndThen(CommandPtr&& next) && { CommandPtr CommandPtr::BeforeStarting( std::function toRun, std::span requirements) && { + AssertValid(); return std::move(*this).BeforeStarting(CommandPtr( std::make_unique(std::move(toRun), requirements))); } @@ -102,11 +119,13 @@ CommandPtr CommandPtr::BeforeStarting( CommandPtr CommandPtr::BeforeStarting( std::function toRun, std::initializer_list requirements) && { + AssertValid(); return std::move(*this).BeforeStarting(CommandPtr( std::make_unique(std::move(toRun), requirements))); } CommandPtr CommandPtr::BeforeStarting(CommandPtr&& before) && { + AssertValid(); std::vector> temp; temp.emplace_back(std::move(before).Unwrap()); temp.emplace_back(std::move(m_ptr)); @@ -115,6 +134,7 @@ CommandPtr CommandPtr::BeforeStarting(CommandPtr&& before) && { } CommandPtr CommandPtr::WithTimeout(units::second_t duration) && { + AssertValid(); std::vector> temp; temp.emplace_back(std::make_unique(duration)); temp.emplace_back(std::move(m_ptr)); @@ -123,6 +143,7 @@ CommandPtr CommandPtr::WithTimeout(units::second_t duration) && { } CommandPtr CommandPtr::Until(std::function condition) && { + AssertValid(); std::vector> temp; temp.emplace_back(std::make_unique(std::move(condition))); temp.emplace_back(std::move(m_ptr)); @@ -131,6 +152,7 @@ CommandPtr CommandPtr::Until(std::function condition) && { } CommandPtr CommandPtr::Unless(std::function condition) && { + AssertValid(); m_ptr = std::make_unique( std::make_unique(), std::move(m_ptr), std::move(condition)); @@ -138,6 +160,7 @@ CommandPtr CommandPtr::Unless(std::function condition) && { } CommandPtr CommandPtr::DeadlineWith(CommandPtr&& parallel) && { + AssertValid(); std::vector> vec; vec.emplace_back(std::move(parallel).Unwrap()); m_ptr = @@ -146,6 +169,7 @@ CommandPtr CommandPtr::DeadlineWith(CommandPtr&& parallel) && { } CommandPtr CommandPtr::AlongWith(CommandPtr&& parallel) && { + AssertValid(); std::vector> vec; vec.emplace_back(std::move(m_ptr)); vec.emplace_back(std::move(parallel).Unwrap()); @@ -154,6 +178,7 @@ CommandPtr CommandPtr::AlongWith(CommandPtr&& parallel) && { } CommandPtr CommandPtr::RaceWith(CommandPtr&& parallel) && { + AssertValid(); std::vector> vec; vec.emplace_back(std::move(m_ptr)); vec.emplace_back(std::move(parallel).Unwrap()); @@ -179,11 +204,13 @@ class FinallyCommand : public WrapperCommand { } // namespace CommandPtr CommandPtr::FinallyDo(std::function end) && { + AssertValid(); m_ptr = std::make_unique(std::move(m_ptr), std::move(end)); return std::move(*this); } CommandPtr CommandPtr::HandleInterrupt(std::function handler) && { + AssertValid(); return std::move(*this).FinallyDo( [handler = std::move(handler)](bool interrupted) { if (interrupted) { @@ -193,29 +220,39 @@ CommandPtr CommandPtr::HandleInterrupt(std::function handler) && { } Command* CommandPtr::get() const { + AssertValid(); return m_ptr.get(); } std::unique_ptr CommandPtr::Unwrap() && { + AssertValid(); return std::move(m_ptr); } void CommandPtr::Schedule() const { + AssertValid(); CommandScheduler::GetInstance().Schedule(*this); } void CommandPtr::Cancel() const { + AssertValid(); CommandScheduler::GetInstance().Cancel(*this); } bool CommandPtr::IsScheduled() const { + AssertValid(); return CommandScheduler::GetInstance().IsScheduled(*this); } bool CommandPtr::HasRequirement(Subsystem* requirement) const { + AssertValid(); return m_ptr->HasRequirement(requirement); } +CommandPtr::operator bool() const { + return m_ptr.operator bool(); +} + std::vector> CommandPtr::UnwrapVector( std::vector&& vec) { std::vector> ptrs; diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h b/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h index 74663aa100..d849a4a284 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/CommandPtr.h @@ -259,6 +259,11 @@ class CommandPtr final { */ bool HasRequirement(Subsystem* requirement) const; + /** + * Check if this CommandPtr object is valid and wasn't moved-from. + */ + explicit operator bool() const; + /** * Convert a vector of CommandPtr objects to their underlying unique_ptrs. */ @@ -267,6 +272,7 @@ class CommandPtr final { private: std::unique_ptr m_ptr; + void AssertValid() const; }; } // namespace frc2 diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp new file mode 100644 index 0000000000..26077f2012 --- /dev/null +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandPtrTest.cpp @@ -0,0 +1,35 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include + +#include "CommandTestBase.h" +#include "frc2/command/CommandPtr.h" +#include "frc2/command/CommandScheduler.h" +#include "frc2/command/Commands.h" + +using namespace frc2; +class CommandPtrTest : public CommandTestBase {}; + +TEST_F(CommandPtrTest, MovedFrom) { + CommandScheduler scheduler = GetScheduler(); + + int counter = 0; + + CommandPtr movedFrom = cmd::Run([&counter] { counter++; }); + CommandPtr movedTo = std::move(movedFrom); + + EXPECT_NO_FATAL_FAILURE(scheduler.Schedule(movedTo)); + EXPECT_NO_FATAL_FAILURE(scheduler.Run()); + + EXPECT_EQ(1, counter); + EXPECT_NO_FATAL_FAILURE(scheduler.Cancel(movedTo)); + + EXPECT_THROW(scheduler.Schedule(movedFrom), frc::RuntimeError); + EXPECT_THROW(movedFrom.IsScheduled(), frc::RuntimeError); + EXPECT_THROW(static_cast(std::move(movedFrom).Repeatedly()), + frc::RuntimeError); + + EXPECT_EQ(1, counter); +}