diff options
Diffstat (limited to 'sources/shiboken2/ApiExtractor/abstractmetalang.cpp')
-rw-r--r-- | sources/shiboken2/ApiExtractor/abstractmetalang.cpp | 398 |
1 files changed, 281 insertions, 117 deletions
diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 5be7050bf..c65d7e0bd 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -27,10 +27,13 @@ ****************************************************************************/ #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typedatabase.h" #include "typesystem.h" +#include <parser/codemodel.h> + #ifndef QT_NO_DEBUG_STREAM # include <QtCore/QMetaEnum> # include <QtCore/QMetaObject> @@ -55,6 +58,16 @@ QDebug operator<<(QDebug d, const AbstractMetaAttributes *aa) } #endif // !QT_NO_DEBUG_STREAM +template <class MetaClass> +MetaClass *findByName(QVector<MetaClass *> haystack, QStringView needle) +{ + for (MetaClass *c : haystack) { + if (c->name() == needle) + return c; + } + return nullptr; +} + /******************************************************************************* * AbstractMetaVariable */ @@ -112,8 +125,8 @@ void AbstractMetaAttributes::assignMetaAttributes(const AbstractMetaAttributes & AbstractMetaType::AbstractMetaType() : m_constant(false), + m_volatile(false), m_cppInstantiation(true), - m_indirections(0), m_reserved(0) { } @@ -155,8 +168,9 @@ AbstractMetaType *AbstractMetaType::copy() const cpy->setTypeUsagePattern(typeUsagePattern()); cpy->setConstant(isConstant()); + cpy->setVolatile(isVolatile()); cpy->setReferenceType(referenceType()); - cpy->setIndirections(indirections()); + cpy->setIndirectionsV(indirectionsV()); cpy->setInstantiations(instantiations()); cpy->setArrayElementCount(arrayElementCount()); cpy->setOriginalTypeDescription(originalTypeDescription()); @@ -278,6 +292,26 @@ bool AbstractMetaType::hasTemplateChildren() const return false; } +bool AbstractMetaType::equals(const AbstractMetaType &rhs) const +{ + if (m_typeEntry != rhs.m_typeEntry || m_constant != rhs.m_constant + || m_referenceType != rhs.m_referenceType + || m_indirections != rhs.m_indirections + || m_instantiations.size() != rhs.m_instantiations.size() + || m_arrayElementCount != rhs.m_arrayElementCount) { + return false; + } + if ((m_arrayElementType != nullptr) != (rhs.m_arrayElementType != nullptr) + || (m_arrayElementType != nullptr && !m_arrayElementType->equals(*rhs.m_arrayElementType))) { + return false; + } + for (int i = 0, size = m_instantiations.size(); i < size; ++i) { + if (!m_instantiations.at(i)->equals(*rhs.m_instantiations.at(i))) + return false; + } + return true; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaType *at) { @@ -291,16 +325,32 @@ QDebug operator<<(QDebug d, const AbstractMetaType *at) d << ", typeEntry=" << at->typeEntry() << ", signature=\"" << at->cppSignature() << "\", pattern=" << at->typeUsagePattern(); - if (at->indirections()) - d << ", indirections=" << at->indirections(); + const auto indirections = at->indirectionsV(); + if (!indirections.isEmpty()) { + d << ", indirections="; + for (auto i : indirections) + d << ' ' << TypeInfo::indirectionKeyword(i); + } if (at->referenceType()) d << ", reftype=" << at->referenceType(); if (at->isConstant()) d << ", [const]"; + if (at->isVolatile()) + d << ", [volatile]"; if (at->isArray()) { d << ", array of \"" << at->arrayElementType()->cppSignature() << "\", arrayElementCount=" << at->arrayElementCount(); } + const auto &instantiations = at->instantiations(); + if (const int instantiationsSize = instantiations.size()) { + d << ", instantiations[" << instantiationsSize << "]=<"; + for (int i = 0; i < instantiationsSize; ++i) { + if (i) + d << ", "; + d << instantiations.at(i); + } + } + d << '>'; } } else { d << '0'; @@ -357,7 +407,8 @@ AbstractMetaFunction::AbstractMetaFunction() m_userAdded(false), m_explicit(false), m_pointerOperator(false), - m_isCallOperator(false) + m_isCallOperator(false), + m_generateExceptionHandling(false) { } @@ -475,6 +526,8 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const if (type()) cpy->setType(type()->copy()); cpy->setConstant(isConstant()); + cpy->setExceptionSpecification(m_exceptionSpecification); + cpy->setGenerateExceptionHandling(m_generateExceptionHandling); for (AbstractMetaArgument *arg : m_arguments) cpy->addArgument(arg->copy()); @@ -504,18 +557,17 @@ QStringList AbstractMetaFunction::introspectionCompatibleSignatures(const QStrin if (arguments.size() == resolvedArguments.size()) { QString signature = name() + QLatin1Char('(') + resolvedArguments.join(QLatin1Char(',')) + QLatin1Char(')'); return QStringList(TypeDatabase::normalizedSignature(signature)); - } else { - QStringList returned; - - AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); - QStringList minimalTypeSignature = argument->type()->minimalSignature().split(QLatin1String("::")); - for (int i = 0; i < minimalTypeSignature.size(); ++i) { - returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) - << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); - } + } + QStringList returned; - return returned; + AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); + QStringList minimalTypeSignature = argument->type()->minimalSignature().split(QLatin1String("::")); + for (int i = 0; i < minimalTypeSignature.size(); ++i) { + returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) + << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); } + + return returned; } QString AbstractMetaFunction::signature() const @@ -674,38 +726,54 @@ bool AbstractMetaFunction::argumentRemoved(int key) const return false; } -bool AbstractMetaFunction::isVirtualSlot() const +bool AbstractMetaFunction::isDeprecated() const { const FunctionModificationList &modifications = this->modifications(declaringClass()); for (const FunctionModification &modification : modifications) { - if (modification.isVirtualSlot()) + if (modification.isDeprecated()) return true; } - return false; } -bool AbstractMetaFunction::isDeprecated() const +// Auto-detect whether a function should be wrapped into +// Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, that is, temporarily release +// the GIL (global interpreter lock). Doing so is required for any thread-wait +// functions, anything that might call a virtual function (potentially +// reimplemented in Python), and recommended for lengthy I/O or similar. +// It has performance costs, though. +bool AbstractMetaFunction::autoDetectAllowThread() const { - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.isDeprecated()) - return true; - } - return false; + // Disallow for simple getter functions. + const bool maybeGetter = m_constant != 0 && m_type != nullptr + && m_arguments.isEmpty(); + return !maybeGetter; } bool AbstractMetaFunction::allowThread() const { - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.allowThread()) - return true; + using AllowThread = TypeSystem::AllowThread; + + if (m_cachedAllowThread < 0) { + AllowThread allowThread = AllowThread::Auto; + // Find a modification that specifies allowThread + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + if (modification.allowThread() != AllowThread::Unspecified) { + allowThread = modification.allowThread(); + break; + } + } + + m_cachedAllowThread = allowThread == AllowThread::Allow + || (allowThread == AllowThread::Auto && autoDetectAllowThread()) ? 1 : 0; + + if (m_cachedAllowThread == 0) + qCDebug(lcShiboken).noquote() << msgDisallowThread(this); } - return false; + return m_cachedAllowThread > 0; } - TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int key) const { const FunctionModificationList &modifications = this->modifications(cls); @@ -751,6 +819,18 @@ QString AbstractMetaFunction::typeReplaced(int key) const return QString(); } +bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == argumentIndex && argumentModification.array != 0) + return true; + } + } + return false; +} + QString AbstractMetaFunction::minimalSignature() const { if (!m_cachedMinimalSignature.isEmpty()) @@ -809,8 +889,9 @@ FunctionModificationList AbstractMetaFunction::modifications(const AbstractMetaC while (implementor) { mods += implementor->typeEntry()->functionModifications(minimalSignature()); if ((implementor == implementor->baseClass()) || - (implementor == implementingClass() && (mods.size() > 0))) + (implementor == implementingClass() && !mods.isEmpty())) { break; + } const AbstractMetaClassList &interfaces = implementor->interfaces(); for (const AbstractMetaClass *interface : interfaces) mods += this->modifications(interface); @@ -873,14 +954,24 @@ bool AbstractMetaFunction::hasSignatureModifications() const return false; } -bool AbstractMetaFunction::isConversionOperator(QString funcName) +bool AbstractMetaFunction::isConversionOperator(const QString& funcName) { static const QRegularExpression opRegEx(QStringLiteral("^operator(?:\\s+(?:const|volatile))?\\s+(\\w+\\s*)&?$")); Q_ASSERT(opRegEx.isValid()); return opRegEx.match(funcName).hasMatch(); } -bool AbstractMetaFunction::isOperatorOverload(QString funcName) +ExceptionSpecification AbstractMetaFunction::exceptionSpecification() const +{ + return m_exceptionSpecification; +} + +void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e) +{ + m_exceptionSpecification = e; +} + +bool AbstractMetaFunction::isOperatorOverload(const QString& funcName) { if (isConversionOperator(funcName)) return true; @@ -1038,6 +1129,13 @@ bool function_sorter(AbstractMetaFunction *a, AbstractMetaFunction *b) return a->signature() < b->signature(); } +AbstractMetaFunction * +AbstractMetaFunction::find(const AbstractMetaFunctionList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} + #ifndef QT_NO_DEBUG_STREAM static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction *af) { @@ -1046,7 +1144,20 @@ static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction void AbstractMetaFunction::formatDebugVerbose(QDebug &d) const { - d << m_functionType << ' ' << m_type << ' ' << m_name << '('; + d << m_functionType << ' ' << m_type << ' ' << m_name; + switch (m_exceptionSpecification) { + case ExceptionSpecification::Unknown: + break; + case ExceptionSpecification::NoExcept: + d << " noexcept"; + break; + case ExceptionSpecification::Throws: + d << " throw(...)"; + break; + } + if (m_generateExceptionHandling) + d << "[generate-exception-handling]"; + d << '('; for (int i = 0, count = m_arguments.size(); i < count; ++i) { if (i) d << ", "; @@ -1106,7 +1217,6 @@ AbstractMetaClass::AbstractMetaClass() : m_hasVirtuals(false), m_isPolymorphic(false), m_hasNonpublic(false), - m_hasVirtualSlots(false), m_hasNonPrivateConstructor(false), m_hasPrivateConstructor(false), m_functionsFixed(false), @@ -1330,11 +1440,8 @@ void AbstractMetaClass::setFunctions(const AbstractMetaFunctionList &functions) for (AbstractMetaFunction *f : qAsConst(m_functions)) { f->setOwnerClass(this); - - m_hasVirtualSlots = m_hasVirtualSlots || f->isVirtualSlot(); - m_hasVirtuals = m_hasVirtuals || f->isVirtualSlot() || hasVirtualDestructor(); - m_isPolymorphic = m_isPolymorphic || m_hasVirtuals; - m_hasNonpublic = m_hasNonpublic || !f->isPublic(); + if (!f->isPublic()) + m_hasNonpublic = true; } } @@ -1368,8 +1475,7 @@ void AbstractMetaClass::addFunction(AbstractMetaFunction *function) else Q_ASSERT(false); //memory leak - m_hasVirtualSlots |= function->isVirtualSlot(); - m_hasVirtuals |= function->isVirtual() || function->isVirtualSlot() || hasVirtualDestructor(); + m_hasVirtuals |= function->isVirtual() || hasVirtualDestructor(); m_isPolymorphic |= m_hasVirtuals; m_hasNonpublic |= !function->isPublic(); } @@ -1432,11 +1538,7 @@ bool AbstractMetaClass::hasFunction(const QString &str) const const AbstractMetaFunction* AbstractMetaClass::findFunction(const QString& functionName) const { - for (const AbstractMetaFunction *f : m_functions) { - if (f->name() == functionName) - return f; - } - return 0; + return AbstractMetaFunction::find(m_functions, functionName); } bool AbstractMetaClass::hasProtectedFunctions() const @@ -1511,6 +1613,13 @@ void AbstractMetaClass::setTemplateBaseClassInstantiations(AbstractMetaTypeList& metaClassBaseTemplateInstantiations()->insert(this, instantiations); } +// Does any of the base classes require deletion in the main thread? +bool AbstractMetaClass::deleteInMainThread() const +{ + return typeEntry()->deleteInMainThread() + || (m_baseClass && m_baseClass->deleteInMainThread()); +} + static bool functions_contains(const AbstractMetaFunctionList &l, const AbstractMetaFunction *func) { for (const AbstractMetaFunction *f : l) { @@ -1537,6 +1646,11 @@ AbstractMetaField *AbstractMetaField::copy() const return returned; } +AbstractMetaField *AbstractMetaField::find(const AbstractMetaFieldList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} /******************************************************************************* * Indicates that this field has a modification that removes it */ @@ -1723,27 +1837,22 @@ QDebug operator<<(QDebug d, const AbstractMetaEnum *ae) bool AbstractMetaClass::hasConstructors() const { - return queryFunctions(Constructors).size(); + return AbstractMetaClass::queryFirstFunction(m_functions, Constructors) != nullptr; } -bool AbstractMetaClass::hasCopyConstructor() const +const AbstractMetaFunction *AbstractMetaClass::copyConstructor() const { - const AbstractMetaFunctionList &ctors = queryFunctions(Constructors); - for (const AbstractMetaFunction* ctor : ctors) { - if (ctor->functionType() == AbstractMetaFunction::CopyConstructorFunction) - return true; + for (const AbstractMetaFunction *f : m_functions) { + if (f->functionType() == AbstractMetaFunction::CopyConstructorFunction) + return f; } - return false; + return nullptr; } bool AbstractMetaClass::hasPrivateCopyConstructor() const { - const AbstractMetaFunctionList &ctors = queryFunctions(Constructors); - for (const AbstractMetaFunction *ctor : ctors) { - if (ctor->functionType() == AbstractMetaFunction::CopyConstructorFunction && ctor->isPrivate()) - return true; - } - return false; + const AbstractMetaFunction *copyCt = copyConstructor(); + return copyCt && copyCt->isPrivate(); } void AbstractMetaClass::addDefaultConstructor() @@ -1801,85 +1910,122 @@ bool AbstractMetaClass::hasFunction(const AbstractMetaFunction *f) const return functions_contains(m_functions, f); } +bool AbstractMetaClass::generateExceptionHandling() const +{ + return queryFirstFunction(m_functions, AbstractMetaClass::Visible + | AbstractMetaClass::GenerateExceptionHandling) != nullptr; +} /* Goes through the list of functions and returns a list of all functions matching all of the criteria in \a query. */ -AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +bool AbstractMetaClass::queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query) { - AbstractMetaFunctionList functions; - - for (AbstractMetaFunction *f : m_functions) { - if ((query & NotRemovedFromTargetLang) && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) - continue; + if ((query & NotRemovedFromTargetLang) + && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) { + return false; + } - if ((query & NotRemovedFromTargetLang) && f->isVirtual() && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) - continue; + if ((query & NotRemovedFromTargetLang) && f->isVirtual() + && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) { + return false; + } - if ((query & Visible) && f->isPrivate()) - continue; + if ((query & Visible) && f->isPrivate()) + return false; - if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) - continue; + if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) + return false; - if ((query & Invisible) && !f->isPrivate()) - continue; + if ((query & Invisible) && !f->isPrivate()) + return false; - if ((query & Empty) && !f->isEmptyFunction()) - continue; + if ((query & Empty) && !f->isEmptyFunction()) + return false; - if ((query & WasPublic) && !f->wasPublic()) - continue; + if ((query & WasPublic) && !f->wasPublic()) + return false; - if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) - continue; + if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) + return false; - if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) - continue; + if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) + return false; - if ((query & VirtualInCppFunctions) && !f->isVirtual()) - continue; + if ((query & VirtualInCppFunctions) && !f->isVirtual()) + return false; - if ((query & Signals) && (!f->isSignal())) - continue; + if ((query & Signals) && (!f->isSignal())) + return false; - if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) - continue; + if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) + return false; - if (!(query & Constructors) && f->isConstructor()) - continue; + if (!(query & Constructors) && f->isConstructor()) + return false; - // Destructors are never included in the functions of a class currently - /* + // Destructors are never included in the functions of a class currently + /* if ((query & Destructors) && (!f->isDestructor() || f->ownerClass() != f->implementingClass()) || f->isDestructor() && (query & Destructors) == 0) { - continue; + return false; }*/ - if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) - continue; + if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) + return false; - if ((query & NonStaticFunctions) && (f->isStatic())) - continue; + if ((query & NonStaticFunctions) && (f->isStatic())) + return false; - if ((query & NormalFunctions) && (f->isSignal())) - continue; + if ((query & NormalFunctions) && (f->isSignal())) + return false; - if ((query & OperatorOverloads) && !f->isOperatorOverload()) - continue; + if ((query & OperatorOverloads) && !f->isOperatorOverload()) + return false; + + if ((query & GenerateExceptionHandling) && !f->generateExceptionHandling()) + return false; + + return true; +} - functions << f; +AbstractMetaFunctionList AbstractMetaClass::queryFunctionList(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + result.append(f); + } + return result; +} + +const AbstractMetaFunction *AbstractMetaClass::queryFirstFunction(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + return f; } + return nullptr; +} - return functions; +AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +{ + return AbstractMetaClass::queryFunctionList(m_functions, query); } bool AbstractMetaClass::hasSignals() const { - return cppSignalFunctions().size() > 0; + return queryFirstFunction(m_functions, Signals | Visible | NotRemovedFromTargetLang) != nullptr; } +AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const +{ + return queryFunctions(Signals | Visible | NotRemovedFromTargetLang); +} /** * Adds the specified interface to this class by adding all the @@ -1932,13 +2078,15 @@ void AbstractMetaClass::setInterfaces(const AbstractMetaClassList &interfaces) } } +AbstractMetaField *AbstractMetaClass::findField(const QString &name) const +{ + return AbstractMetaField::find(m_fields, name); +} AbstractMetaEnum *AbstractMetaClass::findEnum(const QString &enumName) { - for (AbstractMetaEnum *e : qAsConst(m_enums)) { - if (e->name() == enumName) - return e; - } + if (AbstractMetaEnum *e = findByName(m_enums, enumName)) + return e; if (typeEntry()->designatedInterface()) return extractInterface()->findEnum(enumName); @@ -2002,8 +2150,8 @@ void AbstractMetaClass::fixFunctions() { if (m_functionsFixed) return; - else - m_functionsFixed = true; + + m_functionsFixed = true; AbstractMetaClass *superClass = baseClass(); AbstractMetaFunctionList funcs = functions(); @@ -2046,8 +2194,7 @@ void AbstractMetaClass::fixFunctions() // we generally don't care about private functions, but we have to get the ones that are // virtual in case they override abstract functions. bool add = (sf->isNormal() || sf->isSignal() || sf->isEmptyFunction()); - for (int fi = 0; fi < funcs.size(); ++fi) { - AbstractMetaFunction *f = funcs.at(fi); + for (AbstractMetaFunction *f : funcs) { if (f->isRemovedFromAllLanguages(f->implementingClass())) continue; @@ -2114,7 +2261,8 @@ void AbstractMetaClass::fixFunctions() if (mod.isNonFinal()) { hasNonFinalModifier = true; break; - } else if (mod.isPrivate()) { + } + if (mod.isPrivate()) { isBaseImplPrivate = true; break; } @@ -2222,6 +2370,8 @@ QString AbstractMetaType::formatSignature(bool minimal) const QString result; if (isConstant()) result += QLatin1String("const "); + if (isVolatile()) + result += QLatin1String("volatile "); if (isArray()) { // Build nested array dimensions a[2][3] in correct order result += m_arrayElementType->minimalSignature(); @@ -2245,10 +2395,10 @@ QString AbstractMetaType::formatSignature(bool minimal) const result += QLatin1String(" >"); } - if (!minimal && (m_indirections != 0 || m_referenceType != NoReference)) + if (!minimal && (!m_indirections.isEmpty() || m_referenceType != NoReference)) result += QLatin1Char(' '); - if (m_indirections) - result += QString(m_indirections, QLatin1Char('*')); + for (Indirection i : m_indirections) + result += TypeInfo::indirectionKeyword(i); switch (referenceType()) { case NoReference: break; @@ -2376,6 +2526,8 @@ QDebug operator<<(QDebug d, const AbstractMetaClass *ac) d << " [final]"; if (ac->m_baseClass) d << ", inherits \"" << ac->m_baseClass->name() << '"'; + if (ac->m_templateBaseClass) + d << ", inherits template \"" << ac->m_templateBaseClass->name() << '"'; const AbstractMetaEnumList &enums = ac->enums(); if (!enums.isEmpty()) d << ", enums[" << enums.size() << "]=" << enums; @@ -2406,6 +2558,18 @@ QDebug operator<<(QDebug d, const AbstractMetaClass *ac) } d << ')'; } + const auto &templateArguments = ac->templateArguments(); + if (const int count = templateArguments.size()) { + d << ", templateArguments=[" << count << "]("; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + d << templateArguments.at(i); + } + d << ')'; + } + + } else { d << '0'; } |