[commands] C++ unique_ptr migration (#4319)

Add a CommandPtr with an internal unique_ptr to enable not needing to move the underlying classes, which is error-prone due to the potential for lambda captures.
This commit is contained in:
Starlight220
2022-10-06 01:19:28 +03:00
committed by GitHub
parent 3b81cf6c35
commit 60e29627c0
18 changed files with 644 additions and 133 deletions

View File

@@ -24,15 +24,8 @@ std::string GetTypeName(const T& type) {
return wpi::Demangle(typeid(type).name());
}
class EndlessCommand;
class ParallelCommandGroup;
class ParallelRaceGroup;
class ParallelDeadlineGroup;
class SequentialCommandGroup;
class PerpetualCommand;
class ProxyScheduleCommand;
class RepeatCommand;
class ConditionalCommand;
/**
* A state machine representing a complete action to be performed by the robot.
@@ -121,6 +114,8 @@ class Command {
kCancelIncoming
};
friend class CommandPtr;
/**
* Decorates this command with a timeout. If the specified timeout is
* exceeded before the command finishes normally, the command will be
@@ -130,7 +125,7 @@ class Command {
* @param duration the timeout duration
* @return the command with the timeout added
*/
ParallelRaceGroup WithTimeout(units::second_t duration) &&;
[[nodiscard]] CommandPtr WithTimeout(units::second_t duration) &&;
/**
* Decorates this command with an interrupt condition. If the specified
@@ -141,7 +136,7 @@ class Command {
* @param condition the interrupt condition
* @return the command with the interrupt condition added
*/
ParallelRaceGroup Until(std::function<bool()> condition) &&;
[[nodiscard]] CommandPtr Until(std::function<bool()> condition) &&;
/**
* Decorates this command with an interrupt condition. If the specified
@@ -154,7 +149,7 @@ class Command {
* @deprecated Replace with Until()
*/
WPI_DEPRECATED("Replace with Until()")
ParallelRaceGroup WithInterrupt(std::function<bool()> condition) &&;
[[nodiscard]] CommandPtr WithInterrupt(std::function<bool()> condition) &&;
/**
* Decorates this command with a runnable to run before this command starts.
@@ -163,7 +158,7 @@ class Command {
* @param requirements the required subsystems
* @return the decorated command
*/
SequentialCommandGroup BeforeStarting(
[[nodiscard]] CommandPtr BeforeStarting(
std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements) &&;
@@ -174,7 +169,7 @@ class Command {
* @param requirements the required subsystems
* @return the decorated command
*/
SequentialCommandGroup BeforeStarting(
[[nodiscard]] CommandPtr BeforeStarting(
std::function<void()> toRun,
wpi::span<Subsystem* const> requirements = {}) &&;
@@ -185,7 +180,7 @@ class Command {
* @param requirements the required subsystems
* @return the decorated command
*/
SequentialCommandGroup AndThen(
[[nodiscard]] CommandPtr AndThen(
std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements) &&;
@@ -196,7 +191,7 @@ class Command {
* @param requirements the required subsystems
* @return the decorated command
*/
SequentialCommandGroup AndThen(
[[nodiscard]] CommandPtr AndThen(
std::function<void()> toRun,
wpi::span<Subsystem* const> requirements = {}) &&;
@@ -216,7 +211,7 @@ class Command {
*
* @return the decorated command
*/
EndlessCommand Endlessly() &&;
[[nodiscard]] CommandPtr Endlessly() &&;
/**
* Decorates this command to run repeatedly, restarting it when it ends, until
@@ -224,7 +219,7 @@ class Command {
*
* @return the decorated command
*/
RepeatCommand Repeatedly() &&;
[[nodiscard]] CommandPtr Repeatedly() &&;
/**
* Decorates this command to run "by proxy" by wrapping it in a
@@ -232,9 +227,11 @@ class Command {
* when the user does not wish to extend the command's requirements to the
* entire command group.
*
* <p>This overload transfers command ownership to the returned CommandPtr.
*
* @return the decorated command
*/
ProxyScheduleCommand AsProxy();
[[nodiscard]] CommandPtr AsProxy() &&;
/**
* Decorates this command to only run if this condition is not met. If the
@@ -245,7 +242,7 @@ class Command {
* @param condition the condition that will prevent the command from running
* @return the decorated command
*/
ConditionalCommand Unless(std::function<bool()> condition) &&;
[[nodiscard]] CommandPtr Unless(std::function<bool()> condition) &&;
/**
* Decorates this command to run or stop when disabled.
@@ -253,7 +250,7 @@ class Command {
* @param doesRunWhenDisabled true to run when disabled.
* @return the decorated command
*/
std::unique_ptr<Command> IgnoringDisable(bool doesRunWhenDisabled) &&;
[[nodiscard]] CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&;
/**
* Decorates this command to run or stop when disabled.
@@ -261,8 +258,8 @@ class Command {
* @param interruptBehavior true to run when disabled.
* @return the decorated command
*/
std::unique_ptr<Command> WithInterruptBehavior(
InterruptionBehavior interruptBehavior) &&;
[[nodiscard]] CommandPtr WithInterruptBehavior(
Command::InterruptionBehavior interruptBehavior) &&;
/**
* Schedules this command.

View File

@@ -9,6 +9,7 @@
#include <utility>
#include "frc2/command/Command.h"
#include "frc2/command/CommandPtr.h"
namespace frc2 {
@@ -28,6 +29,11 @@ class CommandHelper : public Base {
public:
CommandHelper() = default;
CommandPtr ToPtr() && {
return CommandPtr(
std::make_unique<CRTP>(std::move(*static_cast<CRTP*>(this))));
}
protected:
std::unique_ptr<Command> TransferOwnership() && override {
return std::make_unique<CRTP>(std::move(*static_cast<CRTP*>(this)));

View File

@@ -0,0 +1,189 @@
// 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.
#pragma once
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <utility>
#include "frc2/command/Command.h"
namespace frc2 {
class CommandPtr final {
public:
explicit CommandPtr(std::unique_ptr<Command>&& command)
: m_ptr(std::move(command)) {}
template <class T, typename = std::enable_if_t<std::is_base_of_v<
Command, std::remove_reference_t<T>>>>
explicit CommandPtr(T&& command)
: CommandPtr(std::make_unique<std::remove_reference_t<T>>(
std::forward<T>(command))) {}
CommandPtr(CommandPtr&&) = default;
CommandPtr& operator=(CommandPtr&&) = default;
/**
* Decorates this command to run repeatedly, restarting it when it ends, until
* this command is interrupted. The decorated command can still be canceled.
*
* @return the decorated command
*/
[[nodiscard]] CommandPtr Repeatedly() &&;
/**
* Decorates this command to run endlessly, ignoring its ordinary end
* conditions. The decorated command can still be interrupted or canceled.
*
* @return the decorated command
*/
[[nodiscard]] CommandPtr Endlessly() &&;
/**
* Decorates this command to run "by proxy" by wrapping it in a
* ProxyScheduleCommand. This is useful for "forking off" from command groups
* when the user does not wish to extend the command's requirements to the
* entire command group.
*
* @return the decorated command
*/
[[nodiscard]] CommandPtr AsProxy() &&;
/**
* Decorates this command to run or stop when disabled.
*
* @param doesRunWhenDisabled true to run when disabled.
* @return the decorated command
*/
[[nodiscard]] CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&;
/**
* Decorates this command to run or stop when disabled.
*
* @param interruptBehavior true to run when disabled.
* @return the decorated command
*/
[[nodiscard]] CommandPtr WithInterruptBehavior(
Command::InterruptionBehavior interruptBehavior) &&;
/**
* Decorates this command with a runnable to run after the command finishes.
*
* @param toRun the Runnable to run
* @param requirements the required subsystems
* @return the decorated command
*/
[[nodiscard]] CommandPtr AndThen(
std::function<void()> toRun,
wpi::span<Subsystem* const> requirements = {}) &&;
/**
* Decorates this command with a runnable to run after the command finishes.
*
* @param toRun the Runnable to run
* @param requirements the required subsystems
* @return the decorated command
*/
[[nodiscard]] CommandPtr AndThen(
std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements) &&;
/**
* Decorates this command with a runnable to run before this command starts.
*
* @param toRun the Runnable to run
* @param requirements the required subsystems
* @return the decorated command
*/
[[nodiscard]] CommandPtr BeforeStarting(
std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements) &&;
/**
* Decorates this command with a runnable to run before this command starts.
*
* @param toRun the Runnable to run
* @param requirements the required subsystems
* @return the decorated command
*/
[[nodiscard]] CommandPtr BeforeStarting(
std::function<void()> toRun,
wpi::span<Subsystem* const> requirements = {}) &&;
/**
* Decorates this command with a timeout. If the specified timeout is
* exceeded before the command finishes normally, the command will be
* interrupted and un-scheduled. Note that the timeout only applies to the
* command returned by this method; the calling command is not itself changed.
*
* @param duration the timeout duration
* @return the command with the timeout added
*/
[[nodiscard]] CommandPtr WithTimeout(units::second_t duration) &&;
/**
* Decorates this command with an interrupt condition. If the specified
* condition becomes true before the command finishes normally, the command
* will be interrupted and un-scheduled. Note that this only applies to the
* command returned by this method; the calling command is not itself changed.
*
* @param condition the interrupt condition
* @return the command with the interrupt condition added
*/
[[nodiscard]] CommandPtr Until(std::function<bool()> condition) &&;
/**
* Decorates this command to only run if this condition is not met. If the
* command is already running and the condition changes to true, the command
* will not stop running. The requirements of this command will be kept for
* the new conditonal command.
*
* @param condition the condition that will prevent the command from running
* @return the decorated command
*/
[[nodiscard]] CommandPtr Unless(std::function<bool()> condition) &&;
/**
* Get a raw pointer to the held command.
*/
Command* get() const;
/**
* Schedules this command.
*/
void Schedule() const;
/**
* Cancels this command. Will call End(true). Commands will be canceled
* regardless of interruption behavior.
*/
void Cancel() const;
/**
* Whether or not the command is currently scheduled. Note that this does not
* detect whether the command is being run by a CommandGroup, only whether it
* is directly being run by the scheduler.
*
* @return Whether the command is scheduled.
*/
bool IsScheduled() const;
/**
* Whether the command requires a given subsystem. Named "HasRequirement"
* rather than "requires" to avoid confusion with Command::Requires(Subsystem)
* -- this may be able to be changed in a few years.
*
* @param requirement the subsystem to inquire about
* @return whether the subsystem is required
*/
bool HasRequirement(Subsystem* requirement) const;
private:
std::unique_ptr<Command> m_ptr;
};
} // namespace frc2

View File

@@ -20,6 +20,7 @@
namespace frc2 {
class Command;
class CommandPtr;
class Subsystem;
/**
@@ -82,6 +83,17 @@ class CommandScheduler final : public nt::NTSendable,
WPI_DEPRECATED("Call Clear on the EventLoop instance directly!")
void ClearButtons();
/**
* Schedules a command for execution. Does nothing if the command is already
* scheduled. If a command's requirements are not available, it will only be
* started if all the commands currently using those requirements are
* interruptible. If this is the case, they will be interrupted and the
* command will be scheduled.
*
* @param command the command to schedule
*/
void Schedule(const CommandPtr& command);
/**
* Schedules a command for execution. Does nothing if the command is already
* scheduled. If a command's requirements are not available, it will only be
@@ -200,6 +212,18 @@ class CommandScheduler final : public nt::NTSendable,
*/
void Cancel(Command* command);
/**
* Cancels commands. The scheduler will only call Command::End()
* method of the canceled command with true, indicating they were
* canceled (as opposed to finishing normally).
*
* <p>Commands will be canceled even if they are not scheduled as
* interruptible.
*
* @param command the command to cancel
*/
void Cancel(const CommandPtr& command);
/**
* Cancels commands. The scheduler will only call Command::End()
* method of the canceled command with true, indicating they were
@@ -259,6 +283,16 @@ class CommandScheduler final : public nt::NTSendable,
*/
bool IsScheduled(const Command* command) const;
/**
* Whether a given command is running. Note that this only works on commands
* that are directly scheduled by the scheduler; it will not work on commands
* inside of CommandGroups, as the scheduler does not see them.
*
* @param command the command to query
* @return whether the command is currently scheduled
*/
bool IsScheduled(const CommandPtr& command) const;
/**
* Returns the command currently requiring a given subsystem. Null if no
* command is currently requiring the subsystem

View File

@@ -11,6 +11,7 @@
#include <wpi/span.h>
#include "Trigger.h"
#include "frc2/command/CommandPtr.h"
namespace frc2 {
class Command;
@@ -47,6 +48,16 @@ class Button : public Trigger {
*/
Button WhenPressed(Command* command);
/**
* Binds a command to start when the button is pressed. Transfers
* command ownership to the button scheduler, so the user does not have to
* worry about lifespan.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Button WhenPressed(CommandPtr&& command);
/**
* Binds a command to start when the button is pressed. Transfers
* command ownership to the button scheduler, so the user does not have to
@@ -91,6 +102,16 @@ class Button : public Trigger {
*/
Button WhileHeld(Command* command);
/**
* Binds a command to be started repeatedly while the button is pressed, and
* canceled when it is released. Transfers command ownership to the button
* scheduler, so the user does not have to worry about lifespan.
*
* @param command The command to bind.
* @return The button, for chained calls.
*/
Button WhileHeld(CommandPtr&& command);
/**
* Binds a command to be started repeatedly while the button is pressed, and
* canceled when it is released. Transfers command ownership to the button
@@ -135,6 +156,16 @@ class Button : public Trigger {
*/
Button WhenHeld(Command* command);
/**
* Binds a command to be started when the button is pressed, and canceled
* when it is released. Transfers command ownership to the button scheduler,
* so the user does not have to worry about lifespan.
*
* @param command The command to bind.
* @return The button, for chained calls.
*/
Button WhenHeld(CommandPtr&& command);
/**
* Binds a command to be started when the button is pressed, and canceled
* when it is released. Transfers command ownership to the button scheduler,
@@ -161,6 +192,16 @@ class Button : public Trigger {
*/
Button WhenReleased(Command* command);
/**
* Binds a command to start when the button is pressed. Transfers
* command ownership to the button scheduler, so the user does not have to
* worry about lifespan.
*
* @param command The command to bind.
* @return The button, for chained calls.
*/
Button WhenReleased(CommandPtr&& command);
/**
* Binds a command to start when the button is pressed. Transfers
* command ownership to the button scheduler, so the user does not have to
@@ -205,6 +246,16 @@ class Button : public Trigger {
*/
Button ToggleWhenPressed(Command* command);
/**
* Binds a command to start when the button is pressed, and be canceled when
* it is pessed again. Transfers command ownership to the button scheduler,
* so the user does not have to worry about lifespan.
*
* @param command The command to bind.
* @return The button, for chained calls.
*/
Button ToggleWhenPressed(CommandPtr&& command);
/**
* Binds a command to start when the button is pressed, and be canceled when
* it is pessed again. Transfers command ownership to the button scheduler,

View File

@@ -69,6 +69,15 @@ class Trigger : public frc::BooleanEvent {
*/
Trigger WhenActive(Command* command);
/**
* Binds a command to start when the trigger becomes active. Moves
* command ownership to the button scheduler.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Trigger WhenActive(CommandPtr&& command);
/**
* Binds a command to start when the trigger becomes active. Transfers
* command ownership to the button scheduler, so the user does not have to
@@ -116,6 +125,16 @@ class Trigger : public frc::BooleanEvent {
*/
Trigger WhileActiveContinous(Command* command);
/**
* Binds a command to be started repeatedly while the trigger is active, and
* canceled when it becomes inactive. Moves command ownership to the button
* scheduler.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Trigger WhileActiveContinous(CommandPtr&& command);
/**
* Binds a command to be started repeatedly while the trigger is active, and
* canceled when it becomes inactive. Transfers command ownership to the
@@ -164,6 +183,16 @@ class Trigger : public frc::BooleanEvent {
*/
Trigger WhileActiveOnce(Command* command);
/**
* Binds a command to be started when the trigger becomes active, and
* canceled when it becomes inactive. Moves command ownership to the button
* scheduler.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Trigger WhileActiveOnce(CommandPtr&& command);
/**
* Binds a command to be started when the trigger becomes active, and
* canceled when it becomes inactive. Transfers command ownership to the
@@ -195,6 +224,15 @@ class Trigger : public frc::BooleanEvent {
*/
Trigger WhenInactive(Command* command);
/**
* Binds a command to start when the trigger becomes inactive. Moves
* command ownership to the button scheduler.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Trigger WhenInactive(CommandPtr&& command);
/**
* Binds a command to start when the trigger becomes inactive. Transfers
* command ownership to the button scheduler, so the user does not have to
@@ -242,6 +280,16 @@ class Trigger : public frc::BooleanEvent {
*/
Trigger ToggleWhenActive(Command* command);
/**
* Binds a command to start when the trigger becomes active, and be canceled
* when it again becomes active. Moves command ownership to the button
* scheduler.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Trigger ToggleWhenActive(CommandPtr&& command);
/**
* Binds a command to start when the trigger becomes active, and be canceled
* when it again becomes active. Transfers command ownership to the button