WPI_FalconFX Derived Class Constructor

I would like to make a derived class from the WPI_TalonFX, and I was a bit surprised that it didn’t work as expected. I’m hoping someone can help fill in the gaps here wrt constructor chaining:

First; this is how I would expect it to work (and it does work as expected with a Spark):

 #pragma once
 #include <frc/Spark.h>
 
 class LazySpark : public frc::Spark  {
 
   public:
   LazySpark() = delete;
   LazySpark(int id) : Spark(id) { }
 };

So I naturally change the spark to WPI_FalconFX:

 #pragma once
 #include <ctre/Phoenix.h>
 
 class LazyFalcon : public WPI_TalonFX
 {
 public:
 LazyFalcon() = delete;
 LazyFalcon(int id) : WPI_TalonFX(id) { }
 };

And get an unexpected build failure:

Task :compileFrcUserProgramDebugExecutableFrcUserProgramCpp FAILED
In file included from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:17:0,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/projects/3538/LazyFalcon/src/main/include/LazyFalcon2.h: In constructor 'LazyFalcon::LazyFalcon(int)':
/home/dereck/projects/3538/LazyFalcon/src/main/include/LazyFalcon2.h:15:38: error: use of deleted function 'ctre::phoenix::motorcontrol::can::BaseMotorController::BaseMotorController()'
   LazyFalcon(int id) : WPI_TalonFX(id) { }
                                      ^
In file included from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/BaseTalon.h:7:0,
                 from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/TalonFX.h:7,
                 from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/Phoenix.h:20,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:14,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/BaseMotorController.h:650:6: note: declared here
      BaseMotorController() = delete;
      ^~~~~~~~~~~~~~~~~~~
In file included from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:17:0,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/projects/3538/LazyFalcon/src/main/include/LazyFalcon2.h:15:38: error: use of deleted function 'ctre::phoenix::motorcontrol::can::BaseTalon::BaseTalon()'
   LazyFalcon(int id) : WPI_TalonFX(id) { }
                                      ^
In file included from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/TalonFX.h:7:0,
                 from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/Phoenix.h:20,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:14,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/BaseTalon.h:317:6: note: declared here
      BaseTalon() = delete;
      ^~~~~~~~~
In file included from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:17:0,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/projects/3538/LazyFalcon/src/main/include/LazyFalcon2.h:15:38: error: use of deleted function 'ctre::phoenix::motorcontrol::can::TalonFX::TalonFX()'
   LazyFalcon(int id) : WPI_TalonFX(id) { }
                                      ^
In file included from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/Phoenix.h:20:0,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:14,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/TalonFX.h:136:6: note: declared here
      TalonFX() = delete;
      ^~~~~~~
In file included from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:17:0,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/projects/3538/LazyFalcon/src/main/include/LazyFalcon2.h:15:38: error: use of deleted function 'ctre::phoenix::motorcontrol::can::WPI_BaseMotorController::WPI_BaseMotorController()'
   LazyFalcon(int id) : WPI_TalonFX(id) { }
                                      ^
In file included from /home/dereck/.gradle/caches/transforms-2/files-2.1/683f51cae80232a4efef6144c2bbd6d3/wpiapi-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/WPI_TalonFX.h:14:0,
                 from /home/dereck/.gradle/caches/transforms-2/files-2.1/130194f81e05a9a932af63493f4564ec/api-cpp-5.18.3-headers/ctre/Phoenix.h:40,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/include/Robot.h:14,
                 from /home/dereck/projects/3538/LazyFalcon/src/main/cpp/Robot.cpp:8:
/home/dereck/.gradle/caches/transforms-2/files-2.1/683f51cae80232a4efef6144c2bbd6d3/wpiapi-cpp-5.18.3-headers/ctre/phoenix/motorcontrol/can/WPI_BaseMotorController.h:63:2: note: declared here
  WPI_BaseMotorController() = delete;

Simply doing as the error asked, and calling each of the base class’s base class constructors directly seems to build fine:

 #pragma once
 #include <ctre/Phoenix.h>
 
 class LazyFalcon : public WPI_TalonFX
 {
 
 public:
   LazyFalcon() = delete;
   LazyFalcon(int id) :
                        BaseMotorController(id, device),
                        BaseTalon(id, device),
                        TalonFX(id),
                        WPI_BaseMotorController(id, device),
                        WPI_TalonFX(id)
   { }
 
 private:
   const char *device = "Talon FX";
 };

Why? :anger: :orangutan:

I would expect that calling the WPI_TalonFX(id) should call it’s base class’s constructors automatically, yet I was forced to call them directly from my derived class’s constructor.

This is due to the fact the CTRE classes use virtual inheritance, which requires the most-derived class to construct all virtual base classes. See this Stack Overflow question/response. One way to avoid this is by using composition instead of inheritance.

5 Likes

Phoenix talonfx Line 54:
http://www.ctr-electronics.com/downloads/api/cpp/html/_w_p_i___talon_f_x_8h_source.html

wpilib VictorSP Line 30:
https://first.wpi.edu/FRC/roborio/release/docs/cpp/VictorSP_8h_source.html

For my own edification, is there a reason why Phoenix uses virtual interfaces? @ozrien

They have to, because they have diamond inheritance.
WPI_TalonFX -> WPI_BaseMotorController -> BaseMotorController
WPI_TalonFX -> TalonFX -> BaseTalon -> BaseMotorController

1 Like

The point of all the WPI_whatever classes is lost on me. We have used the regular classes with no problems.

The WPI_ classes implement the WPILib SpeedController and Sendable interfaces which are required to interoperate with several parts of WPILib, namely the drivetrain classes and LiveWindow. Some parts of WPILib (e.g. the new path planning features) are moving away from interfaces like SpeedController to generic functional interfaces, which alleviates most of this problem.

CTRE decided when designing Phoenix to make most of their classes non-WPILib-dependent (as they have non-FRC/RoboRIO use cases), and just layer the WPI_ classes on top. Arguably they could have instead had FRC and non-FRC variants of their base classes instead of doing this layering.

2 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.