Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce minimum/maximum values specified in SDFormat description files (Retry PR) #303

Merged
merged 5 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions include/sdf/Element.hh
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,30 @@ namespace sdf
const std::string &_description="");

/// \brief Add a value to this Element.
/// \param[in] _type Type of data the attribute will hold.
/// \param[in] _defaultValue Default value for the attribute.
/// \param[in] _type Type of data the parameter will hold.
/// \param[in] _defaultValue Default value for the parameter.
/// \param[in] _required Requirement string. \as Element::SetRequired.
/// \param[in] _description A text description of the attribute.
/// \param[in] _description A text description of the parameter.
/// \throws sdf::AssertionInternalError if an invalid type is given.
public: void AddValue(const std::string &_type,
const std::string &_defaultValue, bool _required,
const std::string &_description="");

/// \brief Add a value to this Element. This override allows passing min and
/// max values of the parameter.
/// \param[in] _type Type of data the parameter will hold.
/// \param[in] _defaultValue Default value for the parameter.
/// \param[in] _required Requirement string. \as Element::SetRequired.
/// \param[in] _minValue Minimum allowed value for the parameter.
/// \param[in] _maxValue Maximum allowed value for the parameter.
/// \param[in] _description A text description of the parameter.
/// \throws sdf::AssertionInternalError if an invalid type is given.
public: void AddValue(const std::string &_type,
const std::string &_defaultValue, bool _required,
const std::string &_minValue,
const std::string &_maxValue,
const std::string &_description = "");

/// \brief Get the param of an attribute.
/// \param[in] _key the name of the attribute.
/// \return The parameter attribute value. NULL if the key is invalid.
Expand Down
64 changes: 58 additions & 6 deletions include/sdf/Param.hh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <typeinfo>
Expand Down Expand Up @@ -105,6 +106,41 @@ namespace sdf
const std::string &_default, bool _required,
const std::string &_description = "");

/// \brief Constructor with min and max values.
/// \param[in] _key Key for the parameter.
/// \param[in] _typeName String name for the value type (double,
/// int,...).
/// \param[in] _default Default value.
/// \param[in] _required True if the parameter is required to be set.
/// \param[in] _minValue Minimum allowed value for the parameter.
/// \param[in] _maxValue Maximum allowed value for the parameter.
/// \param[in] _description Description of the parameter.
/// \throws sdf::AssertionInternalError if an invalid type is given.
public: Param(const std::string &_key, const std::string &_typeName,
const std::string &_default, bool _required,
const std::string &_minValue, const std::string &_maxValue,
const std::string &_description = "");

/// \brief Copy constructor
/// Note that the updateFunc member does not get copied
/// \param[in] _param Param to copy
public: Param(const Param &_param);

/// \brief Move constructor
/// \param[in] _param Param to move from
public: Param(Param &&_param) noexcept = default;

/// \brief Copy assignment operator
/// Note that the updateFunc member will not get copied
/// \param[in] _param The parameter to set values from.
/// \return *This
public: Param &operator=(const Param &_param);

/// \brief Move assignment operator
/// \param[in] _param Param to move from
/// \returns Reference to this
public: Param &operator=(Param &&_param) noexcept = default;

/// \brief Destructor
public: virtual ~Param();

Expand All @@ -116,6 +152,18 @@ namespace sdf
/// \return String containing the default value of the parameter.
public: std::string GetDefaultAsString() const;

/// \brief Get the minimum allowed value as a string
/// \return Returns a string containing the minimum allowed value of the
/// parameter if the minimum value is specified in the SDFormat description
/// of the parameter. nullopt otherwise.
public: std::optional<std::string> GetMinValueAsString() const;

/// \brief Get the maximum allowed value as a string
/// \return Returns a string containing the maximum allowed value of the
/// parameter if the maximum value is specified in the SDFormat description
/// of the parameter. nullopt otherwise.
public: std::optional<std::string> GetMaxValueAsString() const;

/// \brief Set the parameter value from a string.
/// \param[in] _value New value for the parameter in string form.
public: bool SetFromString(const std::string &_value);
Expand Down Expand Up @@ -186,12 +234,6 @@ namespace sdf
public: template<typename T>
bool GetDefault(T &_value) const;

/// \brief Equal operator. Set's the value and default value from the
/// provided Param.
/// \param[in] _param The parameter to set values from.
/// \return *This
public: Param &operator=(const Param &_param);

