diff options
16 files changed, 237 insertions, 23 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp index 5b344f6e1..334288ebf 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp @@ -223,6 +223,23 @@ void AbstractMetaFunction::setExplicit(bool isExplicit) d->m_explicit = isExplicit; } +bool AbstractMetaFunction::returnsBool() const +{ + if (d->m_type.typeUsagePattern() != AbstractMetaType::PrimitivePattern) + return false; + auto *pte = static_cast<const PrimitiveTypeEntry *>(d->m_type.typeEntry()); + // Walk along typedefs + while (auto *referencedPte = pte->referencedTypeEntry()) + pte =referencedPte; + return pte->name() == u"bool"; +} + +bool AbstractMetaFunction::isOperatorBool() const +{ + return d->m_functionType == AbstractMetaFunction::ConversionOperator + && d->m_constant && returnsBool(); +} + AbstractMetaFunction::AbstractMetaFunction() : d(new AbstractMetaFunctionPrivate) { } @@ -1100,6 +1117,18 @@ bool AbstractMetaFunction::isAssignmentOperator() const || d->m_functionType == MoveAssignmentOperatorFunction; } +bool AbstractMetaFunction::isGetter() const +{ + return d->m_functionType == NormalFunction && !isVoid() + && d->m_constant && d->m_access == Access::Public + && d->m_arguments.isEmpty(); +} + +bool AbstractMetaFunction::isQtIsNullMethod() const +{ + return isGetter() && d->m_name == u"isNull" && returnsBool(); +} + int AbstractMetaFunction::arityOfOperator() const { if (!isOperatorOverload() || isCallOperator()) diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.h b/sources/shiboken6/ApiExtractor/abstractmetafunction.h index 177eb5ea2..9bdbafb3d 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetafunction.h +++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.h @@ -191,6 +191,8 @@ public: bool isExplicit() const; void setExplicit(bool isExplicit); + bool returnsBool() const; + bool isOperatorBool() const; static bool isConversionOperator(const QString& funcName); ExceptionSpecification exceptionSpecification() const; @@ -210,6 +212,9 @@ public: bool isLogicalOperator() const; bool isSubscriptOperator() const; bool isAssignmentOperator() const; // Assignment or move assignment + bool isGetter() const; + /// Returns whether it is a Qt-style isNull() method suitable for nb_bool + bool isQtIsNullMethod() const; /** * Informs the arity of the operator or -1 if the function is not diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.cpp b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp index 3c33f52bc..0caf4812e 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetalang.cpp @@ -607,6 +607,28 @@ AbstractMetaFunctionCPtr AbstractMetaClass::findFunction(const QString &function return AbstractMetaFunction::find(d->m_functions, functionName); } +AbstractMetaFunctionCPtr AbstractMetaClass::findOperatorBool() const +{ + auto it = std::find_if(d->m_functions.cbegin(), d->m_functions.cend(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isOperatorBool(); + }); + if (it == d->m_functions.cend()) + return {}; + return *it; +} + +AbstractMetaFunctionCPtr AbstractMetaClass::findQtIsNullMethod() const +{ + auto it = std::find_if(d->m_functions.cbegin(), d->m_functions.cend(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isQtIsNullMethod(); + }); + if (it == d->m_functions.cend()) + return {}; + return *it; +} + bool AbstractMetaClass::hasProtectedFunctions() const { for (const auto &func : d->m_functions) { diff --git a/sources/shiboken6/ApiExtractor/abstractmetalang.h b/sources/shiboken6/ApiExtractor/abstractmetalang.h index daa9eba1e..7b5ef4cc2 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken6/ApiExtractor/abstractmetalang.h @@ -95,6 +95,9 @@ public: void addFunction(const AbstractMetaFunctionCPtr &function); bool hasFunction(const QString &str) const; AbstractMetaFunctionCPtr findFunction(const QString& functionName) const; + AbstractMetaFunctionCPtr findOperatorBool() const; + // Find a Qt-style isNull() method suitable for nb_bool + AbstractMetaFunctionCPtr findQtIsNullMethod() const; bool hasSignal(const AbstractMetaFunction *f) const; bool hasConstructors() const; diff --git a/sources/shiboken6/ApiExtractor/typesystem.cpp b/sources/shiboken6/ApiExtractor/typesystem.cpp index acc4f7009..c15abf69a 100644 --- a/sources/shiboken6/ApiExtractor/typesystem.cpp +++ b/sources/shiboken6/ApiExtractor/typesystem.cpp @@ -1179,6 +1179,8 @@ public: TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified; TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Unspecified; + TypeSystem::BoolCast m_operatorBoolMode = TypeSystem::BoolCast::Unspecified; + TypeSystem::BoolCast m_isNullMode = TypeSystem::BoolCast::Unspecified; }; ComplexTypeEntry::ComplexTypeEntry(const QString &entryName, TypeEntry::Type t, @@ -1199,6 +1201,30 @@ void ComplexTypeEntry::setTypeFlags(TypeFlags flags) d->m_typeFlags = flags; } +TypeSystem::BoolCast ComplexTypeEntry::operatorBoolMode() const +{ + S_D(const ComplexTypeEntry); + return d->m_operatorBoolMode; +} + +void ComplexTypeEntry::setOperatorBoolMode(TypeSystem::BoolCast b) +{ + S_D(ComplexTypeEntry); + d->m_operatorBoolMode = b; +} + +TypeSystem::BoolCast ComplexTypeEntry::isNullMode() const +{ + S_D(const ComplexTypeEntry); + return d->m_isNullMode; +} + +void ComplexTypeEntry::setIsNullMode(TypeSystem::BoolCast b) +{ + S_D(ComplexTypeEntry); + d->m_isNullMode = b; +} + ComplexTypeEntry::TypeFlags ComplexTypeEntry::typeFlags() const { S_D(const ComplexTypeEntry); diff --git a/sources/shiboken6/ApiExtractor/typesystem.h b/sources/shiboken6/ApiExtractor/typesystem.h index 8d2169731..bd3d2c189 100644 --- a/sources/shiboken6/ApiExtractor/typesystem.h +++ b/sources/shiboken6/ApiExtractor/typesystem.h @@ -513,6 +513,13 @@ public: TypeFlags typeFlags() const; void setTypeFlags(TypeFlags flags); + // Override command line options to generate nb_bool from + // operator bool or method isNull(). + TypeSystem::BoolCast operatorBoolMode() const; + void setOperatorBoolMode(TypeSystem::BoolCast b); + TypeSystem::BoolCast isNullMode() const; + void setIsNullMode(TypeSystem::BoolCast b); + FunctionModificationList functionModifications() const; void setFunctionModifications(const FunctionModificationList &functionModifications); void addFunctionModification(const FunctionModification &functionModification); diff --git a/sources/shiboken6/ApiExtractor/typesystem_enums.h b/sources/shiboken6/ApiExtractor/typesystem_enums.h index 1953fef6a..1a972ec1f 100644 --- a/sources/shiboken6/ApiExtractor/typesystem_enums.h +++ b/sources/shiboken6/ApiExtractor/typesystem_enums.h @@ -92,6 +92,12 @@ enum Visibility { // For namespaces Auto }; +enum class BoolCast { // Generate nb_bool (overriding command line) + Unspecified, + Disabled, + Enabled +}; + enum : int { OverloadNumberUnset = -1, OverloadNumberDefault = 99999 }; } // namespace TypeSystem diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp index bdab34126..c821a15e7 100644 --- a/sources/shiboken6/ApiExtractor/typesystemparser.cpp +++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp @@ -79,8 +79,10 @@ static inline QString generateGetSetDefAttribute() { return QStringLiteral("gene static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); } static inline QString indexAttribute() { return QStringLiteral("index"); } static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); } +static inline QString isNullAttribute() { return QStringLiteral("isNull"); } static inline QString locationAttribute() { return QStringLiteral("location"); } static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); } +static inline QString operatorBoolAttribute() { return QStringLiteral("operator-bool"); } static inline QString pyiTypeAttribute() { return QStringLiteral("pyi-type"); } static inline QString overloadNumberAttribute() { return QStringLiteral("overload-number"); } static inline QString ownershipAttribute() { return QStringLiteral("owner"); } @@ -222,6 +224,17 @@ ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive, }; ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::BoolCast, Qt::CaseInsensitive, + boolCastFromAttribute) + { + {u"yes", TypeSystem::BoolCast::Enabled}, + {u"true", TypeSystem::BoolCast::Enabled}, + {u"no", TypeSystem::BoolCast::Disabled}, + {u"false", TypeSystem::BoolCast::Disabled}, + }; +ENUM_LOOKUP_LINEAR_SEARCH() + ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive, languageFromAttribute) { @@ -1639,6 +1652,24 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } + } else if (name == isNullAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto boolCastOpt = boolCastFromAttribute(attribute.value()); + if (boolCastOpt.has_value()) { + ctype->setIsNullMode(boolCastOpt.value()); + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == operatorBoolAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto boolCastOpt = boolCastFromAttribute(attribute.value()); + if (boolCastOpt.has_value()) { + ctype->setOperatorBoolMode(boolCastOpt.value()); + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } } } diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 182e3a112..25f34bd40 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -279,19 +279,23 @@ QList<AbstractMetaFunctionCList> AbstractMetaFunctionCPtr CppGenerator::boolCast(const AbstractMetaClass *metaClass) const { - if (!useIsNullAsNbNonZero()) - return {}; - // TODO: This could be configurable someday - const auto func = metaClass->findFunction(QLatin1String("isNull")); - if (func.isNull() || func->isVoid() || !func->type().typeEntry()->isPrimitive() - || !func->isPublic()) { - return {}; + const auto *te = metaClass->typeEntry(); + auto mode = te->operatorBoolMode(); + if (useOperatorBoolAsNbNonZero() + ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { + const auto func = metaClass->findOperatorBool(); + if (!func.isNull()) + return func; + } + + mode = te->isNullMode(); + if (useIsNullAsNbNonZero() + ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { + const auto func = metaClass->findQtIsNullMethod(); + if (!func.isNull()) + return func; } - auto pte = static_cast<const PrimitiveTypeEntry *>(func->type().typeEntry()); - while (pte->referencedTypeEntry()) - pte = pte->referencedTypeEntry(); - return func->isConstant() && pte->name() == QLatin1String("bool") - && func->arguments().isEmpty() ? func : AbstractMetaFunctionCPtr{}; + return {}; } std::optional<AbstractMetaType> @@ -686,15 +690,20 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject *self)\n" << "{\n" << indent; writeCppSelfDefinition(s, classContext); - if (f->allowThread()) { - s << "int result;\n" - << BEGIN_ALLOW_THREADS << '\n' - << "result = !" << CPP_SELF_VAR << "->isNull();\n" - << END_ALLOW_THREADS << '\n' - << "return result;\n"; - } else { - s << "return !" << CPP_SELF_VAR << "->isNull();\n"; - } + + const bool allowThread = f->allowThread(); + if (allowThread) + s << "int result;\n" << BEGIN_ALLOW_THREADS << "\nresult = "; + else + s << "return "; + + if (f->isOperatorBool()) + s << '*' << CPP_SELF_VAR << " ? 1 : 0;\n"; + else + s << CPP_SELF_VAR << "->isNull() ? 0 : 1;\n"; + + if (allowThread) + s << END_ALLOW_THREADS << "\nreturn result;\n"; s << outdent << "}\n\n"; } diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp index 233b95776..ae187e536 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -57,6 +57,7 @@ static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions"; static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages"; static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero"; +static const char USE_OPERATOR_BOOL_AS_NB_NONZERO[] = "use-operator-bool-as-nb_nonzero"; static const char WRAPPER_DIAGNOSTICS[] = "wrapper-diagnostics"; const char *CPP_ARG = "cppArg"; @@ -2367,6 +2368,9 @@ Generator::OptionDescriptions ShibokenGenerator::options() const {QLatin1String(USE_ISNULL_AS_NB_NONZERO), QLatin1String("If a class have an isNull() const method, it will be used to compute\n" "the value of boolean casts")}, + {QLatin1String(USE_OPERATOR_BOOL_AS_NB_NONZERO), + QLatin1String("If a class has an operator bool, it will be used to compute\n" + "the value of boolean casts")}, {QLatin1String(WRAPPER_DIAGNOSTICS), QLatin1String("Generate diagnostic code around wrappers")} }; @@ -2384,6 +2388,8 @@ bool ShibokenGenerator::handleOption(const QString &key, const QString & /* valu return (m_verboseErrorMessagesDisabled = true); if (key == QLatin1String(USE_ISNULL_AS_NB_NONZERO)) return (m_useIsNullAsNbNonZero = true); + if (key == QLatin1String(USE_OPERATOR_BOOL_AS_NB_NONZERO)) + return (m_useOperatorBoolAsNbNonZero = true); if (key == QLatin1String(AVOID_PROTECTED_HACK)) return (m_avoidProtectedHack = true); if (key == QLatin1String(WRAPPER_DIAGNOSTICS)) @@ -2491,6 +2497,11 @@ bool ShibokenGenerator::useIsNullAsNbNonZero() const return m_useIsNullAsNbNonZero; } +bool ShibokenGenerator::useOperatorBoolAsNbNonZero() const +{ + return m_useOperatorBoolAsNbNonZero; +} + bool ShibokenGenerator::avoidProtectedHack() const { return m_avoidProtectedHack; diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h index ba904ea49..c4c93a600 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.h +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h @@ -325,6 +325,8 @@ protected: bool useReturnValueHeuristic() const; /// Returns true if the generator should use the result of isNull()const to compute boolean casts. bool useIsNullAsNbNonZero() const; + /// Returns true if the generator should use operator bool to compute boolean casts. + bool useOperatorBoolAsNbNonZero() const; /// Returns true if the generated code should use the "#define protected public" hack. bool avoidProtectedHack() const; static QString cppApiVariableName(const QString &moduleName = QString()); @@ -518,6 +520,7 @@ private: bool m_usePySideExtensions = false; bool m_verboseErrorMessagesDisabled = false; bool m_useIsNullAsNbNonZero = false; + bool m_useOperatorBoolAsNbNonZero = false; bool m_avoidProtectedHack = false; bool m_wrapperDiagnostics = false; diff --git a/sources/shiboken6/tests/libsample/pen.cpp b/sources/shiboken6/tests/libsample/pen.cpp index 1f39e7cbb..e538a6a49 100644 --- a/sources/shiboken6/tests/libsample/pen.cpp +++ b/sources/shiboken6/tests/libsample/pen.cpp @@ -45,6 +45,35 @@ bool Color::isNull() const return m_null; } +Brush::Brush(const Color &c) : m_color(c) +{ +} + +Brush::operator bool() const +{ + return !m_color.isNull(); +} + +Brush::Style Brush::style() const +{ + return m_style; +} + +void Brush::setStyle(Style newStyle) +{ + m_style = newStyle; +} + +const Color &Brush::color() const +{ + return m_color; +} + +void Brush::setColor(const Color &newColor) +{ + m_color = newColor; +} + Pen::Pen() : m_ctor(EmptyCtor) { } diff --git a/sources/shiboken6/tests/libsample/pen.h b/sources/shiboken6/tests/libsample/pen.h index 20977fdeb..4feb9f540 100644 --- a/sources/shiboken6/tests/libsample/pen.h +++ b/sources/shiboken6/tests/libsample/pen.h @@ -44,6 +44,26 @@ private: bool m_null; }; +class LIBSAMPLE_API Brush +{ +public: + enum Style { Solid, Cross }; + + explicit Brush(const Color &c = {}); + + operator bool() const; + + Style style() const; + void setStyle(Style newStyle); + + const Color &color() const; + void setColor(const Color &newColor); + +private: + Style m_style = Solid; + Color m_color; +}; + class LIBSAMPLE_API Pen { public: diff --git a/sources/shiboken6/tests/samplebinding/CMakeLists.txt b/sources/shiboken6/tests/samplebinding/CMakeLists.txt index cfdc21041..558140279 100644 --- a/sources/shiboken6/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken6/tests/samplebinding/CMakeLists.txt @@ -15,6 +15,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/base4_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/base5_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/base6_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/blackbox_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/brush_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/bytearray_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/bucket_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/classwithfunctionpointer_wrapper.cpp diff --git a/sources/shiboken6/tests/samplebinding/nonzero_test.py b/sources/shiboken6/tests/samplebinding/nonzero_test.py index 0e14b674b..16f675547 100644 --- a/sources/shiboken6/tests/samplebinding/nonzero_test.py +++ b/sources/shiboken6/tests/samplebinding/nonzero_test.py @@ -38,14 +38,23 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from shiboken_paths import init_paths init_paths() -from sample import * +from sample import Color, Brush class TestNonZeroOperator(unittest.TestCase): - def testIt(self): + def testColor(self): + """Color has a Qt-style isNull()""" c = Color() self.assertFalse(c) c = Color(2) self.assertTrue(c) + def testBrush(self): + """Brush enables its operator bool in the typesystem XML""" + b = Brush() + self.assertFalse(b) + b = Brush(Color(2)) + self.assertTrue(b) + + if __name__ == "__main__": unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml index d86a637ff..e479256b7 100644 --- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -2324,6 +2324,9 @@ </value-type> <value-type name="Color" /> + <value-type name="Brush" operator-bool="true"> + <enum-type name="Style"/> + </value-type> <value-type name="Pen"> <enum-type identified-by-value="EnumCtor"/> <enum-type name="RenderHints"/> |