Fix illegal function pointer conversions#1304
Conversation
…s by `using`'s as the latter is a bit easier to read.
There was a problem hiding this comment.
Pull Request Overview
This PR removes most C-style function pointer conversions in favor of letting the compiler automatically determine function pointer types or using C++ reinterpret_cast<> for necessary conversions. The changes prepare the codebase for addressing undefined behavior related to function pointer type conversions (issue #834).
- Eliminates most C-style casts for function pointers by removing unnecessary explicit conversions
- Adds template type parameters to
PropertyManager->Tie()calls wherenullptrgetters are used - Replaces remaining C casts with explicit
reinterpret_cast<>to identify problematic conversions
Reviewed Changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/models/flight_control/FGPID.cpp | Replaced typedef and C-cast with template parameters for PropertyManager::Tie |
| src/models/atmosphere/FGWinds.cpp | Removed C-style casts and added template parameters, used reinterpret_cast for remaining conversions |
| src/models/atmosphere/FGStandardAtmosphere.cpp | Changed typedef to using declaration and replaced C-casts with reinterpret_cast |
| src/models/FGPropulsion.cpp | Removed typedefs and added template parameters for nullptr getter cases |
| src/models/FGPropagate.cpp | Removed typedefs and C-casts, added template parameters and reinterpret_cast for enum conversions |
| src/models/FGOutput.cpp | Replaced typedef and C-cast with template parameters |
| src/models/FGMassBalance.cpp | Removed typedefs and C-casts, replaced numeric indices with enum constants |
| src/models/FGExternalReactions.cpp | Removed typedef and C-casts for function pointers |
| src/models/FGAuxiliary.cpp | Removed typedefs and C-casts for function pointers |
| src/models/FGAircraft.cpp | Removed typedef and C-casts for function pointers |
| src/models/FGAerodynamics.cpp | Removed typedef and C-casts for function pointers |
| src/models/FGAccelerations.cpp | Removed using declaration and C-casts for function pointers |
| src/FGFDMExec.cpp | Removed typedef, added template parameters, and replaced C-casts with proper types |
| PropertyManager->Tie("simulation/integrator/position/translational", (int*)&integrator_translational_position); | ||
|
|
||
| PropertyManager->Tie("simulation/write-state-file", this, (iPMF)0, &FGPropagate::WriteStateFile); | ||
| PropertyManager->Tie("simulation/integrator/rate/rotational", |
There was a problem hiding this comment.
[nitpick] The use of reinterpret_cast for enum-to-int conversion may indicate a design issue. Consider if the PropertyManager API could be enhanced to handle enum types directly to avoid the need for casting.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1304 +/- ##
==========================================
- Coverage 24.76% 24.76% -0.01%
==========================================
Files 169 169
Lines 19605 19608 +3
==========================================
Hits 4856 4856
- Misses 14749 14752 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
When I saw the undefined behavior reported in #834 (comment) last night I tried a quick test to see what would be involved in terms of removing the function pointer casts for the So changing: PropertyManager->Tie("atmosphere/turb-type", this, (PMFt)&FGWinds::GetTurbType, (PMFi)&FGWinds::SetTurbType);to: PropertyManager->Tie("atmosphere/turb-type", this, &FGWinds::GetTurbType, &FGWinds::SetTurbType);The first compiler issue was that there was no matching jsbsim/src/simgear/props/props.hxx Lines 180 to 197 in c8af924 So I added a template<> struct simgear::props::PropertyTraits<JSBSim::FGWinds::tType> {
static const simgear::props::Type type_tag = simgear::props::Type::INT;
enum { Internal = 1 };
};Which resulted in a successful compile, but then a linker error since it couldn't find a matching template<>
inline JSBSim::FGWinds::tType getValue<JSBSim::FGWinds::tType>(const SGPropertyNode* node)
{ return (JSBSim::FGWinds::tType)node->getIntValue(); }Not sure how feasible it would be to have something like: DEFINTERNALPROP(<any enum type>, INT); |
|
Hmm, thought I could potentially use template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T getValue<T>(const SGPropertyNode* node) {
return reinterpret_cast<T>(node->getIntValue());
}But hitting a compiler error. 1>FGWinds.cpp
1>C:\source\jsbsim\src\models\atmosphere\FGWinds.cpp(76,10): error C2768: 'getValue': illegal use of explicit template arguments |
Yeah, I'm stumbling upon the same problem. However I've managed to shorten some of your code: template<> struct simgear::props::PropertyTraits<JSBSim::FGWinds::tType> {
static const simgear::props::Type type_tag = simgear::props::Type::INT;
enum { Internal = 1 };
};can be replaced by namespace simgear::props {
template<> struct PropertyTraits<JSBSim::FGWinds::tType> : public PropertyTraits<int> {};
};This avoids knowing the internals of |
|
Some progress on my side. The following works for any template<typename T> T getValue(const SGPropertyNode* node)
{
static_assert(std::is_enum_v<T>);
return static_cast<T>(node->getIntValue());
}As simple as that. This is working because jsbsim/src/simgear/props/props.hxx Lines 1837 to 1844 in c8af924 Note that the static_assert serves no other purpose than making sure that this template is not instantiated for anything other than an enum.
|
Co-authored-by: Sean McLeod <sean@seanmcleod.com>
|
@seanmcleod70 I've just pushed a new commit that implements your ideas about the specialization of Let me know what you think about it. |
Yep, my ultimate aim/hope was that there might be a way to turn that into something like this with some template magic. namespace simgear::props {
template<> struct PropertyTraits<is_any_enum> : public PropertyTraits<int> {};
};@bcoconni I pushed |
|
Just looking at your latest commit and I noticed the previous 2 commits are from June 8th. So you had been working on those before this SPEC specific issue came up? I did think it was a very quick response from you to the SPEC issue in terms of the amount of code you'd changed in such a short space of time given it was only reported around midnight our time last night 😉 |
Yeah. The bug that @heshpdx fixed in PR #1291 regarding the property Now this is the second time we are bitten by these conversions so it is some more incentive to remove the |
It is unconditionally defined for all platforms: jsbsim/src/simgear/props/props.hxx Lines 14 to 16 in c8af924
None that I could find.
Yep but I am not sure that it would be a problem since we are defining a more general case. But the compiler may complain that the 2 definitions overlap. |
|
@bcoconni any reason why in the commit you added the I also tried out the following template<typename T> requires std::is_enum_v<T> T getValue(const SGPropertyNode* node)
{
return static_cast<T>(node->getIntValue());
} |
|
Talking of namespace simgear::props {
//template<> struct PropertyTraits<JSBSim::FGWinds::tType> : public PropertyTraits<int> {};
template<typename T> requires std::is_enum_v<T> struct PropertyTraits<T> : public PropertyTraits<int> {};
}; |
|
Okay, back to namespace simgear::props {
//template<> struct PropertyTraits<JSBSim::FGWinds::tType> : public PropertyTraits<int> {};
//template<typename T> requires std::is_enum_v<T> struct PropertyTraits<T> : public PropertyTraits<int> {};
template<typename T> struct PropertyTraits : public PropertyTraits<int> {
static_assert(std::is_enum_v<T>, "PropertyTraits specialization for enum types only");
};
};To be clear, all my testing so far has simply been to confirm that the code compiles and links successfully. |
|
I've put the following into namespace simgear::props {
template<typename T> struct PropertyTraits : public PropertyTraits<int> {
static_assert(std::is_enum_v<T>, "PropertyTraits specialization for enum types only");
};
}
template<typename T> T getValue(const SGPropertyNode* node)
{
static_assert(std::is_enum_v<T>, "getValue specialization for enum types only"); // Guard against misusing template instantiation.
return static_cast<T>(node->getIntValue());
} |
Hmm, I am not too comfortable with the default implementation of a template that is inheriting from one of its own specialization. This is very close to a recursive definition and I am concerned that later in the future some pedantic compiler may reject that creative construction. |
|
Could revert to my earlier use of mirroring the template<typename T> struct PropertyTraits
{
static_assert(std::is_enum_v<T>, "PropertyTraits specialization for enum types only");
static const simgear::props::Type type_tag = simgear::props::Type::INT;
enum { Internal = 1 };
};It does as you mentioned at the time tie us into the internal implementation of |
@seanmcleod70 We cannot add new content to However I have pushed a new commit to this PR to move the template After this commit Let me know what you think. |
But I switched to putting the code into
Plus you even commented on my comment mentioning the move to I'll take a look at the latest commit. |
|
Was a bit surprised that there were only 3 enum types across all the property bindings. template<> struct PropertyTraits<JSBSim::FGPropagate::eIntegrateType> : public PropertyTraits<int> {};
template<> struct PropertyTraits<JSBSim::FGWinds::tType> : public PropertyTraits<int> {};
template<> struct PropertyTraits<JSBSim::FGWinds::eGustFrame> : public PropertyTraits<int> {};
Yep. I thought there were a whole host of enum types spread across all the different files with property bindings so I thought having a single version would help. Plus, no need to have another location with the internals of So all good from my side in terms of your latest commits. |
be52048 to
707005f
Compare
Oops ! Sorry for the confusion. |
Great ! The PR is now merged. |
Co-authored-by: Sean McLeod <sean@seanmcleod.com>


In preparation to the resolution of the undefined behavior reported in #834 (comment), this PR removes most of the pointer conversions that are currently done to tie properties.
nullptris given for the getters, the type names in the template need to be explicitly specified and this removes the need for explicit conversions.reinterpret_cast<>. These casts are precisely where the problem reported in issue JSBSim for SPEC CPUv8 #834 happens. A further PR will be needed to remove them. In the meantime they can easily be spotted in the source code by doing a simple search of thereinterpret_cast.