/// \brief Set the description of the parameter.
/// \param[in] _desc New description for the parameter.
public: void SetDescription(const std::string &_desc);
Expand All @@ -200,6 +242,10 @@ namespace sdf
/// \return The description of the parameter.
public: std::string GetDescription() const;

/// \brief Validate the value against minimum and maximum allowed values
/// \return True if the value is valid
public: bool ValidateValue() const;

/// \brief Ostream operator. Outputs the parameter's value.
/// \param[in] _out Output stream.
/// \param[in] _p The parameter to output.
Expand Down Expand Up @@ -258,6 +304,12 @@ namespace sdf

/// \brief This parameter's default value
public: ParamVariant defaultValue;

/// \brief This parameter's minimum allowed value
public: std::optional<ParamVariant> minValue;

/// \brief This parameter's maximum allowed value
public: std::optional<ParamVariant> maxValue;
};

///////////////////////////////////////////////
Expand Down
24 changes: 24 additions & 0 deletions src/Element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@ void Element::AddValue(const std::string &_type,
_type, _defaultValue, _required, _description);
}

/////////////////////////////////////////////////
void Element::AddValue(const std::string &_type,
const std::string &_defaultValue,
bool _required,
const std::string &_minValue,
const std::string &_maxValue,
const std::string &_description)
{
this->dataPtr->value =
std::make_shared<Param>(this->dataPtr->name, _type, _defaultValue,
_required, _minValue, _maxValue, _description);
}

/////////////////////////////////////////////////
ParamPtr Element::CreateParam(const std::string &_key,
const std::string &_type,
Expand Down Expand Up @@ -251,6 +264,17 @@ void Element::PrintDescription(const std::string &_prefix) const
<< "'"
<< " default ='" << this->dataPtr->value->GetDefaultAsString()
<< "'";
auto minValue = this->dataPtr->value->GetMinValueAsString();
if (minValue.has_value())
{
std::cout << " min ='" << *minValue << "'";
}

auto maxValue = this->dataPtr->value->GetMaxValueAsString();
if (maxValue.has_value())
{
std::cout << " max ='" << *maxValue << "'";
}
}

std::cout << ">\n";
Expand Down
121 changes: 113 additions & 8 deletions src/Param.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,42 @@ Param::Param(const std::string &_key, const std::string &_typeName,
this->dataPtr->defaultValue = this->dataPtr->value;
}

//////////////////////////////////////////////////
Param::Param(const std::string &_key, const std::string &_typeName,
const std::string &_default, bool _required,
const std::string &_minValue, const std::string &_maxValue,
const std::string &_description)
: Param(_key, _typeName, _default, _required, _description)
{
auto valCopy = this->dataPtr->value;
if (!_minValue.empty())
{
SDF_ASSERT(
this->ValueFromString(_minValue),
std::string("Invalid [min] parameter in SDFormat description of [") +
_key + "]");
this->dataPtr->minValue = this->dataPtr->value;
}

if (!_maxValue.empty())
{
SDF_ASSERT(
this->ValueFromString(_maxValue),
std::string("Invalid [max] parameter in SDFormat description of [") +
_key + "]");
this->dataPtr->maxValue = this->dataPtr->value;
}

this->dataPtr->value = valCopy;
}

Param::Param(const Param &_param)
: dataPtr(std::make_unique<ParamPrivate>(*_param.dataPtr))
{
// We don't want to copy the updateFunc
this->dataPtr->updateFunc = nullptr;
}

//////////////////////////////////////////////////
Param::~Param()
{
Expand All @@ -80,9 +116,11 @@ Param::~Param()
/////////////////////////////////////////////////
Param &Param::operator=(const Param &_param)
{
this->dataPtr->value = _param.dataPtr->value;
this->dataPtr->defaultValue = _param.dataPtr->defaultValue;
this->dataPtr->set = _param.dataPtr->set;
auto updateFuncCopy = this->dataPtr->updateFunc;
*this = Param(_param);

// Restore the update func
this->dataPtr->updateFunc = updateFuncCopy;
return *this;
}

Expand Down Expand Up @@ -272,6 +310,32 @@ std::string Param::GetDefaultAsString() const
return ss.str();
}

