Curt Welch
Member
I'm a long-time C programmer, but I'm only familiar with the basic features of C++. Generics (templates) bit me here with FlexCAN when I tried to change which CAN bus was being used.
We have a PH_motor class that implements motor control, mapping motor commands to CAN bus commands. It currently consists of around 2,000 lines of code.
The usage of the class was to create a CAN object as usual and pass it to the motor control class like this:
But then I needed to use CAN3 on one of our new projects and realized the issue that templates creates with this.
I could not just change the CAN1 to CAN3 on the FlexCAN object because our Ph_motor class had multiple places where the type of CAN1 had to be hard-coded as part of the FlexCAN type.
This motor wrapper class couldn't support any CAN bus other than the one for which it was hard-coded.
The only solution I have found so far is to turn the entire 2000 lines of our PhMotor class into a template class. So the usage becomes:
This is not bad, but it's awkward. Now I have 2000 lines of C++ code in a header file as a template that can't be compiled and linked. It just bothers my old school aesthetics but creates no real operational issue.
But then I had constants in the PH_motor class, which now must all include the CAN bus definition in my higher level code:
So, I suddenly have to hard-code these CAN3 references in many places where they don't belong.
I see one path to fixing this by never referencing the Class constants and using instance methods, such as:
All this mess arises because the CAN bus definition is a compile-time type definition rather than a run-time variable that can be passed and referenced.
I assume the reason for doing this was to optimize the CAN driver code, but I never realized that the use of templates in C++ would then propagate its approach, making all code above it using the lower level have to be templates.
If one Teeny project uses all three CAN buses then it also means three versions of all the Ph_motor code gets compiled even though there litterly is no difference in the code.
So my question is, are there any other ways to structure the code of my PH_motor class other than what I did by turning the class into a template so I could pass the CAN bus definition through it to the FlexCAN_T4 class? Is there some standard C++ way to deal with this I just haven't found yet?
Part of the issue is that the PH_motor class has an instance variable that points to the FlexCAN object. But the type of this pointer needs to be different depending on what CAN bus is used. Typing it a void* might bypas the checking and work? But that feels wrong.
I've also seen that by subclassing, some of this is solved. Create a base class with all the code that is not dependent, and a template subclass that implements just the code that is dependent on talking to FlexCAN. That could be done. But that also just adds odd complexity to work around what the language doesn't let me do directly. I'm undecided if I like that or not.
So just hoping others might have dealt with this with the FlexCAN package and had some ideas to share....
We have a PH_motor class that implements motor control, mapping motor commands to CAN bus commands. It currently consists of around 2,000 lines of code.
The usage of the class was to create a CAN object as usual and pass it to the motor control class like this:
C++:
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can;
PH_motor MyMotor(Can, 25, 1); // PH25 motor with ID 1
But then I needed to use CAN3 on one of our new projects and realized the issue that templates creates with this.
I could not just change the CAN1 to CAN3 on the FlexCAN object because our Ph_motor class had multiple places where the type of CAN1 had to be hard-coded as part of the FlexCAN type.
This motor wrapper class couldn't support any CAN bus other than the one for which it was hard-coded.
The only solution I have found so far is to turn the entire 2000 lines of our PhMotor class into a template class. So the usage becomes:
C++:
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can3;
PH_motor<CAN3> MyMotor(Can3, 25, 1); // PH25 motor with ID 1
This is not bad, but it's awkward. Now I have 2000 lines of C++ code in a header file as a template that can't be compiled and linked. It just bothers my old school aesthetics but creates no real operational issue.
But then I had constants in the PH_motor class, which now must all include the CAN bus definition in my higher level code:
C++:
MyMotor.check_state(PH_motor<CAN3>::MODE_VEL);
if (MyMotor.state != PH_motor<CAN3>::State::READY) {
...
So, I suddenly have to hard-code these CAN3 references in many places where they don't belong.
I see one path to fixing this by never referencing the Class constants and using instance methods, such as:
C++:
MyMotor.check_state(MyMotor.MODE_VEL);
if (MyMotor.is_ready()) {
...
All this mess arises because the CAN bus definition is a compile-time type definition rather than a run-time variable that can be passed and referenced.
I assume the reason for doing this was to optimize the CAN driver code, but I never realized that the use of templates in C++ would then propagate its approach, making all code above it using the lower level have to be templates.
If one Teeny project uses all three CAN buses then it also means three versions of all the Ph_motor code gets compiled even though there litterly is no difference in the code.
So my question is, are there any other ways to structure the code of my PH_motor class other than what I did by turning the class into a template so I could pass the CAN bus definition through it to the FlexCAN_T4 class? Is there some standard C++ way to deal with this I just haven't found yet?
Part of the issue is that the PH_motor class has an instance variable that points to the FlexCAN object. But the type of this pointer needs to be different depending on what CAN bus is used. Typing it a void* might bypas the checking and work? But that feels wrong.
I've also seen that by subclassing, some of this is solved. Create a base class with all the code that is not dependent, and a template subclass that implements just the code that is dependent on talking to FlexCAN. That could be done. But that also just adds odd complexity to work around what the language doesn't let me do directly. I'm undecided if I like that or not.
So just hoping others might have dealt with this with the FlexCAN package and had some ideas to share....