libcamera: yaml_parser: Replace ok flag to get() with std::optional

The YamlObject::get() function takes a default value and an optional
bool ok flag to handle parsing errors. This ad-hoc mechanism complicates
error handling in callers.

A better API is possible by dropping the default value and ok flag and
returning an std::optional. Not only does it simplify the calls, it also
lets callers handle errors through the standard std::optional class
instead of the current ad-hoc mechanism.

Provide a get() wrapper around std::optional::value_or() to further
simplify callers that don't need any specific error handling.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Tested-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
Laurent Pinchart 2022-07-26 04:33:19 +03:00
parent 22ffeae04d
commit feb8c9be78
3 changed files with 96 additions and 122 deletions

View file

@ -9,6 +9,7 @@
#include <iterator> #include <iterator>
#include <map> #include <map>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
@ -165,7 +166,13 @@ public:
#else #else
template<typename T> template<typename T>
#endif #endif
T get(const T &defaultValue, bool *ok = nullptr) const; std::optional<T> get() const;
template<typename T>
T get(const T &defaultValue) const
{
return get<T>().value_or(defaultValue);
}
DictAdapter asDict() const { return DictAdapter{ dictionary_ }; } DictAdapter asDict() const { return DictAdapter{ dictionary_ }; }
ListAdapter asList() const { return ListAdapter{ list_ }; } ListAdapter asList() const { return ListAdapter{ list_ }; }

View file