//////////////////////////////////////////////////
std::optional<std::string> Param::GetMinValueAsString() const
{
if (this->dataPtr->minValue.has_value())
{
StringStreamClassicLocale ss;

ss << ParamStreamer{ *this->dataPtr->minValue };
return ss.str();
}
return std::nullopt;
}

//////////////////////////////////////////////////
std::optional<std::string> Param::GetMaxValueAsString() const
{
if (this->dataPtr->maxValue.has_value())
{
StringStreamClassicLocale ss;

ss << ParamStreamer{ *this->dataPtr->maxValue };
return ss.str();
}
return std::nullopt;
}

//////////////////////////////////////////////////
/// \brief Helper function for Param::ValueFromString
/// \param[in] _input Input string.
Expand Down Expand Up @@ -478,11 +542,19 @@ bool Param::SetFromString(const std::string &_value)
return true;
}

auto oldValue = this->dataPtr->value;
if (!this->ValueFromString(str))
{
return false;
}

// Check if the value is permitted
if (!this->ValidateValue())
{
this->dataPtr->value = oldValue;
return false;
}

this->dataPtr->set = true;
return this->dataPtr->set;
}
Expand All @@ -497,11 +569,7 @@ void Param::Reset()
//////////////////////////////////////////////////
ParamPtr Param::Clone() const
{
ParamPtr clone(new Param(this->dataPtr->key, this->dataPtr->typeName,
this->GetAsString(), this->dataPtr->required,
this->dataPtr->description));
clone->dataPtr->set = this->dataPtr->set;
return clone;
return std::make_shared<Param>(*this);
}

//////////////////////////////////////////////////
Expand Down Expand Up @@ -539,3 +607,40 @@ bool Param::GetSet() const
{
return this->dataPtr->set;
}

/////////////////////////////////////////////////
bool Param::ValidateValue() const
{
return std::visit(
[this](const auto &_val) -> bool
{
using T = std::decay_t<decltype(_val)>;
// cppcheck-suppress syntaxError
if constexpr (std::is_scalar_v<T>)
{
if (this->dataPtr->minValue.has_value())
{
if (_val < std::get<T>(*this->dataPtr->minValue))
{
sdferr << "The value [" << _val
<< "] is less than the minimum allowed value of ["
<< *this->GetMinValueAsString() << "] for key ["
<< this->GetKey() << "]\n";
return false;
}
}
if (this->dataPtr->maxValue.has_value())
{
if (_val > std::get<T>(*this->dataPtr->maxValue))
{
sdferr << "The value [" << _val
<< "] is greater than the maximum allowed value of ["
<< *this->GetMaxValueAsString() << "] for key ["
<< this->GetKey() << "]\n";
return false;
}
}
}
return true;
}, this->dataPtr->value);
}
22 changes: 22 additions & 0 deletions src/Param_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,28 @@ TEST(Param, SetTemplate)
EXPECT_DOUBLE_EQ(value, 25.456);
}

////////////////////////////////////////////////////
TEST(Param, MinMaxViolation)
{
sdf::Param doubleParam("key", "double", "1.0", false, "0", "10.0",
"description");
{
double value;
EXPECT_TRUE(doubleParam.Get<double>(value));
EXPECT_DOUBLE_EQ(value, 1.0);
}

EXPECT_FALSE(doubleParam.Set<double>(-1.));
EXPECT_FALSE(doubleParam.Set<double>(11.));
EXPECT_TRUE(doubleParam.Set<double>(5.));

{
double value;
EXPECT_TRUE(doubleParam.Get<double>(value));
EXPECT_DOUBLE_EQ(value, 5.0);
}
}

/////////////////////////////////////////////////
/// Main
int main(int argc, char **argv)
Expand Down
17 changes: 16 additions & 1 deletion src/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,22 @@ bool initXml(TiXmlElement *_xml, ElementPtr _sdf)
description = descChild->GetText();
}

_sdf->AddValue(elemTypeString, elemDefaultValue, required, description);
std::string minValue;
const char *elemMinValue = _xml->Attribute("min");
if (nullptr != elemMinValue)
{
minValue = elemMinValue;
}

std::string maxValue;
const char *elemMaxValue = _xml->Attribute("max");
if (nullptr != elemMaxValue)
{
maxValue = elemMaxValue;
}

_sdf->AddValue(elemTypeString, elemDefaultValue, required, minValue,
maxValue, description);
}

// Get all attributes
Expand Down
Loading