diff --git a/src/Base/QuantityPyImp.cpp b/src/Base/QuantityPyImp.cpp index ce84681dfa..617b914046 100644 --- a/src/Base/QuantityPyImp.cpp +++ b/src/Base/QuantityPyImp.cpp @@ -22,7 +22,12 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include +#include +#include +#include +#include #endif #include "Unit.h" @@ -33,7 +38,7 @@ #include "UnitPy.h" -using namespace Base; +using Base::Quantity; // returns a string which represents the object e.g. when printed in python std::string QuantityPy::representation() const @@ -181,84 +186,128 @@ PyObject* QuantityPy::getUserPreferred(PyObject* /*args*/) const PyObject* QuantityPy::getValueAs(PyObject* args) const { - Quantity quant; - quant.setInvalid(); - - // first try Quantity - if (!quant.isValid()) { + auto tryQuantity = [&]() -> std::optional { PyObject* object {}; - if (PyArg_ParseTuple(args, "O!", &(QuantityPy::Type), &object)) { - quant = *static_cast(object)->getQuantityPtr(); + if (!PyArg_ParseTuple(args, "O!", &(QuantityPy::Type), &object)) { + return std::nullopt; } - } - if (!quant.isValid()) { + return *getQuantityPtr(); + }; + + auto tryUnit = [&]() -> std::optional { PyObject* object {}; - PyErr_Clear(); - if (PyArg_ParseTuple(args, "O!", &(UnitPy::Type), &object)) { - quant.setUnit(*static_cast(object)->getUnitPtr()); - quant.setValue(1.0); + if (!PyArg_ParseTuple(args, "O!", &(UnitPy::Type), &object)) { + return std::nullopt; } - } - if (!quant.isValid()) { + return Quantity {1.0, *static_cast(object)->getUnitPtr()}; + }; + + auto tryUnitAndValue = [&]() -> std::optional { PyObject* object {}; double value {}; - PyErr_Clear(); - if (PyArg_ParseTuple(args, "dO!", &value, &(UnitPy::Type), &object)) { - quant.setUnit(*static_cast(object)->getUnitPtr()); - quant.setValue(value); + if (!PyArg_ParseTuple(args, "dO!", &value, &(UnitPy::Type), &object)) { + return std::nullopt; } - } - if (!quant.isValid()) { + return Quantity {value, *static_cast(object)->getUnitPtr()}; + }; + + auto tryUnitPartsAndValue = [&]() -> std::optional { double f = std::numeric_limits::max(); - int i1 = 0; - int i2 = 0; - int i3 = 0; - int i4 = 0; - int i5 = 0; - int i6 = 0; - int i7 = 0; - int i8 = 0; + int i1 {0}; + int i2 {0}; + int i3 {0}; + int i4 {0}; + int i5 {0}; + int i6 {0}; + int i7 {0}; + int i8 {0}; PyErr_Clear(); - if (PyArg_ParseTuple(args, "d|iiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { - if (f < std::numeric_limits::max()) { - quant = Quantity(f, - Unit {static_cast(i1), - static_cast(i2), - static_cast(i3), - static_cast(i4), - static_cast(i5), - static_cast(i6), - static_cast(i7), - static_cast(i8)}); + if (!PyArg_ParseTuple(args, "d|iiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { + return std::nullopt; + } + + if (f >= std::numeric_limits::max()) { + return std::nullopt; + } + + auto re = [](auto val) { + return static_cast(val); + }; + + return Quantity {f, Unit {re(i1), re(i2), re(i3), re(i4), re(i5), re(i6), re(i7), re(i8)}}; + }; + + auto tryString = [&]() -> std::optional { + char* string {}; + if (!PyArg_ParseTuple(args, "et", "utf-8", &string)) { + return std::nullopt; + } + + const std::string str {string}; + PyMem_Free(string); + return Quantity::parse(str); + }; + + const std::vector()>> funcs = {tryQuantity, + tryUnit, + tryUnitAndValue, + tryUnitPartsAndValue, + tryString}; + + auto tryFuncs = [&]() -> std::optional { + for (const auto& func : funcs) { + PyErr_Clear(); + if (auto quant = func(); quant.has_value()) { + return quant; } } + return std::nullopt; + }; + + auto checkQuant = [&](const Quantity& quant) -> bool { + auto err = [&](const std::string& str) { + PyErr_SetString(PyExc_ValueError, str.c_str()); + }; + + const auto* qPtr = getQuantityPtr(); + if (!qPtr) { + err("QuantityPtr is null"); + return false; + } + + const auto qpUnit = qPtr->getUnit(); + if (qpUnit.isEmpty()) { + err("QuantityPtr returned empty unit"); + return false; + } + + if (const auto qUnit = quant.getUnit(); qUnit != qpUnit) { + err("Unit mismatch (`" + qUnit.getString() + "` != `" + qpUnit.getString() + "`)"); + return false; + } + + return true; + }; + + //---------------------------------------------------------------------------------------------- + + const auto optQuant = tryFuncs(); + if (!optQuant.has_value()) { + PyErr_SetString(PyExc_TypeError, "Expected quantity, string, float or unit"); + return nullptr; } - if (!quant.isValid()) { - PyErr_Clear(); - char* string {}; - if (PyArg_ParseTuple(args, "et", "utf-8", &string)) { - std::string str(string); - PyMem_Free(string); - quant = Quantity::parse(str); + const auto quant = optQuant.value(); + if (quant.isQuantity()) { + if (!checkQuant(quant)) { + return nullptr; } } - if (!quant.isValid()) { - PyErr_SetString(PyExc_TypeError, "Either quantity, string, float or unit expected"); - return nullptr; - } - - if (getQuantityPtr()->getUnit() != quant.getUnit() && quant.isQuantity()) { - PyErr_SetString(PyExc_ValueError, "Unit mismatch"); - return nullptr; - } - - quant = Quantity(getQuantityPtr()->getValueAs(quant)); - return new QuantityPy(new Quantity(quant)); + return new QuantityPy(new Quantity(getQuantityPtr()->getValue() / quant.getValue())); } PyObject* QuantityPy::__round__(PyObject* args) const