From 8ec1ff7e2d8deb21e97dd194c6399929a4e06674 Mon Sep 17 00:00:00 2001 From: Bertrand Coconnier Date: Sun, 9 Feb 2025 01:52:37 +0100 Subject: [PATCH] Make `atof_locale_c` more pedantic. --- src/FGJSBBase.h | 1 - src/initialization/FGInitialCondition.cpp | 1 + src/input_output/FGInputSocket.cpp | 19 +++++------- src/input_output/FGOutputSocket.cpp | 15 ++++----- src/input_output/FGOutputTextFile.cpp | 1 + src/input_output/FGScript.cpp | 1 + src/input_output/FGUDPInputSocket.cpp | 17 +++++------ src/input_output/FGXMLElement.cpp | 14 +++++---- src/input_output/FGfdmSocket.cpp | 2 ++ src/input_output/string_utilities.cpp | 35 ++++++++++++++++----- src/input_output/string_utilities.h | 7 +++++ src/math/FGFunction.cpp | 37 +++++++++++------------ src/math/FGParameterValue.h | 7 +++-- src/math/FGTable.cpp | 1 + src/models/FGInput.cpp | 1 + src/models/FGOutput.cpp | 1 + src/models/flight_control/FGSwitch.cpp | 6 ++-- src/models/propulsion/FGRotor.cpp | 25 +++++++-------- src/models/propulsion/FGTurbine.cpp | 11 ++++--- 19 files changed, 120 insertions(+), 82 deletions(-) diff --git a/src/FGJSBBase.h b/src/FGJSBBase.h index f203b211c0..5352704c75 100644 --- a/src/FGJSBBase.h +++ b/src/FGJSBBase.h @@ -47,7 +47,6 @@ INCLUDES #include #include "JSBSim_API.h" -#include "input_output/string_utilities.h" #ifndef M_PI # define M_PI 3.14159265358979323846 diff --git a/src/initialization/FGInitialCondition.cpp b/src/initialization/FGInitialCondition.cpp index a0826ebdb8..343dcb6d16 100644 --- a/src/initialization/FGInitialCondition.cpp +++ b/src/initialization/FGInitialCondition.cpp @@ -52,6 +52,7 @@ INCLUDES #include "input_output/FGXMLFileRead.h" #include "FGTrim.h" #include "FGFDMExec.h" +#include "input_output/string_utilities.h" using namespace std; diff --git a/src/input_output/FGInputSocket.cpp b/src/input_output/FGInputSocket.cpp index 89de037d7f..f0e3bf32f8 100644 --- a/src/input_output/FGInputSocket.cpp +++ b/src/input_output/FGInputSocket.cpp @@ -47,6 +47,7 @@ INCLUDES #include "FGFDMExec.h" #include "models/FGAircraft.h" #include "input_output/FGXMLElement.h" +#include "input_output/string_utilities.h" using namespace std; @@ -168,17 +169,13 @@ void FGInputSocket::Read(bool Holding) socket->Reply("Not a leaf property\r\n"); break; } else { - if (is_number(trim(str_value))) { - try { - double value = atof_locale_c(str_value); - node->setDoubleValue(value); - } catch(BaseException& e) { - socket->Reply(e.what()); - break; - } - } - else { - socket->Reply("Invalid number\r\n"); + try { + double value = atof_locale_c(str_value); + node->setDoubleValue(value); + } catch(InvalidNumber& e) { + string msg(e.what()); + msg += "\r\n"; + socket->Reply(msg); break; } } diff --git a/src/input_output/FGOutputSocket.cpp b/src/input_output/FGOutputSocket.cpp index 9c0d654d3d..df9b1d0092 100644 --- a/src/input_output/FGOutputSocket.cpp +++ b/src/input_output/FGOutputSocket.cpp @@ -56,6 +56,7 @@ INCLUDES #include "models/atmosphere/FGWinds.h" #include "input_output/FGXMLElement.h" #include "math/FGPropertyValue.h" +#include "input_output/string_utilities.h" using namespace std; @@ -85,25 +86,25 @@ void FGOutputSocket::SetOutputName(const string& fname) // tokenize the output name size_t dot_pos = fname.find(':', 0); size_t slash_pos = fname.find('/', 0); - + string name = fname.substr(0, dot_pos); - + string proto = "TCP"; if(dot_pos + 1 < slash_pos) proto = fname.substr(dot_pos + 1, slash_pos - dot_pos - 1); - + string port = "1138"; if(slash_pos < string::npos) port = fname.substr(slash_pos + 1, string::npos); - + // set the model name Name = name + ":" + port + "/" + proto; - + // set the socket params SockName = name; - + SockPort = atoi(port.c_str()); - + if (to_upper(proto) == "UDP") SockProtocol = FGfdmSocket::ptUDP; else // Default to TCP diff --git a/src/input_output/FGOutputTextFile.cpp b/src/input_output/FGOutputTextFile.cpp index d42bb6ef46..ea7fc6e9a1 100644 --- a/src/input_output/FGOutputTextFile.cpp +++ b/src/input_output/FGOutputTextFile.cpp @@ -52,6 +52,7 @@ INCLUDES #include "models/FGFCS.h" #include "models/atmosphere/FGWinds.h" #include "input_output/FGXMLElement.h" +#include "input_output/string_utilities.h" using namespace std; diff --git a/src/input_output/FGScript.cpp b/src/input_output/FGScript.cpp index 0d664bd695..d3e98fd498 100644 --- a/src/input_output/FGScript.cpp +++ b/src/input_output/FGScript.cpp @@ -50,6 +50,7 @@ INCLUDES #include "models/FGInput.h" #include "math/FGCondition.h" #include "math/FGFunctionValue.h" +#include "input_output/string_utilities.h" using namespace std; diff --git a/src/input_output/FGUDPInputSocket.cpp b/src/input_output/FGUDPInputSocket.cpp index b2cbc46c3e..54626f2044 100644 --- a/src/input_output/FGUDPInputSocket.cpp +++ b/src/input_output/FGUDPInputSocket.cpp @@ -44,6 +44,7 @@ INCLUDES #include "FGUDPInputSocket.h" #include "FGFDMExec.h" #include "input_output/FGXMLElement.h" +#include "input_output/string_utilities.h" using namespace std; @@ -106,15 +107,13 @@ void FGUDPInputSocket::Read(bool Holding) vector values; - for (string& token : tokens) { - if (is_number(trim(token))) { - try { - values.push_back(atof_locale_c(token)); - } catch(BaseException& e) { - return; - } - } - } + try { + for (string& token : tokens) + values.push_back(atof_locale_c(token)); + } catch(InvalidNumber& e) { + cerr << e.what() << endl; + return; + } if (values[0] < oldTimeStamp) { return; diff --git a/src/input_output/FGXMLElement.cpp b/src/input_output/FGXMLElement.cpp index afcdbdd579..469c541ce1 100644 --- a/src/input_output/FGXMLElement.cpp +++ b/src/input_output/FGXMLElement.cpp @@ -31,8 +31,10 @@ INCLUDES #include #include // for assembling the error messages / what of exceptions. #include // using domain_error, invalid_argument, and length_error. + #include "FGXMLElement.h" #include "FGJSBBase.h" +#include "input_output/string_utilities.h" using namespace std; @@ -293,11 +295,11 @@ double Element::GetAttributeValueAsNumber(const string& attr) } else { double number=0; - if (is_number(trim(attribute))) + try { number = atof_locale_c(attribute); - else { + } catch (InvalidNumber& e) { std::stringstream s; - s << ReadFrom() << "Expecting numeric attribute value, but got: " << attribute; + s << ReadFrom() << e.what(); cerr << s.str() << endl; throw BaseException(s.str()); } @@ -347,11 +349,11 @@ double Element::GetDataAsNumber(void) { if (data_lines.size() == 1) { double number=0; - if (is_number(trim(data_lines[0]))) + try { number = atof_locale_c(data_lines[0]); - else { + } catch (InvalidNumber& e) { std::stringstream s; - s << ReadFrom() << "Expected numeric value, but got: " << data_lines[0]; + s << ReadFrom() << e.what(); cerr << s.str() << endl; throw BaseException(s.str()); } diff --git a/src/input_output/FGfdmSocket.cpp b/src/input_output/FGfdmSocket.cpp index 536ae49367..178673bc19 100644 --- a/src/input_output/FGfdmSocket.cpp +++ b/src/input_output/FGfdmSocket.cpp @@ -55,7 +55,9 @@ INCLUDES #include #include #include + #include "FGfdmSocket.h" +#include "input_output/string_utilities.h" using std::cout; using std::cerr; diff --git a/src/input_output/string_utilities.cpp b/src/input_output/string_utilities.cpp index c8c2c2c7ca..28a1c90210 100644 --- a/src/input_output/string_utilities.cpp +++ b/src/input_output/string_utilities.cpp @@ -56,6 +56,8 @@ typedef _locale_t locale_t; #define strtod_l _strtod_l #endif +using namespace std; + namespace JSBSim { struct CNumericLocale { @@ -80,12 +82,25 @@ struct CNumericLocale * Whatever is the current locale of the application, atof_locale_c() reads * numbers assuming that the decimal point is the period (.) */ -double atof_locale_c(const std::string& input) +double atof_locale_c(const string& input) { - const char* first = input.c_str(); + if (input.empty()) { + InvalidNumber e("Expecting numeric attribute value, but got an empty string"); + cerr << e.what() << endl; + throw e; + } + + string v = input; + trim(v); + + if (v.find_first_not_of("+-.0123456789Ee") != std::string::npos) { + InvalidNumber e("Expecting numeric attribute value, but got: " + input); + cerr << e.what() << endl; + throw e; + } + + const char* first = v.c_str(); - // Skip leading whitespaces - while (isspace(*first)) ++first; //Ignoring the leading '+' sign if (*first == '+') ++first; @@ -104,7 +119,7 @@ double atof_locale_c(const std::string& input) return value; std::cerr << s.str() << std::endl; - throw JSBSim::BaseException(s.str()); + throw InvalidNumber(s.str()); } @@ -158,8 +173,14 @@ bool is_number(const std::string& str) { if (str.empty()) return false; - else - return (str.find_first_not_of("+-.0123456789Ee") == std::string::npos); + + try { + atof_locale_c(str); + } catch (InvalidNumber&) { + return false; + } + + return true; } std::vector split(std::string str, char d) diff --git a/src/input_output/string_utilities.h b/src/input_output/string_utilities.h index 8583ef6355..6957091255 100644 --- a/src/input_output/string_utilities.h +++ b/src/input_output/string_utilities.h @@ -41,6 +41,8 @@ INCLUDES #include #include +#include "FGJSBBase.h" + /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS DECLARATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ @@ -56,6 +58,11 @@ JSBSIM_API std::string& to_lower(std::string& str); JSBSIM_API bool is_number(const std::string& str); JSBSIM_API std::vector split(std::string str, char d); JSBSIM_API std::string replace(std::string str, const std::string& old, const std::string& newstr); + +class JSBSIM_API InvalidNumber : public BaseException { +public: + InvalidNumber(const std::string& msg) : BaseException(msg) {} +}; }; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/math/FGFunction.cpp b/src/math/FGFunction.cpp index 8b06bc5293..dc03b6df46 100644 --- a/src/math/FGFunction.cpp +++ b/src/math/FGFunction.cpp @@ -38,6 +38,7 @@ INCLUDES #include "FGRealValue.h" #include "input_output/FGXMLElement.h" #include "math/FGFunctionValue.h" +#include "input_output/string_utilities.h" using namespace std; @@ -611,21 +612,19 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex, string mean_attr = element->GetAttributeValue("mean"); string stddev_attr = element->GetAttributeValue("stddev"); if (!mean_attr.empty()) { - if (is_number(trim(mean_attr))) + try { mean = atof_locale_c(mean_attr); - else { - cerr << element->ReadFrom() - << "Expecting a number, but got: " << mean_attr <ReadFrom() << e.what() << endl; + throw e; } } if (!stddev_attr.empty()) { - if (is_number(trim(stddev_attr))) + try { stddev = atof_locale_c(stddev_attr); - else { - cerr << element->ReadFrom() - << "Expecting a number, but got: " << stddev_attr <ReadFrom() << e.what() << endl; + throw e; } } auto generator(makeRandomGenerator(element, fdmex)); @@ -641,21 +640,19 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex, string lower_attr = element->GetAttributeValue("lower"); string upper_attr = element->GetAttributeValue("upper"); if (!lower_attr.empty()) { - if (is_number(trim(lower_attr))) + try { lower = atof_locale_c(lower_attr); - else { - cerr << element->ReadFrom() - << "Expecting a number, but got: " << lower_attr <ReadFrom() << e.what() << endl; + throw e; } } if (!upper_attr.empty()) { - if (is_number(trim(upper_attr))) + try { upper = atof_locale_c(upper_attr); - else { - cerr << element->ReadFrom() - << "Expecting a number, but got: " << upper_attr <ReadFrom() << e.what() << endl; + throw e; } } auto generator(makeRandomGenerator(element, fdmex)); diff --git a/src/math/FGParameterValue.h b/src/math/FGParameterValue.h index be52560d9a..fc56e0455a 100644 --- a/src/math/FGParameterValue.h +++ b/src/math/FGParameterValue.h @@ -37,6 +37,7 @@ #include "math/FGRealValue.h" #include "math/FGPropertyValue.h" #include "input_output/FGXMLElement.h" +#include "input_output/string_utilities.h" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -77,9 +78,9 @@ class JSBSIM_API FGParameterValue : public FGParameter FGParameterValue(const std::string& value, std::shared_ptr pm, Element* el) { - if (is_number(value)) { - param = new FGRealValue(atof(value.c_str())); - } else { + try { + param = new FGRealValue(atof_locale_c(value.c_str())); + } catch (InvalidNumber&) { // "value" must be a property if execution passes to here. param = new FGPropertyValue(value, pm, el); } diff --git a/src/math/FGTable.cpp b/src/math/FGTable.cpp index 62f2ab59b5..bba0371e7c 100644 --- a/src/math/FGTable.cpp +++ b/src/math/FGTable.cpp @@ -41,6 +41,7 @@ INCLUDES #include "FGTable.h" #include "input_output/FGXMLElement.h" +#include "input_output/string_utilities.h" using namespace std; diff --git a/src/models/FGInput.cpp b/src/models/FGInput.cpp index d8555aed24..0fe7b2e273 100644 --- a/src/models/FGInput.cpp +++ b/src/models/FGInput.cpp @@ -44,6 +44,7 @@ INCLUDES #include "input_output/FGXMLFileRead.h" #include "input_output/FGModelLoader.h" #include "input_output/FGLog.h" +#include "input_output/string_utilities.h" using namespace std; diff --git a/src/models/FGOutput.cpp b/src/models/FGOutput.cpp index c57e01e17c..39e44daa27 100644 --- a/src/models/FGOutput.cpp +++ b/src/models/FGOutput.cpp @@ -45,6 +45,7 @@ INCLUDES #include "input_output/FGXMLFileRead.h" #include "input_output/FGModelLoader.h" #include "input_output/FGLog.h" +#include "input_output/string_utilities.h" using namespace std; diff --git a/src/models/flight_control/FGSwitch.cpp b/src/models/flight_control/FGSwitch.cpp index 380265a8b7..04f4001678 100644 --- a/src/models/flight_control/FGSwitch.cpp +++ b/src/models/flight_control/FGSwitch.cpp @@ -65,6 +65,7 @@ INCLUDES #include "models/FGFCS.h" #include "math/FGCondition.h" #include "input_output/FGLog.h" +#include "math/FGRealValue.h" using namespace std; @@ -89,8 +90,9 @@ FGSwitch::FGSwitch(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element) value = test_element->GetAttributeValue("value"); current_test->setTestValue(value, Name, PropertyManager, test_element); current_test->Default = true; - if (delay > 0 && is_number(trim(value))) { // If there is a delay, initialize the - double v = atof_locale_c(value); + auto output_value = current_test->OutputValue.ptr(); + if (delay > 0 && dynamic_cast(output_value)) { + double v = output_value->GetValue(); for (unsigned int i=0; iGetParent()->FindElement("sense"); if (thruster_element) { double s = thruster_element->GetDataAsNumber(); @@ -277,9 +278,9 @@ double FGRotor::Configure(Element* rotor_element) Radius = 0.5 * ConfigValueConv(rotor_element, "diameter", 42.0, "FT", yell); Radius = Constrain(1e-3, Radius, 1e9); - + BladeNum = (int) ConfigValue(rotor_element, "numblades", 3 , yell); - + GearRatio = ConfigValue(rotor_element, "gearratio", 1.0, yell); GearRatio = Constrain(1e-9, GearRatio, 1e9); @@ -304,7 +305,7 @@ double FGRotor::Configure(Element* rotor_element) HingeOffset = ConfigValueConv(rotor_element, "hingeoffset", 0.05 * Radius, "FT" ); estimate = sqr(BladeChord) * sqr(Radius - HingeOffset) * 0.57; - BladeFlappingMoment = ConfigValueConv(rotor_element, "flappingmoment", estimate, "SLUG*FT2"); + BladeFlappingMoment = ConfigValueConv(rotor_element, "flappingmoment", estimate, "SLUG*FT2"); BladeFlappingMoment = Constrain(1e-9, BladeFlappingMoment, 1e9); // guess mass from moment of a thin stick, and multiply by the blades cg distance @@ -353,7 +354,7 @@ double FGRotor::Configure(Element* rotor_element) // calculate control-axes components of total airspeed at the hub. // sets rotor orientation angle (beta) as side effect. /SH79/ eqn(19-22) -FGColumnVector3 FGRotor::hub_vel_body2ca( const FGColumnVector3 &uvw, +FGColumnVector3 FGRotor::hub_vel_body2ca( const FGColumnVector3 &uvw, const FGColumnVector3 &pqr, double a_ic, double b_ic) { @@ -415,7 +416,7 @@ void FGRotor::calc_flow_and_thrust( double theta_0, double Uw, double Ww, mu = Uw/(Omega*Radius); // /SH79/ eqn(24) if (mu > 0.7) mu = 0.7; mu2 = sqr(mu); - + ct_t0 = (1.0/3.0*B[3] + 1.0/2.0 * TipLossB*mu2 - 4.0/(9.0*M_PI) * mu*mu2 ) * theta_0; ct_t1 = (1.0/4.0*B[4] + 1.0/4.0 * B[2]*mu2) * BladeTwist; @@ -448,7 +449,7 @@ void FGRotor::calc_flow_and_thrust( double theta_0, double Uw, double Ww, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -// Two blade teetering rotors are often 'preconed' to a fixed angle, but the +// Two blade teetering rotors are often 'preconed' to a fixed angle, but the // calculated value is pretty close to the real one. /SH79/ eqn(29) void FGRotor::calc_coning_angle(double theta_0) @@ -473,7 +474,7 @@ void FGRotor::calc_flapping_angles(double theta_0, const FGColumnVector3 &pqr_fu double mu2_2 = sqr(mu)/2.0; double t075 = theta_0 + 0.75 * BladeTwist; // common approximation for rectangular blades - + a_1 = 1.0/(1.0 - mu2_2) * ( (2.0*lambda + (8.0/3.0)*t075)*mu + pqr_fus_w(eP)/Omega @@ -573,7 +574,7 @@ FGColumnVector3 FGRotor::body_forces(double a_ic, double b_ic) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -// calculates the additional moments due to hinge offset and handles +// calculates the additional moments due to hinge offset and handles // torque and sense FGColumnVector3 FGRotor::body_moments(double a_ic, double b_ic) diff --git a/src/models/propulsion/FGTurbine.cpp b/src/models/propulsion/FGTurbine.cpp index 0b5c737415..f9b0012938 100644 --- a/src/models/propulsion/FGTurbine.cpp +++ b/src/models/propulsion/FGTurbine.cpp @@ -48,6 +48,7 @@ INCLUDES #include "FGTurbine.h" #include "FGThruster.h" #include "input_output/FGXMLElement.h" +#include "input_output/string_utilities.h" using namespace std; @@ -487,19 +488,21 @@ bool FGTurbine::Load(FGFDMExec* exec, Element *el) JSBSim::Element* tsfcElement = el->FindElement("tsfc"); if (tsfcElement) { string value = tsfcElement->GetDataLine(); - if (is_number(trim(value))) + try { TSFC = std::make_unique(this, atof_locale_c(value)); - else + } catch (InvalidNumber&) { TSFC = std::make_unique(FDMExec, tsfcElement, to_string(EngineNumber)); + } } JSBSim::Element* atsfcElement = el->FindElement("atsfc"); if (atsfcElement) { string value = atsfcElement->GetDataLine(); - if (is_number(trim(value))) + try { ATSFC = std::make_unique(atof_locale_c(value)); - else + } catch (InvalidNumber&) { ATSFC = std::make_unique(FDMExec, atsfcElement, to_string((int)EngineNumber)); + } } // Pre-calculations and initializations