@ -31,12 +31,6 @@ namespace {
/* Empty static YamlObject as a safe result for invalid operations */ /* Empty static YamlObject as a safe result for invalid operations */
static const YamlObject empty; static const YamlObject empty;
void setOk(bool *ok, bool result)
{
if (ok)
*ok = result;
}
} /* namespace */ } /* namespace */
/** /**
@ -100,54 +94,52 @@ std::size_t YamlObject::size() const
} }
/** /**
* \fn template<typename T> YamlObject::get<T>( * \fn template<typename T> YamlObject::get<T>() const
* const T &defaultValue, bool *ok) const
* \brief Parse the YamlObject as a \a T value * \brief Parse the YamlObject as a \a T value
* \param[in] defaultValue The default value when failing to parse
* \param[out] ok The result of whether the parse succeeded
* *
* This function parses the value of the YamlObject as a \a T object, and * This function parses the value of the YamlObject as a \a T object, and
* returns the value. If parsing fails (usually because the YamlObject doesn't * returns the value. If parsing fails (usually because the YamlObject doesn't
* store a \a T value), the \a defaultValue is returned, and \a ok is set to * store a \a T value), std::nullopt is returned.
* false. Otherwise, the YamlObject value is returned, and \a ok is set to true.
* *
* The \a ok pointer is optional and can be a nullptr if the caller doesn't * \return The YamlObject value, or std::nullopt if parsing failed
* need to know if parsing succeeded. */
/**
* \fn template<typename T> YamlObject::get<T>(const T &defaultValue) const
* \brief Parse the YamlObject as a \a T value
* \param[in] defaultValue The default value when failing to parse
* *
* \return Value as a bool type * This function parses the value of the YamlObject as a \a T object, and
* returns the value. If parsing fails (usually because the YamlObject doesn't
* store a \a T value), the \a defaultValue is returned.
*
* \return The YamlObject value, or \a defaultValue if parsing failed
*/ */
#ifndef __DOXYGEN__ #ifndef __DOXYGEN__
template<> template<>
bool YamlObject::get(const bool &defaultValue, bool *ok) const std::optional<bool> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
if (value_ == "true") { if (value_ == "true")
setOk(ok, true);
return true; return true;
} else if (value_ == "false") { else if (value_ == "false")
setOk(ok, true);
return false; return false;
}
return defaultValue; return {};
} }
template<> template<>
int16_t YamlObject::get(const int16_t &defaultValue, bool *ok) const std::optional<int16_t> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
if (value_ == "") if (value_ == "")
return defaultValue; return {};
char *end; char *end;
@ -157,22 +149,19 @@ int16_t YamlObject::get(const int16_t &defaultValue, bool *ok) const
if ('\0' != *end || errno == ERANGE || if ('\0' != *end || errno == ERANGE ||
value < std::numeric_limits<int16_t>::min() || value < std::numeric_limits<int16_t>::min() ||
value > std::numeric_limits<int16_t>::max()) value > std::numeric_limits<int16_t>::max())
return defaultValue; return {};
setOk(ok, true);
return value; return value;
} }
template<> template<>
uint16_t YamlObject::get(const uint16_t &defaultValue, bool *ok) const std::optional<uint16_t> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
if (value_ == "") if (value_ == "")
return defaultValue; return {};
/* /*
* libyaml parses all scalar values as strings. When a string has * libyaml parses all scalar values as strings. When a string has
@ -183,7 +172,7 @@ uint16_t YamlObject::get(const uint16_t &defaultValue, bool *ok) const
*/ */
std::size_t found = value_.find_first_not_of(" \t"); std::size_t found = value_.find_first_not_of(" \t");
if (found != std::string::npos && value_[found] == '-') if (found != std::string::npos && value_[found] == '-')
return defaultValue; return {};
char *end; char *end;
@ -193,22 +182,19 @@ uint16_t YamlObject::get(const uint16_t &defaultValue, bool *ok) const
if ('\0' != *end || errno == ERANGE || if ('\0' != *end || errno == ERANGE ||
value < std::numeric_limits<uint16_t>::min() || value < std::numeric_limits<uint16_t>::min() ||
value > std::numeric_limits<uint16_t>::max()) value > std::numeric_limits<uint16_t>::max())
return defaultValue; return {};
setOk(ok, true);
return value; return value;
} }
template<> template<>
int32_t YamlObject::get(const int32_t &defaultValue, bool *ok) const std::optional<int32_t> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
if (value_ == "") if (value_ == "")
return defaultValue; return {};
char *end; char *end;
@ -218,22 +204,19 @@ int32_t YamlObject::get(const int32_t &defaultValue, bool *ok) const
if ('\0' != *end || errno == ERANGE || if ('\0' != *end || errno == ERANGE ||
value < std::numeric_limits<int32_t>::min() || value < std::numeric_limits<int32_t>::min() ||
value > std::numeric_limits<int32_t>::max()) value > std::numeric_limits<int32_t>::max())
return defaultValue; return {};
setOk(ok, true);
return value; return value;
} }
template<> template<>
uint32_t YamlObject::get(const uint32_t &defaultValue, bool *ok) const std::optional<uint32_t> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
if (value_ == "") if (value_ == "")
return defaultValue; return {};
/* /*
* libyaml parses all scalar values as strings. When a string has * libyaml parses all scalar values as strings. When a string has
@ -244,7 +227,7 @@ uint32_t YamlObject::get(const uint32_t &defaultValue, bool *ok) const
*/ */
std::size_t found = value_.find_first_not_of(" \t"); std::size_t found = value_.find_first_not_of(" \t");
if (found != std::string::npos && value_[found] == '-') if (found != std::string::npos && value_[found] == '-')
return defaultValue; return {};
char *end; char *end;
@ -254,22 +237,19 @@ uint32_t YamlObject::get(const uint32_t &defaultValue, bool *ok) const
if ('\0' != *end || errno == ERANGE || if ('\0' != *end || errno == ERANGE ||
value < std::numeric_limits<uint32_t>::min() || value < std::numeric_limits<uint32_t>::min() ||
value > std::numeric_limits<uint32_t>::max()) value > std::numeric_limits<uint32_t>::max())
return defaultValue; return {};
setOk(ok, true);
return value; return value;
} }
template<> template<>
double YamlObject::get(const double &defaultValue, bool *ok) const std::optional<double> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
if (value_ == "") if (value_ == "")
return defaultValue; return {};
char *end; char *end;
@ -277,50 +257,38 @@ double YamlObject::get(const double &defaultValue, bool *ok) const
double value = std::strtod(value_.c_str(), &end); double value = std::strtod(value_.c_str(), &end);
if ('\0' != *end || errno == ERANGE) if ('\0' != *end || errno == ERANGE)
return defaultValue; return {};
setOk(ok, true);
return value; return value;
} }
template<> template<>
std::string YamlObject::get(const std::string &defaultValue, bool *ok) const std::optional<std::string> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::Value) if (type_ != Type::Value)
return defaultValue; return {};
setOk(ok, true);
return value_; return value_;
} }
template<> template<>
Size YamlObject::get(const Size &defaultValue, bool *ok) const std::optional<Size> YamlObject::get() const
{ {
setOk(ok, false);
if (type_ != Type::List) if (type_ != Type::List)
return defaultValue; return {};
if (list_.size() != 2) if (list_.size() != 2)
return defaultValue; return {};
/* auto width = list_[0]->get<uint32_t>();
* Add a local variable to validate each dimension in case if (!width)
* that ok == nullptr. return {};
*/
bool valid;
uint32_t width = list_[0]->get<uint32_t>(0, &valid);
if (!valid)
return defaultValue;
uint32_t height = list_[1]->get<uint32_t>(0, &valid); auto height = list_[1]->get<uint32_t>();
if (!valid) if (!height)
return defaultValue; return {};
setOk(ok, true); return Size(*width, *height);
return Size(width, height);
} }
#endif /* __DOXYGEN__ */ #endif /* __DOXYGEN__ */

View file

@ -148,7 +148,6 @@ protected:
} }
/* Test string object */ /* Test string object */
bool ok;
auto &strObj = (*root)["string"]; auto &strObj = (*root)["string"];
if (strObj.isDictionary()) { if (strObj.isDictionary()) {
@ -161,27 +160,27 @@ protected:
return TestFail; return TestFail;
} }
if (strObj.get<string>("", &ok) != "libcamera" || !ok) { if (strObj.get<string>().value_or("") != "libcamera") {
cerr << "String object parse as wrong content" << std::endl; cerr << "String object parse as wrong content" << std::endl;
return TestFail; return TestFail;
} }
if (strObj.get<int32_t>(-1, &ok) != -1 || ok) { if (strObj.get<int32_t>()) {
cerr << "String object parse as integer" << std::endl; cerr << "String object parse as integer" << std::endl;
return TestFail; return TestFail;
} }
if (strObj.get<uint32_t>(1, &ok) != 1 || ok) { if (strObj.get<uint32_t>()) {
cerr << "String object parse as unsigned integer" << std::endl; cerr << "String object parse as unsigned integer" << std::endl;
return TestFail; return TestFail;
} }
if (strObj.get<double>(1.0, &ok) != 1.0 || ok) { if (strObj.get<double>()) {
cerr << "String object parse as double" << std::endl; cerr << "String object parse as double" << std::endl;
return TestFail; return TestFail;
} }
if (strObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) { if (strObj.get<Size>()) {
cerr << "String object parse as Size" << std::endl; cerr << "String object parse as Size" << std::endl;
return TestFail; return TestFail;
} }
@ -199,27 +198,27 @@ protected:
return TestFail; return TestFail;
} }
if (int32Obj.get<int32_t>(-100, &ok) != -100 || !ok) { if (int32Obj.get<int32_t>().value_or(0) != -100) {
cerr << "Integer object parse as wrong value" << std::endl; cerr << "Integer object parse as wrong value" << std::endl;
return TestFail; return TestFail;
} }
if (int32Obj.get<string>("", &ok) != "-100" || !ok) { if (int32Obj.get<string>().value_or("") != "-100") {
cerr << "Integer object fail to parse as string" << std::endl; cerr << "Integer object fail to parse as string" << std::endl;
return TestFail; return TestFail;
} }
if (int32Obj.get<double>(1.0, &ok) != -100.0 || !ok) { if (int32Obj.get<double>().value_or(0.0) != -100.0) {
cerr << "Integer object fail to parse as double" << std::endl; cerr << "Integer object fail to parse as double" << std::endl;
return TestFail; return TestFail;
} }
if (int32Obj.get<uint32_t>(1, &ok) != 1 || ok) { if (int32Obj.get<uint32_t>()) {
cerr << "Negative integer object parse as unsigned integer" << std::endl; cerr << "Negative integer object parse as unsigned integer" << std::endl;
return TestFail; return TestFail;
} }
if (int32Obj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) { if (int32Obj.get<Size>()) {
cerr << "Integer object parse as Size" << std::endl; cerr << "Integer object parse as Size" << std::endl;
return TestFail; return TestFail;
} }
@ -237,27 +236,27 @@ protected:
return TestFail; return TestFail;
} }
if (uint32Obj.get<int32_t>(-1, &ok) != 100 || !ok) { if (uint32Obj.get<int32_t>().value_or(0) != 100) {
cerr << "Unsigned integer object fail to parse as integer" << std::endl; cerr << "Unsigned integer object fail to parse as integer" << std::endl;
return TestFail; return TestFail;
} }
if (uint32Obj.get<string>("", &ok) != "100" || !ok) { if (uint32Obj.get<string>().value_or("") != "100") {
cerr << "Unsigned integer object fail to parse as string" << std::endl; cerr << "Unsigned integer object fail to parse as string" << std::endl;
return TestFail; return TestFail;
} }
if (uint32Obj.get<double>(1.0, &ok) != 100.0 || !ok) { if (uint32Obj.get<double>().value_or(0.0) != 100.0) {
cerr << "Unsigned integer object fail to parse as double" << std::endl; cerr << "Unsigned integer object fail to parse as double" << std::endl;
return TestFail; return TestFail;
} }
if (uint32Obj.get<uint32_t>(100, &ok) != 100 || !ok) { if (uint32Obj.get<uint32_t>().value_or(0) != 100) {
cerr << "Unsigned integer object parsed as wrong value" << std::endl; cerr << "Unsigned integer object parsed as wrong value" << std::endl;
return TestFail; return TestFail;
} }
if (uint32Obj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) { if (uint32Obj.get<Size>()) {
cerr << "Unsigned integer object parsed as Size" << std::endl; cerr << "Unsigned integer object parsed as Size" << std::endl;
return TestFail; return TestFail;
} }
@ -275,27 +274,27 @@ protected:
return TestFail; return TestFail;
} }
if (doubleObj.get<string>("", &ok) != "3.14159" || !ok) { if (doubleObj.get<string>().value_or("") != "3.14159") {
cerr << "Double object fail to parse as string" << std::endl; cerr << "Double object fail to parse as string" << std::endl;
return TestFail; return TestFail;
} }
if (doubleObj.get<double>(1.0, &ok) != 3.14159 || !ok) { if (doubleObj.get<double>().value_or(0.0) != 3.14159) {
cerr << "Double object parse as wrong value" << std::endl; cerr << "Double object parse as wrong value" << std::endl;
return TestFail; return TestFail;
} }
if (doubleObj.get<int32_t>(-1, &ok) != -1 || ok) { if (doubleObj.get<int32_t>()) {
cerr << "Double object parse as integer" << std::endl; cerr << "Double object parse as integer" << std::endl;
return TestFail; return TestFail;
} }
if (doubleObj.get<uint32_t>(1, &ok) != 1 || ok) { if (doubleObj.get<uint32_t>()) {
cerr << "Double object parse as unsigned integer" << std::endl; cerr << "Double object parse as unsigned integer" << std::endl;
return TestFail; return TestFail;
} }
if (doubleObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) { if (doubleObj.get<Size>()) {
cerr << "Double object parse as Size" << std::endl; cerr << "Double object parse as Size" << std::endl;
return TestFail; return TestFail;
} }
@ -313,27 +312,27 @@ protected:
return TestFail; return TestFail;
} }
if (sizeObj.get<string>("", &ok) != "" || ok) { if (sizeObj.get<string>()) {
cerr << "Size object parse as string" << std::endl; cerr << "Size object parse as string" << std::endl;
return TestFail; return TestFail;
} }
if (sizeObj.get<double>(1.0, &ok) != 1.0 || ok) { if (sizeObj.get<double>()) {
cerr << "Size object parse as double" << std::endl; cerr << "Size object parse as double" << std::endl;
return TestFail; return TestFail;
} }
if (sizeObj.get<int32_t>(-1, &ok) != -1 || ok) { if (sizeObj.get<int32_t>()) {
cerr << "Size object parse as integer" << std::endl; cerr << "Size object parse as integer" << std::endl;
return TestFail; return TestFail;
} }
if (sizeObj.get<uint32_t>(1, &ok) != 1 || ok) { if (sizeObj.get<uint32_t>()) {
cerr << "Size object parse as unsigned integer" << std::endl; cerr << "Size object parse as unsigned integer" << std::endl;
return TestFail; return TestFail;
} }
if (sizeObj.get<Size>(Size(0, 0), &ok) != Size(1920, 1080) || !ok) { if (sizeObj.get<Size>().value_or(Size(0, 0)) != Size(1920, 1080)) {
cerr << "Size object parse as wrong value" << std::endl; cerr << "Size object parse as wrong value" << std::endl;
return TestFail; return TestFail;
} }
@ -351,27 +350,27 @@ protected:
return TestFail; return TestFail;
} }
if (listObj.get<string>("", &ok) != "" || ok) { if (listObj.get<string>()) {
cerr << "List object parse as string" << std::endl; cerr << "List object parse as string" << std::endl;
return TestFail; return TestFail;
} }
if (listObj.get<double>(1.0, &ok) != 1.0 || ok) { if (listObj.get<double>()) {
cerr << "List object parse as double" << std::endl; cerr << "List object parse as double" << std::endl;
return TestFail; return TestFail;
} }
if (listObj.get<int32_t>(-1, &ok) != -1 || ok) { if (listObj.get<int32_t>()) {
cerr << "List object parse as integer" << std::endl; cerr << "List object parse as integer" << std::endl;
return TestFail; return TestFail;
} }
if (listObj.get<uint32_t>(1, &ok) != 1 || ok) { if (listObj.get<uint32_t>()) {
cerr << "List object parse as unsigne integer" << std::endl; cerr << "List object parse as unsigne integer" << std::endl;
return TestFail; return TestFail;
} }
if (listObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) { if (listObj.get<Size>()) {
cerr << "String list object parse as Size" << std::endl; cerr << "String list object parse as Size" << std::endl;
return TestFail; return TestFail;
} }
@ -424,27 +423,27 @@ protected:
return TestFail; return TestFail;
} }
if (dictObj.get<string>("", &ok) != "" || ok) { if (dictObj.get<string>()) {
cerr << "Dictionary object parse as string" << std::endl; cerr << "Dictionary object parse as string" << std::endl;
return TestFail; return TestFail;
} }
if (dictObj.get<double>(1.0, &ok) != 1.0 || ok) { if (dictObj.get<double>()) {
cerr << "Dictionary object parse as double" << std::endl; cerr << "Dictionary object parse as double" << std::endl;
return TestFail; return TestFail;
} }
if (dictObj.get<int32_t>(-1, &ok) != -1 || ok) { if (dictObj.get<int32_t>()) {
cerr << "Dictionary object parse as integer" << std::endl; cerr << "Dictionary object parse as integer" << std::endl;
return TestFail; return TestFail;
} }
if (dictObj.get<uint32_t>(1, &ok) != 1 || ok) { if (dictObj.get<uint32_t>()) {
cerr << "Dictionary object parse as unsigned integer" << std::endl; cerr << "Dictionary object parse as unsigned integer" << std::endl;
return TestFail; return TestFail;
} }
if (dictObj.get<Size>(Size(0, 0), &ok) != Size(0, 0) || ok) { if (dictObj.get<Size>()) {
cerr << "Dictionary object parse as Size" << std::endl; cerr << "Dictionary object parse as Size" << std::endl;
return TestFail; return TestFail;
} }