# HG changeset patch # User eldar # Date 1257708481 0 # Node ID f9559a957be9641516f4a5df6ec192156b204e76 # Parent b6984b290e4690f6423b85516099be59d91dea18 new signals and slots implementation diff -r b6984b290e46 -r f9559a957be9 build/core.txt --- a/build/core.txt Sun Nov 08 19:20:53 2009 +0000 +++ b/build/core.txt Sun Nov 08 19:28:01 2009 +0000 @@ -17,7 +17,14 @@ qtd/Traits core/QString core/QMetaType - core/QMetaObject) + core/QMetaObject + + qtd/MetaMarshall + qtd/MOC + qtd/util/Tuple + qtd/ctfe/Integer + qtd/ctfe/String + qtd/ctfe/Format) set (d_version_files QtdObject Signal qtd/Str diff -r b6984b290e46 -r f9559a957be9 cpp/qt_core/QMetaObject_shell.cpp --- a/cpp/qt_core/QMetaObject_shell.cpp Sun Nov 08 19:20:53 2009 +0000 +++ b/cpp/qt_core/QMetaObject_shell.cpp Sun Nov 08 19:28:01 2009 +0000 @@ -10,3 +10,30 @@ { QMetaObject::activate(sender, signal_index, argv); } + +extern "C" DLL_PUBLIC void qtd_QMetaObject_activate_3(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv) +{ + QMetaObject::activate(sender, m, local_signal_index, argv); +} + +extern "C" DLL_PUBLIC void qtd_QMetaObject_activate_4(QObject *sender, const QMetaObject *m, int from_local_signal_index, int to_local_signal_index, void **argv) +{ + QMetaObject::activate(sender, m, from_local_signal_index, to_local_signal_index, argv); +} + +extern "C" DLL_PUBLIC bool qtd_QMetaObject_connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type, int *types) +{ + return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); +} + +extern "C" DLL_PUBLIC int qtd_QMetaObject_indexOfMethod(void *nativeId, const char *method) +{ + return ((QMetaObject*)nativeId)->indexOfMethod(method); +} + +extern "C" DLL_PUBLIC int qtd_QMetaObject_methodCount(void *nativeId) +{ + return ((QMetaObject*)nativeId)->methodCount(); +} diff -r b6984b290e46 -r f9559a957be9 generator/abstractmetalang.cpp --- a/generator/abstractmetalang.cpp Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/abstractmetalang.cpp Sun Nov 08 19:28:01 2009 +0000 @@ -42,6 +42,7 @@ #include "abstractmetalang.h" #include "reporthandler.h" #include "jumptable.h" +#include /******************************************************************************* * AbstractMetaType @@ -668,15 +669,15 @@ return QString(); } -QString AbstractMetaFunction::minimalSignature() const +QString AbstractMetaFunction::minimalSignature(int reduce) const { - if (!m_cached_minimal_signature.isEmpty()) + if (!m_cached_minimal_signature.isEmpty() && !reduce) return m_cached_minimal_signature; QString minimalSignature = originalName() + "("; AbstractMetaArgumentList arguments = this->arguments(); - - for (int i=0; itype(); if (i > 0) @@ -689,7 +690,8 @@ minimalSignature += "const"; minimalSignature = QMetaObject::normalizedSignature(minimalSignature.toLocal8Bit().constData()); - m_cached_minimal_signature = minimalSignature; + if(!reduce) + m_cached_minimal_signature = minimalSignature; return minimalSignature; } diff -r b6984b290e46 -r f9559a957be9 generator/abstractmetalang.h --- a/generator/abstractmetalang.h Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/abstractmetalang.h Sun Nov 08 19:28:01 2009 +0000 @@ -440,7 +440,7 @@ QString modifiedName() const; - QString minimalSignature() const; + QString minimalSignature(int reduce = 0) const; QStringList possibleIntrospectionCompatibleSignatures() const; QString marshalledName(bool classIsOwner = true) const; diff -r b6984b290e46 -r f9559a957be9 generator/cppheadergenerator.cpp --- a/generator/cppheadergenerator.cpp Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/cppheadergenerator.cpp Sun Nov 08 19:28:01 2009 +0000 @@ -197,6 +197,8 @@ // << " void *qt_metacast(const char *);" << endl // << " QT_TR_FUNCTIONS" << end << " virtual int qt_metacall(QMetaObject::Call, int, void **);" << endl + << " int __override_qt_metacall(QMetaObject::Call _c, int _id, void **_a);" << endl + << "private:" << endl; } diff -r b6984b290e46 -r f9559a957be9 generator/cppimplgenerator.cpp --- a/generator/cppimplgenerator.cpp Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/cppimplgenerator.cpp Sun Nov 08 19:28:01 2009 +0000 @@ -592,9 +592,10 @@ if (!java_class->isQObject()) writeFinalDestructor(s, java_class); - if (java_class->isQObject()) + if (java_class->isQObject()) { + writeQObjectEntity(s, java_class); writeSignalsHandling(s, java_class); - + } if (shellClass) { foreach (AbstractMetaFunction *function, java_class->functions()) { if (function->isConstructor() && !function->isPrivate()) @@ -1217,22 +1218,21 @@ << "{" << endl << "public:" << endl << " Q_OBJECT_CHECK" << endl - << " virtual int qt_metacall(QMetaObject::Call, int, void **);" << endl << endl + << "// virtual int qt_metacall(QMetaObject::Call, int, void **);" << endl << endl << " " << entityName << "(QObject *qObject, void *dId) : QObject(), QtD_QObjectEntity(qObject, dId) {}" << endl << "};" << endl << endl; - // QObject_Link::qt_metacall() +/* // QObject_Link::qt_metacall() s << "int " << entityName << "::qt_metacall(QMetaObject::Call _c, int _id, void **_a)" << endl << "{" << endl << " _id = QObject::qt_metacall(_c, _id, _a);" << endl << " if (_id < 0 || _c != QMetaObject::InvokeMetaMethod)" << endl << " return _id;" << endl -// << " Q_ASSERT(_id < 2);" << endl << " emit_callbacks_" << java_class->name() << "[_id](dId, _a);" << endl << " return -1;" << endl << "}" << endl << endl; - +*/ s << "extern \"C\" DLL_PUBLIC void qtd_" << className << "_createEntity(void *nativeId, void* dId)" << endl << "{" << endl << " new " << entityName << "((QObject*)nativeId, dId);" << endl @@ -1282,16 +1282,34 @@ << " return " << java_class->qualifiedCppName() << "::qt_metacast(_clname);" << endl << "}" << endl << endl; */ - +/* s << "int " << shellClassName(java_class) << "::qt_metacall(QMetaObject::Call _c, int _id, void **_a)" << endl - << "{" << endl; - - s << " _id = " << java_class->qualifiedCppName() << "::qt_metacall(_c, _id, _a);" << endl + << "{" << endl + << " _id = " << java_class->qualifiedCppName() << "::qt_metacall(_c, _id, _a);" << endl << " if (_id < 0 || _c != QMetaObject::InvokeMetaMethod)" << endl << " return _id;" << endl << " emit_callbacks_" << java_class->name() << "[_id](this->dId, _a);" << endl << " return -1;" << endl << "}" << endl << endl; + */ + + s << "extern \"C\" int qtd_" << java_class->name() << "_qt_metacall_dispatch(void *d_entity, QMetaObject::Call _c, int _id, void **_a);" << endl << endl + + << "int " << shellClassName(java_class) << "::qt_metacall(QMetaObject::Call _c, int _id, void **_a)" << endl + << "{" << endl + << " return qtd_" << java_class->name() << "_qt_metacall_dispatch(this->dId, _c, _id, _a);" << endl + << "}" << endl << endl + + << "int " << shellClassName(java_class) << "::__override_qt_metacall(QMetaObject::Call _c, int _id, void **_a)" << endl + << "{" << endl + << " return " << java_class->qualifiedCppName() << "::qt_metacall(_c, _id, _a);" + << "}" << endl << endl + + << "extern \"C\" DLL_PUBLIC int qtd_" << java_class->name() << "_qt_metacall(void* __this_nativeId, QMetaObject::Call _c, int _id, void **_a)" + << "{" << endl + << " " << shellClassName(java_class) << " *__qt_this = (" << shellClassName(java_class) << " *) __this_nativeId;" << endl + << " return __qt_this->__override_qt_metacall(_c, _id, _a);" << endl + << "}" << endl << endl; } void CppImplGenerator::writeSignalEmitter(QTextStream &s, const AbstractMetaClass *d_class, AbstractMetaFunction *function) @@ -1341,6 +1359,8 @@ void CppImplGenerator::writeSignalsHandling(QTextStream &s, const AbstractMetaClass *java_class) { + return; // #TODO probably don't need this function at all + s << "extern \"C\" typedef void (*EmitCallback)(void*, void**);" << endl; AbstractMetaFunctionList signal_funcs = signalFunctions(java_class); @@ -1369,8 +1389,6 @@ } s << endl << "};" << endl << endl; } - - writeQObjectEntity(s, java_class); } diff -r b6984b290e46 -r f9559a957be9 generator/dgenerator.cpp --- a/generator/dgenerator.cpp Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/dgenerator.cpp Sun Nov 08 19:28:01 2009 +0000 @@ -63,7 +63,7 @@ m_recursive(0), m_isRecursive(false) { - excludedTypes << "long long" << "bool" << "int" << "QString" << "char" << "WId" + excludedTypes << "qint64" << "bool" << "int" << "QString" << "char" << "WId" << "unsigned char" << "uint" << "double" << "short" << "float" << "signed char" << "unsigned short" << "QBool" << "unsigned int" << "Qt::HANDLE" << "QChar" << "java.lang.JObjectWrapper" << "void" @@ -126,7 +126,7 @@ QString constPrefix, constPostfix; if (d_type && d_type->isConstant() && dVersion == 2) { constPrefix = "const("; - constPostfix = ") "; + constPostfix = ")"; } if (!d_type) { @@ -230,7 +230,7 @@ QString arg; AbstractMetaType *type = d_argument->type(); - // if argument is "QString &" ref attribute needed + // qtd2 if argument is "QString &" ref attribute needed FIXME maybe we need this not only for QString, but for other Value types?? if (type->typeEntry()->isValue() && type->isNativePointer() && type->typeEntry()->name() == "QString") arg = "ref "; @@ -983,10 +983,7 @@ if (!(d_function->isEmptyFunction() || d_function->isNormal() || d_function->isSignal())) option = Option(option | SkipReturnType); writeFunctionAttributes(s, d_function, included_attributes, excluded_attributes, option); -/* - if(d_function->isSignal()) - functionName += "_emit"; -*/ + s << functionName << "("; writeFunctionArguments(s, d_function, argument_count, option); s << ")"; @@ -1713,6 +1710,8 @@ QString attr; +// return; // #TODO Don't need handlers for now. Restore in conversion functions later + s << "// signal handlers" << endl; foreach(AbstractMetaFunction *signal, signal_funcs) { QString sigExternName = signalExternName(d_class, signal); @@ -1771,7 +1770,7 @@ s << endl; } // s << INDENT << "Stdout(\"" << d_class->name() << "\", \"" << signal->name() << "\").newline;" << endl; - s << INDENT << "d_object." << signal->name() << "_emit("; + s << INDENT << "//d_object." << signal->name() << "_emit("; for (int j = 0; jindexedName(); @@ -1786,6 +1785,29 @@ } } +AbstractMetaFunctionList DGenerator::generatedClassFunctions(const AbstractMetaClass *d_class) +{ + AbstractMetaFunctionList r; + AbstractMetaFunctionList d_funcs = d_class->functionsInTargetLang(); + for (int i=0; iisInterface()) { + uint includedAttributes = 0; + uint excludedAttributes = 0; + retrieveModifications(function, d_class, &excludedAttributes, &includedAttributes); + if (includedAttributes & AbstractMetaAttributes::Private) + continue; + } + + if (!notWrappedYet(function)) // qtd2 + r += function; + } + return r; +} + void DGenerator::write(QTextStream &s, const AbstractMetaClass *d_class) { ReportHandler::debugSparse("Generating class: " + d_class->fullName()); @@ -1909,6 +1931,7 @@ << "private import qt.qtd.Array;" << endl; if (d_class->isQObject()) { s << "public import qt.Signal;" << endl + << "public import qt.qtd.MOC;" << endl << "public import qt.core.QMetaObject;" << endl << "public import qt.qtd.Traits;" << endl; @@ -2148,7 +2171,7 @@ foreach (AbstractMetaEnum *d_enum, d_class->enums()) writeEnumAlias(s, d_enum); - // Signals + // Signals if (d_class->isQObject()) { AbstractMetaFunctionList signal_funcs = signalFunctions(d_class, false); @@ -2181,22 +2204,13 @@ // Functions AbstractMetaFunctionList d_funcs = d_class->functionsInTargetLang(); - for (int i=0; iisInterface()) { - uint includedAttributes = 0; - uint excludedAttributes = 0; - retrieveModifications(function, d_class, &excludedAttributes, &includedAttributes); - if (includedAttributes & AbstractMetaAttributes::Private) - continue; - } - - if (!notWrappedYet(function)) // qtd2 - writeFunction(s, function); -// s << function->minimalSignature() << endl; + AbstractMetaFunctionList d_funcs_gen = generatedClassFunctions(d_class); + for (int i=0; iisSlot()) +// writeSlot(s, function); + writeFunction(s, function); +// qtd s << function->minimalSignature() << endl; } if(d_class->isInterface()) s << endl << INDENT << "public void* __ptr_" << d_class->name() << "();" << endl << endl; @@ -2243,6 +2257,17 @@ if (d_class->isQObject()) writeQObjectFunctions(s, d_class); + // flag to mark the type of class (to use in templates to convert arguments) + if (d_class->baseClassName().isEmpty()) + { + if (d_class->typeEntry()->isQObject()) + s << INDENT << "public static enum __isQObjectType = true;" << endl << endl; + else if (d_class->typeEntry()->isObject()) + s << INDENT << "public static enum __isObjectType = true;" << endl << endl; + else if (d_class->typeEntry()->isValue()) + s << INDENT << "public static enum __isValueType = true;" << endl << endl; + } + // Add dummy constructor for use when constructing subclasses if (!d_class->isNamespace() && !d_class->isInterface() && !fakeClass) { s << endl @@ -2633,10 +2658,7 @@ if (d_class->isQObject()) - { - s << "private extern(C) void* qtd_" << d_class->name() << "_staticMetaObject();" << endl << endl - << "private extern(C) void qtd_" << d_class->name() << "_createEntity(void* nativeId, void* dId);" << endl << endl; - } + writeQObjectFreeFunctions(s, d_class); } void DGenerator::writeConversionFunction(QTextStream &s, const AbstractMetaClass *d_class) @@ -2674,12 +2696,67 @@ s << "}" << endl << endl; } +void DGenerator::writeQObjectFreeFunctions(QTextStream &s, const AbstractMetaClass *d_class) +{ + s << "private extern(C) QMetaObjectNative* qtd_" << d_class->name() << "_staticMetaObject();" << endl << endl + << "private extern(C) void qtd_" << d_class->name() << "_createEntity(void* nativeId, void* dId);" << endl << endl; + + if (!d_class->isFinal()) + s << "private extern(C) int qtd_" << d_class->name() << "_qt_metacall(void* __this_nativeId, QMetaObject.Call _c, int _id, void **_a);" + << "private extern(C) int qtd_" << d_class->name() << "_qt_metacall_dispatch(void *d_entity, QMetaObject.Call _c, int _id, void **_a) {" + << " auto d_object = cast(" << d_class->name() << ") d_entity;" + << " return d_object.qt_metacall(_c, _id, _a);" + << "}" << endl << endl; +} + +void writeMetaMethodSignatures(QTextStream &s, const QString &var_name, AbstractMetaFunctionList meta_funcs) +{ + s << INDENT << "private static const string[] " << var_name << " = ["; + { + Indentation indent(INDENT); + for (int i = 0; i < meta_funcs.size(); ++i) + { + if (i) + s << ", "; + int j = 0; + bool hasDefault = false; + do // need this to look for default arguments and generate extra signatures + { + if (i || j) + s << ", "; + s << endl << INDENT << " \"" << meta_funcs.at(i)->minimalSignature(j) << "\""; + AbstractMetaArgumentList args = meta_funcs.at(i)->arguments(); + if(args.size() && jdefaultValueExpression().isEmpty(); + else + hasDefault = false; + j++; + } while (hasDefault); + } + } + s << INDENT << "];" << endl << endl; +} + void DGenerator::writeQObjectFunctions(QTextStream &s, const AbstractMetaClass *d_class) { - QString concreteArg; + AbstractMetaFunctionList d_funcs_gen = generatedClassFunctions(d_class); + AbstractMetaFunctionList slot_funcs; + for (int i=0; iisSlot()) + slot_funcs += function; + } + writeMetaMethodSignatures(s, "__slotSignatures", slot_funcs); + + QString concreteArg; if (d_class->isAbstract()) concreteArg += ", " + d_class->name() + "_ConcreteWrapper"; + if (!d_class->isFinal()) + s << " int qt_metacall(QMetaObject.Call _c, int _id, void **_a) {" << endl + << " return qtd_" << d_class->name() << "_qt_metacall(__nativeId, _c, _id, _a);" << endl + << " }" << endl << endl; + s << " private static QMetaObject _staticMetaObject;" << endl << " protected static void createStaticMetaObject() {" << endl << " assert(!_staticMetaObject);" << endl @@ -2695,6 +2772,7 @@ s << " _staticMetaObject = new QMetaObject(qtd_" << d_class->name() << "_staticMetaObject, base);" << endl << " _staticMetaObject.construct!(" << d_class->name() << concreteArg << ");" << endl + << " _populateMetaInfo();" << endl << " }" << endl << endl << " QMetaObject metaObject() {" << endl @@ -2711,7 +2789,64 @@ << " static void __createEntity(void* nativeId, void* dId) {" << endl << " return qtd_" << d_class->name() << "_createEntity(nativeId, dId);" << endl - << " }" << endl << endl; + << " }" << endl << endl + + << " private static void _populateMetaInfo() {" << endl + << " int index;" << endl << endl; + + AbstractMetaFunctionList signal_funcs = signalFunctions(d_class, false); + + int staticId = 0; + for (int i = 0; i < signal_funcs.size(); ++i) + { + int j = 0; + bool hasDefault = false; + do // need this to look for default arguments and generate extra signatures + { + AbstractMetaFunction *fn = signal_funcs.at(i); + s << " index = _staticMetaObject.indexOfMethod_Cpp(__signalSignatures[" << staticId << "]);" << endl + << " _staticMetaObject.addMethod(new QMetaSignal(signature!(\"" << fn->name() << "\""; + if(fn->arguments().size()-j > 0) + s << ", "; + writeMetaMethodArguments(s, fn, j); + s << "), index));" << endl << endl; + AbstractMetaArgumentList args = fn->arguments(); + if(args.size() && jdefaultValueExpression().isEmpty(); + else + hasDefault = false; + j++; + staticId++; + } while (hasDefault); + } + + staticId = 0; + for (int i = 0; i < slot_funcs.size(); ++i) + { + int j = 0; + bool hasDefault = false; + do // need this to look for default arguments and generate extra signatures + { + AbstractMetaFunction *fn = slot_funcs.at(i); + s << " index = _staticMetaObject.indexOfMethod_Cpp(__slotSignatures[" << staticId << "]);" << endl + << " _staticMetaObject.addMethod(new QMetaSlot(signature!(\"" << fn->name() << "\""; + if(fn->arguments().size()-j > 0) + s << ", "; + writeMetaMethodArguments(s, fn, j); + s << "), index));" << endl << endl; + AbstractMetaArgumentList args = fn->arguments(); + if(args.size() && jdefaultValueExpression().isEmpty(); + else + hasDefault = false; + j++; + staticId++; + } while (hasDefault); + } + + s << " }" << endl << endl; + + s << INDENT << "mixin Q_OBJECT_BIND;" << endl << endl; } /* @@ -2778,17 +2913,7 @@ void DGenerator::writeSignalSignatures(QTextStream &s, const AbstractMetaClass *d_class, AbstractMetaFunctionList signal_funcs) { - s << INDENT << "private const string[" << signal_funcs.size() << "] __signalSignatures = ["; - { - Indentation indent(INDENT); - for (int i = 0; i < signal_funcs.size(); ++i) - { - if (i) - s << ", "; - s << endl << INDENT << " \"" << signal_funcs.at(i)->minimalSignature() << "\""; - } - } - s << INDENT << "];" << endl << endl; + writeMetaMethodSignatures(s, "__signalSignatures", signal_funcs); s << INDENT << "int signalSignature(int signalId, ref stringz signature) {" << endl; { @@ -2810,29 +2935,42 @@ s << INDENT << "}" << endl; } +void DGenerator::writeMetaMethodArguments(QTextStream &s, const AbstractMetaFunction *d_function, int reduce) +{ + bool withDefArgs = false; + if(reduce == -1) { + reduce = 0; + withDefArgs = true; + } + + AbstractMetaArgumentList arguments = d_function->arguments(); + int sz = arguments.count() - reduce; + + for (int i=0; itypeReplaced(i+1); + + if (modifiedType.isEmpty()) + s << translateType(arguments.at(i)->type(), d_function->implementingClass(), BoxedPrimitive); + else + s << modifiedType; + + if (!arguments.at(i)->defaultValueExpression().isEmpty() && withDefArgs) // qtd + s << " = " + arguments.at(i)->defaultValueExpression(); + } +} + void DGenerator::writeSignal(QTextStream &s, const AbstractMetaFunction *d_function) { Q_ASSERT(d_function->isSignal()); - AbstractMetaArgumentList arguments = d_function->arguments(); - int sz = arguments.count(); - - s << INDENT << "mixin BindQtSignal!(\"" << d_function->name() << "\""; - - if (sz > 0) { - for (int i=0; itypeReplaced(i+1); - - if (modifiedType.isEmpty()) - s << translateType(arguments.at(i)->type(), d_function->implementingClass(), BoxedPrimitive); - else - s << modifiedType; - } - } - - s << ");" << endl; + s << INDENT << "mixin BindQtSignal!(\"" << d_function->name() << "("; + + writeMetaMethodArguments(s, d_function); + + s << ")\");" << endl; } void DGenerator::writeShellVirtualFunction(QTextStream &s, const AbstractMetaFunction *d_function, diff -r b6984b290e46 -r f9559a957be9 generator/dgenerator.h --- a/generator/dgenerator.h Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/dgenerator.h Sun Nov 08 19:28:01 2009 +0000 @@ -149,13 +149,16 @@ void addInstantiations(const AbstractMetaType* d_type); void writeRequiredImports(QTextStream &s, const AbstractMetaClass *d_class); const TypeEntry* fixedTypeEntry(const TypeEntry *type); + AbstractMetaFunctionList generatedClassFunctions(const AbstractMetaClass *d_class); void writeDestructor(QTextStream &s, const AbstractMetaClass *d_class); void writeFlagsSetter(QTextStream &s, const AbstractMetaClass *d_class); void writeSignalHandlers(QTextStream &s, const AbstractMetaClass *d_class); void writeEnumAlias(QTextStream &s, const AbstractMetaEnum *d_enum); void writeSignalSignatures(QTextStream &s, const AbstractMetaClass *d_class, AbstractMetaFunctionList signal_funcs); + void writeMetaMethodArguments(QTextStream &s, const AbstractMetaFunction *d_function, int reduce = -1); void writeQObjectFunctions(QTextStream &s, const AbstractMetaClass *d_class); + void writeQObjectFreeFunctions(QTextStream &s, const AbstractMetaClass *d_class); void writeConversionFunction(QTextStream &s, const AbstractMetaClass *d_class); // void writeMarshallFunction(QTextStream &s, const AbstractMetaClass *d_class); diff -r b6984b290e46 -r f9559a957be9 generator/typesystem_core-java.java --- a/generator/typesystem_core-java.java Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/typesystem_core-java.java Sun Nov 08 19:28:01 2009 +0000 @@ -79,7 +79,7 @@ QObject __next; QObject __prev; } - +/* override void onSignalHandlerCreated(ref SignalHandler sh) { sh.signalEvent = &onSignalEvent; @@ -104,7 +104,7 @@ } } } - +*/ ~this() { if (__prev) @@ -154,6 +154,13 @@ find(children); return result; } + + static void connect(QObject sender, string signal, QObject receiver, string method) + { + int signalIndex = sender.metaObject.lookUpSignal(signal); + int methodIndex = receiver.metaObject.lookUpMethod(method); + QMetaObject.connect(sender, signalIndex, receiver, methodIndex); + } }// class abstract class QAbstractItemModel___ extends QAbstractItemModel { diff -r b6984b290e46 -r f9559a957be9 generator/typesystem_core.xml --- a/generator/typesystem_core.xml Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/typesystem_core.xml Sun Nov 08 19:28:01 2009 +0000 @@ -103,7 +103,7 @@ - + @@ -2282,9 +2282,10 @@ - + @@ -2302,7 +2303,7 @@ - + @@ -2477,11 +2478,11 @@ --> - + @@ -2655,7 +2656,7 @@ - + @@ -2691,7 +2692,7 @@ - + diff -r b6984b290e46 -r f9559a957be9 generator/typesystem_gui.xml --- a/generator/typesystem_gui.xml Sun Nov 08 19:20:53 2009 +0000 +++ b/generator/typesystem_gui.xml Sun Nov 08 19:28:01 2009 +0000 @@ -3703,13 +3703,13 @@ - + - + @@ -6815,25 +6815,25 @@ - + - + - + - + @@ -7235,7 +7235,7 @@ - > + diff -r b6984b290e46 -r f9559a957be9 qt/core/QMetaObject.d --- a/qt/core/QMetaObject.d Sun Nov 08 19:20:53 2009 +0000 +++ b/qt/core/QMetaObject.d Sun Nov 08 19:28:01 2009 +0000 @@ -4,14 +4,96 @@ import qt.core.QObject; import qt.QtdObject; +import std.algorithm; + +class Meta +{ + string name; +} + +class MetaType : Meta +{ + this() + { + } +} + +class MetaVariable : Meta +{ + MetaType type; +} + +class MetaCallable : Meta { } + +class MetaMethod : Meta { } + +class QMetaArgument : MetaVariable { } + +class QMetaMethod : MetaMethod +{ +// QMetaArgument[] arguments; + string signature; + int indexOfMethod; + + this(string signature_, int indexOfMethod_) + { + signature = signature_; + indexOfMethod = indexOfMethod_; + } +} + +class QMetaSignal : QMetaMethod +{ + this(string signature_, int indexOfMethod_) + { + super(signature_, indexOfMethod_); + } +} + +class QMetaSlot : QMetaMethod +{ + this(string signature_, int indexOfMethod_) + { + super(signature_, indexOfMethod_); + } +} + +class MetaObject : MetaType +{ + MetaObject _base; +} + +struct QMetaObjectNative +{ + QMetaObjectNative *superdata; + immutable(char) *stringdata; + const(uint) *data; + void *extradata; +} + final class QMetaObject { + enum Call + { + InvokeMetaMethod, + ReadProperty, + WriteProperty, + ResetProperty, + QueryPropertyDesignable, + QueryPropertyScriptable, + QueryPropertyStored, + QueryPropertyEditable, + QueryPropertyUser, + CreateInstance + } + private { - void* _nativeId; + QMetaObjectNative* _nativeId; QMetaObject _base; // super class QMetaObject _firstDerived; // head of the linked list of derived classes QMetaObject _next; // next sibling on this derivation level + QMetaMethod[] _methods; ClassInfo _classInfo; QObject function(void* nativeId) _createWrapper; @@ -24,7 +106,7 @@ } // NOTE: construction is split between this non-templated constructor and 'construct' function below. - this(void* nativeId, QMetaObject base) + this(QMetaObjectNative* nativeId, QMetaObject base) { _nativeId = nativeId; if (base) @@ -57,7 +139,7 @@ /++ +/ - void* nativeId() + QMetaObjectNative* nativeId() { return _nativeId; } @@ -69,6 +151,39 @@ return _classInfo; } + const (QMetaMethod[]) methods() + { + return _methods; + } + + void addMethod(QMetaMethod method_) + { + _methods ~= method_; + } + + int lookUpMethod(string slot) + { + foreach (method; _methods) + if (method.signature == slot) + return method.indexOfMethod; + if (_base) + return _base.lookUpMethod(slot); + else + return -1; + } + + int lookUpSignal(string signal) + { +// auto signalBegin = signal[0..$-1]; + foreach (method; _methods) + if (method.signature == signal && cast(QMetaSignal)method) + return method.indexOfMethod; + if (_base) + return _base.lookUpSignal(signal); + else + return -1; + } + private QMetaObject lookupDerived(void*[] moIds) { assert (moIds.length >= 1); @@ -123,12 +238,49 @@ moIds[--moCount] = moId = qtd_QMetaObject_superClass(moId); result = lookupDerived(moIds)._createWrapper(nativeObjId); - } + } } } return result; } + + static void activate(QObject sender, QMetaObject m, int local_signal_index, void **argv) + { + qtd_QMetaObject_activate_3(sender.__nativeId, m.nativeId, local_signal_index, argv); + } + + static void activate(QObject sender, QMetaObject m, int from_local_signal_index, int to_local_signal_index, void **argv) + { + qtd_QMetaObject_activate_4(sender.__nativeId, m.nativeId, from_local_signal_index, to_local_signal_index, argv); + } + + static bool connect(const QObject sender, int signal_index, + const QObject receiver, int method_index, + int type = 0, int *types = null) + { + return qtd_QMetaObject_connect(sender.__nativeId, signal_index, receiver.__nativeId, method_index, type, types); + } + + int indexOfMethod_Cpp(string method) + { + return qtd_QMetaObject_indexOfMethod(_nativeId, toStringz(method)); + } + + int methodCount() + { + return qtd_QMetaObject_methodCount(_nativeId); + } + } +extern(C) void qtd_QMetaObject_activate_3(void* sender, void* m, int local_signal_index, void **argv); +extern(C) void qtd_QMetaObject_activate_4(void *sender, void* m, int from_local_signal_index, int to_local_signal_index, void **argv); +extern(C) bool qtd_QMetaObject_connect(const void* sender, int signal_index, + const void* receiver, int method_index, + int type, int *types); + +extern(C) int qtd_QMetaObject_indexOfMethod(void *nativeId, const(char) *method); +extern(C) int qtd_QMetaObject_methodCount(void *nativeId); + extern(C) void* qtd_QMetaObject_superClass(void* nativeId); \ No newline at end of file diff -r b6984b290e46 -r f9559a957be9 qt/d2/qt/QtdObject.d --- a/qt/d2/qt/QtdObject.d Sun Nov 08 19:20:53 2009 +0000 +++ b/qt/d2/qt/QtdObject.d Sun Nov 08 19:28:01 2009 +0000 @@ -27,8 +27,6 @@ protected QtdObjectFlags __flags_; void* __nativeId; - mixin SignalHandlerOps; - this(void* nativeId, QtdObjectFlags flags = QtdObjectFlags.none) { __nativeId = nativeId; diff -r b6984b290e46 -r f9559a957be9 qt/d2/qt/Signal.d --- a/qt/d2/qt/Signal.d Sun Nov 08 19:20:53 2009 +0000 +++ b/qt/d2/qt/Signal.d Sun Nov 08 19:28:01 2009 +0000 @@ -12,856 +12,137 @@ module qt.Signal; public import qt.QGlobal; -public import - std.metastrings, - std.typetuple; +import qt.qtd.MetaMarshall; + import core.stdc.stdlib : crealloc = realloc, cfree = free; import core.stdc.string : memmove; import - std.traits, core.thread, - core.exception; - -private: // private by default - -alias void delegate(Object) DEvent; - -extern(C) void rt_attachDisposeEvent(Object o, DEvent e); -extern(C) void rt_detachDisposeEvent(Object o, DEvent e); -extern(C) Object _d_toObject(void* p); - -void realloc(T)(ref T[] a, size_t length) -{ - a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length]; - if (!a.ptr) - new OutOfMemoryError(__FILE__, __LINE__); -} + core.exception, + std.algorithm; - -void append(T)(ref T[] a, T element) -{ - auto newLen = a.length + 1; - a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen]; - if (!a.ptr) - new OutOfMemoryError(__FILE__, __LINE__); - a[newLen - 1] = element; -} +public import + std.typetuple, + std.traits, + std.conv, + std.string, + std.metastrings; -void move(T)(ref T[] a, size_t src, size_t dest, size_t length) -{ - if (a.length > 1) - memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); -} - -// COMPILER BUG: Though this is private cannot name it 'remove' because of conflicts -// with Array.remove -void erase(T)(ref T[] a, size_t i) + +// returns name, arguments or tuple of the function depending on type parameter +enum {_Name, _Tuple, _Args} +string getFunc(int type)(string fullName) { - auto newLen = a.length - 1; - move(a, i + 1, i, newLen); - realloc(a, newLen); -} - -version (QtdUnittest) -{ - unittest - { - int[] a; - realloc(a, 16); - assert(a.length == 16); - foreach (i, ref e; a) - e = i; - realloc(a, 4096); - assert(a.length == 4096); - foreach (i, e; a[0..16]) - assert(e == i); - cfree(a.ptr); - } -} - -//TODO: should be in the standard library -struct STuple(A...) -{ - static string genSTuple() - { - string r = ""; - foreach (i, e; A) - r ~= A[i].stringof ~ " _" ~ ToString!(i) ~ ";"; - return r; - } - - mixin (genSTuple); - template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); }; -} - -enum SignalEventId -{ - firstSlotConnected, - lastSlotDisconnected + int pos = 0; + foreach(i, c; fullName) + if (c == '(') + static if (type == _Tuple) + return fullName[i..$]; + else if (type == _Name) + return fullName[0..i]; + else if (type == _Args) + for(int j = fullName.length-1;; j--) + if(fullName[j] == ')') + return fullName[i+1 .. j]; + return null; } -public class SignalException : Exception +/** The beast that takes string representation of function arguments + * and returns an array of default values it doesn't check if arguments + * without default values follow the arguments with default values for + * simplicity. It is done by mixing in an delegate alias. + */ +string[] defaultValues(string signature) { - this(string msg) - { - super(msg); - } -} - -struct Fn -{ - void* funcptr; - - static typeof(this) opCall(R, A...)(R function(A) fn) - { - typeof(this) r; - r.funcptr = fn; - return r; - } + int braces = 0; + bool inDefaultValue = false; + bool inStringLiteral = false; + string[] res; + int startValue = 0; + + if(strip(signature).length == 0) + return res; - template call(R) - { - R call(A...)(A args) - { - alias R function(A) Fn; - return (cast(Fn)funcptr)(args); - } - } - - S get(S)() - { - static assert (is(typeof(*S.init) == function)); - return cast(S)funcptr; - } -} - -struct Dg -{ - void* context; - void* funcptr; - - static typeof(this) opCall(R, A...)(R delegate(A) dg) + foreach (i,c; signature) { - typeof(this) r; - r.context = dg.ptr; - r.funcptr = dg.funcptr; - return r; - } - - template call(R) - { - R call(A...)(A args) + if(!inStringLiteral) { - R delegate(A) dg; // BUG: parameter storage classes are ignored - dg.ptr = context; - dg.funcptr = cast(typeof(dg.funcptr))funcptr; - return dg(args); - } - } - - S get(S)() - { - static assert (is(S == delegate)); - S r; - r.ptr = context; - r.funcptr = cast(typeof(r.funcptr))funcptr; - return r; - } -} - -struct Slot(R) -{ - alias R Receiver; - - Receiver receiver; - Dg invoker; - ConnectionFlags flags; - - static if (is(Receiver == Dg)) - { - static const isDelegate = true; - - bool isDisposed() - { - return !receiver.funcptr; - } - - void dispose() - { - receiver.funcptr = null; - receiver.context = null; + if(c == '{' || c =='(') + braces++; + else if(c == '}' || c ==')') + braces--; } - Object getObject() - { - return flags & ConnectionFlags.NoObject || !receiver.context - ? null : _d_toObject(receiver.context); - } - } - else - static const isDelegate = false; -} - -enum SlotListId -{ - Func, // function pointers - Weak, // object delegates stored in C heap - Strong // delegates stored in GC heap -} - -/** - Used to specify the type of a signal-to-slot connection. - - Examples: ----- -class Sender -{ - mixin Signal!("changed"); - void change() - { - changed.emit; - } -} - - -class Receiver -{ - void alarm() {} -} - -void main() -{ - auto s = new Sender; - auto r = new Receiver; - s.changed.connect(&r.alarm); // now s weakly references r - - r = null; - // collect garbage (assume there is no more reachable pointers - // to the receiver and it gets finalized) - ... - - s.change; - // weak reference to the receiving object - // has been removed from the sender's connection lists. - - r = new Receiver; - s.changed.connect(&r.alarm, ConnectionFlags.Strong); - - r = null; - // collect garbage - ... - // the receiving object has not been finalized because s strongly references it. - - s.change; // the receiver is called. - delete r; - s.change; // the receiver is disconnected from the sender. - - static void foo() - { - } - - s.changed.connect(&foo); - s.changed.emit; // foo is called. - s.changed.disconnect(&foo); // must be explicitly disconnected. - - void bar() - { - } - - // ConnectionFlags.NoObject must be specified for delegates - // to non-static local functions or struct member functions. - s.changed.connect(&bar, ConnectionFlags.NoObject); - s.changed.emit; // bar is called. - s.changed.disconnect(&bar); // must be explicitly disconnected. -} ----- -*/ -public enum ConnectionFlags : ubyte -{ - /// - None, - /** - The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified). - If the signal receiver is not a function pointer or a delegate referencing a D class instance. - the sender will not be notified when the receiving object is deleted and emitting the signal - connected to that receiving object will result in undefined behavior. - */ - Weak = 0x0001, - /** - The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified). - */ - Strong = 0x0002, - /** - Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance. - */ - NoObject = 0x0004 - - // Queued = 0x0004, - // BlockingQueued = 0x0008 -} - - -struct SlotList(SlotT, bool strong = false) -{ - alias SlotT SlotType; - SlotType[] data; - - void length(size_t length) - { - static if (strong) - data.length = length; - else - realloc(data, length); - } - - SlotType* add(SlotType slot) - { - auto oldLen = data.length; - length = oldLen + 1; - auto p = &data[oldLen]; - *p = slot; - return p; - } - - SlotType* get(int slotId) - { - return &data[slotId]; - } - - void remove(int slotId) - { - move(data, slotId, slotId + 1, data.length - slotId - 1); - data = data[0..$ - 1]; - } - - size_t length() - { - return data.length; - } - - void free() - { - static if (!strong) - cfree(data.ptr); - } -} - -public alias void delegate(int signalId, SignalEventId event) SignalEvent; - -struct Receivers -{ - struct Data - { - Object object; - int refs; - } - - Data[] data; - void add(Object receiver, DEvent disposeEvent) - { - foreach (ref d; data) + if(c == '\"' || c == '\'') { - if (d.object is receiver) - { - d.refs++; - return; + if (inStringLiteral) + { + if(signature[i-1] != '\\') + inStringLiteral = false; + } + else + { + inStringLiteral = true; } } - append(data, Data(receiver, 1)); - rt_attachDisposeEvent(receiver, disposeEvent); - } - - void remove(Object receiver, DEvent disposeEvent) - { - foreach (i, ref d; data) + if (!inStringLiteral && braces == 0) { - if (d.object is receiver) + if(c == '=') // found default value { - assert (d.refs); - d.refs--; - if (!d.refs) + inDefaultValue = true; + startValue = i+1; + } + else if(c == ',') // next function argument + { + if (inDefaultValue) { - .erase(data, i); - rt_detachDisposeEvent(receiver, disposeEvent); + res ~= signature[startValue..i]; + inDefaultValue = false; } - return; - } - } - - assert (false); - } - - // remove all refarences for receiver, receiver has been disposed - void removeAll(Object receiver) - { - foreach (i, ref d; data) - { - if (d.object is receiver) - { - .erase(data, i); - return; } } } - // remove all references for all receivers, detaching dispose events - void free(DEvent disposeEvent) - { - foreach (i, ref d; data) - rt_detachDisposeEvent(d.object, disposeEvent); - cfree(data.ptr); - data = null; - } -} - -struct SignalConnections -{ - bool isInUse; - - STuple!( - SlotList!(Slot!(Fn)), - SlotList!(Slot!(Dg)), - SlotList!(Slot!(Dg), true) - ) slotLists; - - STuple!( - Fn[], - Dg[] - ) delayedDisconnects; - - void addDelayedDisconnect(Fn r) - { - delayedDisconnects.at!(0) ~= r; - } - - void addDelayedDisconnect(Dg r) - { - delayedDisconnects.at!(1) ~= r; - } - - SlotListType!(slotListId)* getSlotList(int slotListId)() - { - return &slotLists.tupleof[slotListId]; - } - - bool hasSlots() - { - foreach(i, e; slotLists.tupleof) - { - if (slotLists.tupleof[i].length) - return true; - } - return false; - } - - int slotCount() - { - int count; - foreach(i, e; slotLists.tupleof) - count += slotLists.at!(i).length; - return count; - } + if (inDefaultValue) + res ~= signature[startValue..$]; - void slotListLengths(int[] lengths) - { - foreach(i, e; slotLists.tupleof) - lengths[i] = slotLists.at!(i).length; - } - - SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot) - { - return getSlotList!(slotListId).add(slot); - } - - void removeSlot(int slotListId)(int slotId) - { - slotLists.at!(slotListId).remove(slotId); - } - - void free() - { - foreach(i, e; slotLists.tupleof) - { - static if (is(typeof(slotLists.at!(i).free))) - slotLists.at!(i).free; - } - } - - void onReceiverDisposed(Object receiver) - { - foreach (i, e; slotLists.tupleof) - { - static if (slotLists.at!(i).SlotType.isDelegate) - { - foreach (ref slot; slotLists.at!(i).data) - { - if (slot.getObject is receiver) - slot.dispose; - } - } - } - } - - template SlotListType(int slotListId) - { - alias typeof(slotLists.tupleof)[slotListId] SlotListType; - } - - template SlotType(int slotListId) - { - alias SlotListType!(slotListId).SlotType SlotType; - } - - template ReceiverType(int slotListId) - { - alias SlotType!(slotListId).Receiver ReceiverType; - } - - static const slotListCount = slotLists.tupleof.length; -} - - -private Object signalSender_; - -/** - If called from a slot, returns the object - that is emitting the signal. Otherwise, returns null. -*/ -public Object signalSender() { - return signalSender_; + return res; } -public final class SignalHandler +int defaultValuesLength(string[] defVals) { - SignalConnections[] connections; - Receivers receivers; - Object owner; - int blocked; - - SignalEvent signalEvent; - - alias SignalConnections.SlotType SlotType; - alias SignalConnections.ReceiverType ReceiverType; - - public this(Object owner_) { - owner = owner_; - } - - private SignalConnections* getConnections(int signalId) - { - if (signalId < connections.length) - return &connections[signalId]; - return null; - } - - private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver, - Dg invoker, ConnectionFlags flags) - { - if (signalId >= connections.length) - connections.length = signalId + 1; - auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker)); - - static if (slot.isDelegate) - { - if (!(flags & ConnectionFlags.NoObject)) - receivers.add(_d_toObject(receiver.context), &onReceiverDisposed); - } - - if (signalEvent && connections[signalId].slotCount == 1) - signalEvent(signalId, SignalEventId.firstSlotConnected); - - return slot; - } - - void onReceiverDisposed(Object receiver) - { - synchronized(this) - { - foreach(ref c; connections) - c.onReceiverDisposed(receiver); - receivers.removeAll(receiver); - } - } - - private void removeSlot(int slotListId)(int signalId, int slotId) - { - auto slot = connections[signalId].getSlotList!(slotListId).get(slotId); - static if (slot.isDelegate) - { - if (auto obj = slot.getObject) - receivers.remove(obj, &onReceiverDisposed); - } - - connections[signalId].removeSlot!(slotListId)(slotId); - - if (signalEvent && !connections[signalId].slotCount) - signalEvent(signalId, SignalEventId.lastSlotDisconnected); - } - - size_t slotCount(int signalId) - { - synchronized(this) - { - auto con = getConnections(signalId); - if (con) - return con.slotCount; - return 0; - } - } - - void connect(Receiver)(int signalId, Receiver receiver, - Dg invoker, ConnectionFlags flags) - { - synchronized(this) - { - static if (is(typeof(receiver.context))) - { - Object obj; - if ((flags & ConnectionFlags.NoObject)) - { - // strong by default - if (flags & ConnectionFlags.Weak) - addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); - else - addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); - } - else - { - // weak by default - if (flags & ConnectionFlags.Strong) - addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); - else - addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); - } - } - else - addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags); - } - } - - void disconnect(Receiver)(int signalId, Receiver receiver) - { - synchronized(this) - { - auto cons = getConnections(signalId); - if (!cons) - return; - - // if called from a slot being executed by this signal, delay disconnection - // until all slots has been called. - if (cons.isInUse) - { - cons.addDelayedDisconnect(receiver); - return; - } - - TOP: - foreach (slotListId, e; cons.slotLists.tupleof) - { - /// COMPILER BUG: ReceiverType is evaluated to expression instead of type. - static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver)) - { - auto slotList = cons.getSlotList!(slotListId); - for (int slotId; slotId < slotList.length;) - { - auto slot = slotList.get(slotId); - static if (slot.isDelegate) - { - if (slot.isDisposed) - { - removeSlot!(slotListId)(signalId, slotId); - continue; - } - } - - if (slot.receiver == receiver) - { - removeSlot!(slotListId)(signalId, slotId); - break TOP; - } - - slotId++; - } - } - } - } - } - - void emit(A...)(size_t signalId, A args) - { - synchronized(this) - { - if (signalId >= connections.length || blocked) - return; - auto cons = &connections[signalId]; - - if (cons.hasSlots) - { - { - cons.isInUse = true; - signalSender_ = owner; - scope(exit) - { - cons.isInUse = false; - signalSender_ = null; - } - - // Store the lengths to avoid calling new slots - // connected in the slots being called. - // dmd bug: int[cons.slotListCount] fails - static const c = cons.slotListCount; - int[c] lengths = void; - cons.slotListLengths(lengths); - - foreach (slotListId, e; cons.slotLists.tupleof) - { - auto slotList = cons.getSlotList!(slotListId); - for (size_t slotId; slotId < lengths[slotListId];) - { - auto slot = slotList.get(slotId); - static if (slot.isDelegate) - { - if (slot.isDisposed) - { - removeSlot!(slotListId)(signalId, slotId); - lengths[slotListId]--; - continue; - } - } - - slot.invoker.call!(void)(slot.receiver, args); - ++slotId; - } - } - } - - - // process delayed disconnects if any - foreach(i, e; cons.delayedDisconnects.tupleof) - { - if (cons.delayedDisconnects.at!(i).length) - { - foreach (d; cons.delayedDisconnects.at!(i)) - disconnect(signalId, d); - cons.delayedDisconnects.at!(i).length = 0; - } - } - } - } - } - - // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments - private void invokeSlot(S, Receiver, A...)(Receiver r, A args) - { - r.get!(S)()(args[0..ParameterTypeTuple!(S).length]); - } - - void blockSignals() - { - synchronized(this) - blocked++; - } - - void unblockSignals() - { - synchronized(this) - { - if(!blocked) - throw new SignalException("Signals are not blocked"); - blocked--; - } - } - - ~this() - { - receivers.free(&onReceiverDisposed); - foreach(ref c; connections) - c.free; - } -} - -public template SignalHandlerOps() -{ - static assert (is(typeof(this.signalHandler)), - "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes"); - -protected: - SignalHandler signalHandler_; // manages signal-to-slot connections - - final SignalHandler signalHandler() - { - if (!signalHandler_) - { - signalHandler_ = new SignalHandler(this); - onSignalHandlerCreated(signalHandler_); - } - return signalHandler_; - } - - void onSignalHandlerCreated(ref SignalHandler sh) - { - } - -public: - final void blockSignals() - { - signalHandler.blockSignals(); - } - - final void unblockSignals() - { - signalHandler.unblockSignals(); - } - - template connect(string signalName, A...) - { - static void connect(T, Func)(T sender, Func func, ConnectionFlags flags = ConnectionFlags.None) - if (isFnOrDg!(Func)) - { - alias findSignal!(T, signalName, Func, A).result sig; - auto sh = sender.signalHandler(); - static if (isFn!(Func)) - alias Fn Callable; - else - alias Dg Callable; - auto invoker = Dg(&sh.invokeSlot!(typeof(func), Callable, sig[2..$])); - sh.connect(sig[1], Callable(func), invoker, flags); - } - } - - template disconnect(string signalName, A...) - { - static void connect(T, Func)(T sender, Func func, ConnectionFlags flags = ConnectionFlags.None) - if (isFnOrDg!(Func)) - { - alias findSignal!(T, signalName, Func, A).result sig; - auto sh = sender.signalHandler(); - static if (isFn!(Func)) - alias Fn Callable; - else - alias Dg Callable; - sh.disconnect(sig[1], Callable(func)); - } - } -/* - template slotCount(string signalName, A...) - { - debug static void slotCount(T)(T sender) - { - alias findSignal!(T, signalName, Func, A).result sig; - auto sh = sender.signalHandler(); - return sh.slotCount(sig[1]); - } - } - */ + return defVals.length; } /** New implementation. */ -const string signalPrefix = "__signal"; + +// need this to mark static metamethods-info whether it's generated by presence of default args +enum DefaultArgs +{ + None, Start, Continue +} + +// templates for extracting data from static meta-information of signals, slots or properties +// public alias TypeTuple!("name", index, OwnerClass, DefaultArgs.Start, ArgTypes) __signal +template MetaEntryName(source...) +{ + enum MetaEntryName = source[0]; // name of the metaentry is the first element +} + +template MetaEntryOwner(source...) +{ + alias TupleWrapper!(source[2]).at[0] MetaEntryOwner; // class that owns the property is the third + // Compiler #BUG 3092 - evaluates MetaEntryOwner as a Tuple with one element +} + +template MetaEntryArgs(source...) +{ + alias source[4 .. $] MetaEntryArgs; // arguments-tuple starts from the fourth position +} template TupleWrapper(A...) { alias A at; } @@ -955,6 +236,14 @@ } } +template ByOwner(Owner) +{ + template ByOwner(source...) + { + enum ByOwner = is(MetaEntryOwner!source == Owner); + } +} + template staticSymbolName(string prefix, int id) { const string staticSymbolName = prefix ~ ToString!(id); @@ -965,6 +254,7 @@ const string signatureString = name ~ "(" ~ joinArgs!(A) ~ ")"; } +// recursive search in the static meta-information template findSymbolImpl(string prefix, C, int id, alias pred) { static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) ) @@ -1006,6 +296,30 @@ } } +// recursive search in the static meta-information +template findSymbolsImpl(string prefix, C, int id, alias pred) +{ + static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) ) + { + mixin ("alias C." ~ staticSymbolName!(prefix, id) ~ " current;"); + static if (pred!current) { + alias TupleWrapper!current subres; +// pragma(msg, toStringNow!id ~ " " ~ subres.stringof); + } else + alias TypeTuple!() subres; + alias TypeTuple!(subres, findSymbolsImpl!(prefix, C, id + 1, pred).result) result; + } + else + { + alias TypeTuple!() result; + } +} + +template findSymbols(string prefix, C, alias pred) +{ + alias findSymbolsImpl!(prefix, C, 0, pred).result findSymbols; +} + string __toString(long v) { if (v == 0) @@ -1032,18 +346,42 @@ return ret; } -public string SignalEmitter(A...)(SignalType signalType, string name, int index) +string convertSignalArguments(Args...)() +{ +// void *_a[] = { 0, const_cast(reinterpret_cast(&_t1)) }; + + string res = "void*[" ~ __toString(Args.length+1) ~ "] _a = [null"; + foreach(i, _; Args) + res ~= ", " ~ "cast(void*) &" ~ convertSignalArgument!(Args[i])("_t" ~ __toString(i)); + res ~= "];\n"; + return res; +} + +public string SignalEmitter(A...)(SignalType signalType, string name, string[] defVals, int localIndex) { string fullArgs, args; + int defValsLength = defVals.length; + string argsConversion = ""; + string argsPtr = "null"; static if (A.length) { - fullArgs = A[0].stringof ~ " a0"; - args = ", a0"; + while(A.length != defVals.length) + defVals = "" ~ defVals; + + fullArgs = A[0].stringof ~ " _t0"; + if (defVals[0].length) + fullArgs ~= " = " ~ defVals[0]; + args = "_t0"; foreach(i, _; A[1..$]) { - fullArgs ~= ", " ~ A[i+1].stringof ~ " a" ~ __toString(i+1); - args ~= ", a" ~ __toString(i+1); + fullArgs ~= ", " ~ A[i+1].stringof ~ " _t" ~ __toString(i+1); + if (defVals[i+1].length) + fullArgs ~= " = " ~ defVals[i+1]; + args ~= ", _t" ~ __toString(i+1); } + // build up conversion of signal args from D to C++ + argsPtr = "_a.ptr"; + argsConversion = convertSignalArguments!(A)(); } string attribute; string sigName = name; @@ -1051,197 +389,147 @@ name ~= "_emit"; else attribute = "protected "; - string str = attribute ~ "void " ~ name ~ "(" ~ fullArgs ~ ")" ~ - "{ this.signalHandler.emit(" ~ __toString(index) ~ args ~ "); }"; + + string indexArgs = __toString(localIndex); + if(defValsLength > 0) + indexArgs ~= ", " ~ __toString(localIndex+defValsLength); + string str = attribute ~ "final void " ~ name ~ "(" ~ fullArgs ~ ") {\n" ~ argsConversion ~ "\n" + ~ " QMetaObject.activate(this, typeof(this).staticMetaObject, " ~ indexArgs ~ ", " ~ argsPtr ~ ");\n" + ~ "}\n"; // ~ return str; } - /** ---------------- */ -/** - Examples: ----- -struct Args -{ - bool cancel; -} - -class C -{ - private int _x; - // reference parameters are not supported yet, - // so we pass arguments by pointer. - mixin Signal!("xChanging", int, Args*); - mixin Signal!("xChanged"); - - void x(int v) - { - if (v != _x) - { - Args args; - xChanging.emit(v, &args); - if (!args.cancel) - { - _x = v; - xChanged.emit; - } - } - } -} ----- -*/ +const string signalPrefix = "__signal"; +const string slotPrefix = "__slot"; enum SignalType { BindQtSignal, - NewSignal -} - -template BindQtSignal(string name, A...) -{ - mixin SignalImpl!(0, name, SignalType.BindQtSignal, A); -} - -template Signal(string name, A...) -{ - mixin SignalImpl!(0, name, SignalType.NewSignal, A); + NewSignal, + NewSlot } -template SignalImpl(int index, string name, SignalType signalType, A...) +template BindQtSignal(string fullName) { - static if (is(typeof(mixin(typeof(this).stringof ~ ".__signal" ~ ToString!(index))))) - mixin SignalImpl!(index + 1, name, signalType, A); - else - { - // mixed-in once - static if (!is(typeof(this.signalHandler))) - mixin SignalHandlerOps; - - mixin (SignalEmitter!(A)(signalType, name, index)); - mixin("public alias TypeTuple!(\"" ~ name ~ "\", index, A) __signal" ~ ToString!(index) ~ ";"); - } + mixin MetaMethodImpl!(signalPrefix, 0, fullName, SignalType.BindQtSignal); } -extern(C) alias void function(void*) SlotConnector; - -debug (UnitTest) +template Signal(string fullName) { - class A - { - mixin Signal!("scorched", int); - - int signalId1 = -1; - int signalId2 = -1; - - void onFirstConnect(int sId) - { - signalId1 = sId; - } - - void onLastDisconnect(int sId) - { - signalId2 = sId; - } + mixin MetaMethodImpl!(signalPrefix, 0, fullName, SignalType.NewSignal); +} - this() - { - signalHandler.firstSlotConnected = &onFirstConnect; - signalHandler.lastSlotDisconnected = &onLastDisconnect; - } - } - - class B : A - { - mixin Signal!("booed", int); - - int bazSum; - void baz(int i) - { - bazSum += i; - } - } - - class C : A - { - mixin Signal!("cooked"); - } +template Slot(string fullName) +{ + mixin MetaMethodImpl!(slotPrefix, 0, fullName, SignalType.NewSlot); } -unittest +template SignalImpl(int index, string fullName, SignalType signalType) { - static int fooSum; - static int barSum; - - static void foo(int i) - { - fooSum += i; - } - - void bar(long i) + static if (is(typeof(mixin(typeof(this).stringof ~ "." ~ signalPrefix ~ ToString!(index))))) + mixin SignalImpl!(index + 1, fullName, signalType); + else { - barSum += i; - } - - auto a = new A; - auto b = new B; - auto c = new C; - assert(b.scorched.signalId == 0); - assert(b.booed.signalId == 1); - assert(c.cooked.signalId == 1); - - auto sh = b.signalHandler; - - b.scorched.connect(&foo); - assert(sh.connections.length == 1); - assert(b.signalId1 == 0); - auto scCons = &sh.connections[0]; - - assert(scCons.getSlotList!(SlotListId.Func).length == 1); - b.scorched.emit(1); - assert(fooSum == 1); - - b.scorched.connect(&bar, ConnectionFlags.NoObject); - assert(sh.connections.length == 1); - assert(scCons.getSlotList!(SlotListId.Strong).length == 1); - b.scorched.emit(1); - assert (fooSum == 2 && barSum == 1); +// pragma(msg, "alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;"); + mixin("alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;"); + alias ParameterTypeTuple!(Dg) ArgTypes; + enum args = getFunc!_Args(fullName); + enum defVals = defaultValues(args); + enum defValsLength = defaultValuesLength(defVals); - b.scorched.connect(&b.baz); - assert(scCons.getSlotList!(SlotListId.Weak).length == 1); - b.scorched.emit(1); - assert (fooSum == 3 && barSum == 2 && b.bazSum == 1); - - b.scorched.disconnect(&bar); - assert(scCons.slotCount == 2); - b.scorched.disconnect(&b.baz); - assert(scCons.slotCount == 1); - b.scorched.disconnect(&foo); - assert(scCons.slotCount == 0); - assert(b.signalId2 == 0); - - fooSum = 0; - void connectFoo() +// pragma (msg, SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, index)); + mixin InsertMetaSignal!(fullName, index, defValsLength, ArgTypes); +// pragma (msg, ctfe_meta_signal!(ArgTypes)(fullName, index, defValsLength)); + } +} +template MetaMethodImpl(string metaPrefix, int index, string fullName, SignalType signalType) +{ + static if (is(typeof(mixin(typeof(this).stringof ~ "." ~ metaPrefix ~ toStringNow!(index))))) + { + mixin MetaMethodImpl!(metaPrefix, index + 1, fullName, signalType); + } + else { - b.scorched.connect(&foo); - b.scorched.disconnect(&connectFoo); + mixin("alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;"); + alias ParameterTypeTuple!(Dg) ArgTypes; + enum args = getFunc!_Args(fullName); + enum defVals = defaultValues(args); + enum defValsLength = defaultValuesLength(defVals); + + static if (metaPrefix == signalPrefix) + { + // calculating local index of the signal + static if (typeof(this).stringof == "QObject") + enum localIndex = index; + else + mixin ("enum localIndex = index - 1 - lastSignalIndex_" ~ (typeof(super)).stringof ~ ";"); + + static if (signalType == SignalType.NewSignal) + { + pragma (msg, SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, localIndex)); + mixin (SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, localIndex)); + } + } + mixin InsertMetaMethod!(fullName, metaPrefix, index, defValsLength, DefaultArgs.Start, ArgTypes); +// pragma (msg, ctfe_meta_signal!(ArgTypes)(fullName, index, defValsLength)); + } +} +template InsertMetaMethod(string fullName, string metaPrefix, int index, int defValsCount, DefaultArgs defArgsInitFlag, ArgTypes...) +{ + // this identifies if metamethod is was generated by the presence of default args or not + static if(defValsCount > 0) + { + static if (defArgsInitFlag == DefaultArgs.Start) + enum defValsFlag = DefaultArgs.Start; + else + enum defValsFlag = DefaultArgs.Continue; } + else + { + static if (defArgsInitFlag == DefaultArgs.Start) + enum defValsFlag = DefaultArgs.None; + else + enum defValsFlag = DefaultArgs.Continue; + } + static if(defValsCount >= 0) + mixin("public alias TypeTuple!(\"" ~ getFunc!_Name(fullName) ~ "\", index, typeof(this), defValsFlag, ArgTypes) " ~ metaPrefix ~ toStringNow!(index) ~ ";"); + static if(defValsCount > 0) + mixin InsertMetaMethod!(fullName, metaPrefix, index+1, defValsCount-1, DefaultArgs.Continue, ArgTypes[0..$-1]); +} - b.scorched.connect(&connectFoo, ConnectionFlags.NoObject); - b.scorched.emit(1); - assert(scCons.getSlotList!(SlotListId.Func).length == 1); - assert(scCons.getSlotList!(SlotListId.Strong).length == 0); - assert(!fooSum); - auto r = new B(); - b.scorched.connect(&r.baz); - assert(scCons.getSlotList!(SlotListId.Weak).length == 1); - b.scorched.emit(1); - assert(r.bazSum == 1); - assert(fooSum == 1); +string signature_impl(T...)(string name) +{ + string res = name ~ "("; + foreach(i, _; T) + { + if(i > 0) + res ~= ","; + res ~= T[i].stringof; + } + res ~= ")"; + return res; +} - delete(r); - assert(scCons.getSlotList!(SlotListId.Weak).length == 1); - b.scorched.emit(1); - assert(scCons.getSlotList!(SlotListId.Weak).length == 0); +template signature(string name, T...) +{ + enum signature = signature_impl!(T)(name); } + +template lastSignalIndex(T) +{ + static if (T.stringof == "QObject") + enum lastSignalIndex = lastSignalIndexImpl!(T, 0); + else + mixin ("enum lastSignalIndex = lastSignalIndexImpl!(T, " ~ "T.lastSignalIndex_" ~ (BaseClassesTuple!(T)[0]).stringof ~ ");"); +} + +template lastSignalIndexImpl(T, int index) +{ + static if (is(typeof(mixin("T." ~ signalPrefix ~ toStringNow!(index))))) + enum lastSignalIndexImpl = lastSignalIndexImpl!(T, index + 1); + else + enum lastSignalIndexImpl = index - 1; +} \ No newline at end of file diff -r b6984b290e46 -r f9559a957be9 qt/d2/qt/core/QModelIndex.d --- a/qt/d2/qt/core/QModelIndex.d Sun Nov 08 19:20:53 2009 +0000 +++ b/qt/d2/qt/core/QModelIndex.d Sun Nov 08 19:28:01 2009 +0000 @@ -4,8 +4,8 @@ private import qt.core.QObject; // automatic imports------------- -private import qt.core.QVariant; -private import qt.core.QAbstractItemModel; +//private import qt.core.QVariant; +//private import qt.core.QAbstractItemModel; public import qt.core.Qt; @@ -51,7 +51,7 @@ public final bool isValid() const { return __qtd_QModelIndex_isValid(cast(void*)&this); } - +/* public final QAbstractItemModel model() { // void* __qt_return_value = __qtd_QModelIndex_model(&this); void* __qt_return_value = m; @@ -60,7 +60,7 @@ void* d_obj = qtd_get_d_qobject(__qt_return_value); return cast(QAbstractItemModel) d_obj; } - +*/ private final bool operator_less(QModelIndex other) { return __qtd_QModelIndex_operator_less_QModelIndex(&this, other); } diff -r b6984b290e46 -r f9559a957be9 qt/qtd/MOC.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/MOC.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,534 @@ +module qt.qtd.MOC; + +import qt.qtd.ctfe.Format; + +import std.typetuple; + +import qt.Signal; +import qt.qtd.MetaMarshall; + +public import std.traits; +/** + Utils. + */ + +T qMin(T)(T a,T b) { if (a < b) return a; return b; } +T qMax(T)(T a, T b) { if (a < b) return b; return a; } +T qBound(T)(T min, T val,T max) { return qMax(min, qMin(max, val)); } + +bool is_digit_char(const char s) +{ + return (s >= '0' && s <= '9'); +} + +bool is_octal_char(const char s) +{ + return (s >= '0' && s <= '7'); +} + +bool is_hex_char(const char s) +{ + return ((s >= 'a' && s <= 'f') + || (s >= 'A' && s <= 'F') + || (s >= '0' && s <= '9') + ); +} + +int lastIndexOf(T)(T[] haystack, T[] needle, int from = -1) +{ + auto l = haystack.length; + auto ol = needle.length; + int delta = l - ol; + if (from < 0) + from = delta; + if (from < 0 || from > l) + return -1; + if (from > delta) + from = delta; + + while(from >= 0) + { + if (haystack[from..from+ol] == needle) + return from; + from--; + } + return -1; +} + + +T[] newArray(T)(size_t len, T[] from = []) +{ + if (len == from.length) + return from; + + if (!from.length) + from = [T.init]; + + if (from.length < len) + return newArray!T(len, from ~ from); + + return from[0..len]; +} + +string replicate(int n, char value) +{ + char[] ret = "".dup; + if (n > 0) + { +// ret = newArray!char(n); + for(int i = 0; i < n; i++) + ret ~= value; + } + return cast(string)ret; +} + +template Repeat(T, int I) +{ + static if (!I) alias TypeTuple!() Repeat; + else alias TypeTuple!(T, Repeat!(T, I - 1)) Repeat; +} + +/** + CTFE MOC port. + */ + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40 +} + +enum Access { Private, Protected, Public } + +struct FunctionDef +{ +/* FunctionDef(): returnTypeIsVolatile(false), access(Private), isConst(false), isVirtual(false), + inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false), + isScriptable(false), isSlot(false), isSignal(false), + isConstructor(false), isDestructor(false), isAbstract(false) {} + */ +// Type type; +// string normalizedType; +// string tag; +// string name; + string sig; + string arguments; + Access access; +/* bool returnTypeIsVolatile; + + QList arguments; + + enum Access { Private, Protected, Public }; + bool isConst; + bool isVirtual; + bool inlineCode; + bool wasCloned; + + QByteArray inPrivateClass; + bool isCompat; + bool isInvokable; + bool isScriptable; + bool isSlot; + bool isSignal; + bool isConstructor; + bool isDestructor; + bool isAbstract; + */ +} + +FunctionDef newSlot(string sig, string args) +{ + return FunctionDef(sig, args, Access.Public); +} + +FunctionDef newSignal(string sig, string args) +{ + return FunctionDef(sig, args, Access.Protected); +} + +struct Generator +{ + string output; + string[] strings; +// QByteArray purestSuperClass; +// QList metaTypes; +} + +int lengthOfEscapeSequence(string s, uint i) +{ + if (s[i] != '\\' || i >= s.length - 1) + return 1; + const int startPos = i; + ++i; + auto ch = s[i]; + if (ch == 'x') { + ++i; + while (i < s.length && is_hex_char(s[i])) + ++i; + } else if (is_octal_char(ch)) { + while (i < startPos + 4 + && i < s.length + && is_octal_char(s[i])) { + ++i; + } + } else { // single character escape sequence + i = qMin(i + 1, s.length); + } + return i - startPos; +} + +int strreg(ref Generator gen, string s) +{ + int idx = 0; + foreach (str; gen.strings) { + if (str == s) + return idx; + idx += str.length + 1; + foreach (i, c; str) { + if (c == '\\') { + int cnt = lengthOfEscapeSequence(str, i) - 1; + idx -= cnt; + i += cnt; + } + } + } + gen.strings ~= s; + return idx; +} + +void generateFunctions(ref Generator gen, FunctionDef[] list, string functype, byte type) +{ + if (!list.length) + return; + gen.output ~= format_ctfe("\n // ${}s: signature, parameters, type, tag, flags\n", functype); + + foreach (i, f; list) { + byte flags = type; + + if (f.access == Access.Private) + flags |= MethodFlags.AccessPrivate; + else if (f.access == Access.Public) + flags |= MethodFlags.AccessPublic; + else if (f.access == Access.Protected) + flags |= MethodFlags.AccessProtected; + + gen.output ~= format_ctfe(" ${}, ${}, ${}, ${}, 0x${:x},\n", strreg(gen, f.sig), + strreg(gen, f.arguments), strreg(gen, ""/*f.normalizedType*/), strreg(gen, ""/*f.tag*/), flags); + } +} + +string generateCode(string className, FunctionDef[] signalList, FunctionDef[] slotList) +{ + auto gen = Generator("", []); + +/* bool isQt = (cdef->classname == "Qt"); + bool isQObject = (cdef->classname == "QObject"); + bool isConstructible = !cdef->constructorList.isEmpty(); + +// +// build the data array +// + int i = 0; + + + // filter out undeclared enumerators and sets + { + QList enumList; + for (i = 0; i < cdef->enumList.count(); ++i) { + EnumDef def = cdef->enumList.at(i); + if (cdef->enumDeclarations.contains(def.name)) { + enumList += def; + } + QByteArray alias = cdef->flagAliases.value(def.name); + if (cdef->enumDeclarations.contains(alias)) { + def.name = alias; + enumList += def; + } + } + cdef->enumList = enumList; + } + + + QByteArray qualifiedClassNameIdentifier = cdef->qualified; + qualifiedClassNameIdentifier.replace(':', '_'); +*/ + bool isConstructible = false; + + FunctionDef[] propertyList, enumList, constructorList; + int index = 12; + gen.output ~= format_ctfe("static const uint[] qt_meta_data_${} = [\n", className); + gen.output ~= format_ctfe("\n // content:\n"); + gen.output ~= format_ctfe(" ${}, // revision\n", 2); + gen.output ~= format_ctfe(" ${}, // classname\n", strreg(gen, className)); + gen.output ~= format_ctfe(" ${}, ${}, // classinfo\n", 0, 0); +// index += cdef->classInfoList.count() * 2; + + int methodCount = signalList.length + slotList.length;// + cdef->methodList.count(); + gen.output ~= format_ctfe(" ${}, ${}, // methods\n", methodCount, methodCount ? index : 0); + index += methodCount * 5; + gen.output ~= format_ctfe(" ${}, ${}, // properties\n", propertyList.length, propertyList.length ? index : 0); + index += propertyList.length * 3; +// if(cdef->notifyableProperties) +// index += cdef->propertyList.count(); + gen.output ~= format_ctfe(" ${}, ${}, // enums/sets\n", enumList.length, enumList.length ? index : 0); + +// int enumsIndex = index; +// for (i = 0; i < cdef->enumList.count(); ++i) +// index += 4 + (cdef->enumList.at(i).values.count() * 2); + gen.output ~= format_ctfe(" ${}, ${}, // constructors\n", isConstructible ? constructorList.length : 0, + isConstructible ? index : 0); + +// +// Build classinfo array +// +// generateClassInfos(); + +// +// Build signals array first, otherwise the signal indices would be wrong +// + generateFunctions(gen, signalList, "signal", MethodFlags.MethodSignal); + +// +// Build slots array +// + generateFunctions(gen, slotList, "slot", MethodFlags.MethodSlot); + +// +// Build method array +// +// generateFunctions(cdef->methodList, "method", MethodMethod); + + +// +// Build property array +// +// generateProperties(); + +// +// Build enums array +// +// generateEnums(enumsIndex); + +// +// Build constructors array +// +// if (isConstructible) +// generateFunctions(cdef->constructorList, "constructor", MethodConstructor); + +// +// Terminate data array +// + gen.output ~= format_ctfe("\n 0 // eod\n];\n\n"); + +// +// Build stringdata array +// + gen.output ~= format_ctfe("static const string qt_meta_stringdata_${} = \n", className); + gen.output ~= format_ctfe(" \""); + int col = 0; + int len = 0; + foreach (i, s; gen.strings) { + len = s.length; + if (col && col + len >= 72) { + gen.output ~= format_ctfe("\"\n \""); + col = 0; + } else if (len && s[0] >= '0' && s[0] <= '9') { + gen.output ~= format_ctfe("\"\""); + len += 2; + } + int idx = 0; + while (idx < s.length) { + if (idx > 0) { + col = 0; + gen.output ~= format_ctfe("\"\n \""); + } + int spanLen = qMin(cast(uint)70, s.length - idx); + // don't cut escape sequences at the end of a line + int backSlashPos = s.lastIndexOf("\\", idx + spanLen - 1); + if (backSlashPos >= idx) { + int escapeLen = lengthOfEscapeSequence(s, backSlashPos); + spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, cast(int)(s.length - idx)); + } + gen.output ~= s[idx..idx+spanLen]; + idx += spanLen; + col += spanLen; + } + + gen.output ~= "\\0"; + col += len + 2; + } + gen.output ~= "\";\n\n"; + + return gen.output; +} + +string metaCallArgs(Args...)() +{ + string res; + foreach(i, _; Args) { + if (i > 0) + res ~= ","; + res ~= metaCallArgument!(Args[i])("_a[" ~ __toString(i+1) ~ "]"); + } + return res; +} + +string qtDeclArgs(Args...)() +{ + string ret; + foreach(i, _; Args) + { + if(i > 0) + ret ~= ","; + ret ~= qtDeclArg!(Args[i]); + } + return ret; +} + +string generate_qt_metacall(alias Signals, alias Slots)() +{ + string res = " +protected int qt_metacall(QMetaObject.Call _c, int _id, void **_a) + { + _id = super.qt_metacall(_c, _id, _a); + if (_id < 0) + return _id;\n"; + + alias TypeTuple!(Signals.at, Slots.at) Methods; + enum methodCount = Methods.length; + if(methodCount) + { + res ~= " + if (_c == QMetaObject.Call.InvokeMetaMethod) { + switch (_id) {"; + foreach(i, bogus; Repeat!(void, methodCount)) { + res ~= " + case " ~ __toString(i) ~ ": " ~ MetaEntryName!(Methods[i].at) ~ "(" ~ metaCallArgs!(MetaEntryArgs!(Methods[i].at))() ~ "); break;"; + } + res ~= "\n default: ;\n }\n"; + res ~= " _id -= " ~ __toString(methodCount) ~ ";"; + res ~= "\n }"; + } + + res ~= "\n return _id; + }"; + return res; +} + +string dDeclArgs(Args...)() +{ + string ret; + foreach(i, _; Args) + ret ~= ", " ~ Args[i].stringof; + return ret; +} +string genMetaMethodsConstr(alias Funcs)(string className) +{ + string res; + enum funcsCount = Funcs.at.length; + foreach(i, bogus; Repeat!(void, funcsCount)) + { + res ~= " index++;\n" ~ + " _staticMetaObject.addMethod(new " ~ className ~ "(signature!(\"" ~ MetaEntryName!(Funcs.at[i].at) ~ "\"" ~ dDeclArgs!(MetaEntryArgs!(Funcs.at[i].at))()~ "), index));\n\n"; + } + return res; +} +string generateMetaObjectConstruction(alias Signals, alias Slots)() +{ + string res; + res ~= "\n + private static void _populateMetaInfo() { + alias BaseClassesTuple!(typeof(this))[0] BaseClass; + int index = BaseClass.staticMetaObject().methodCount() - 1;\n\n"; + + res ~= genMetaMethodsConstr!(Signals)("QMetaSignal"); + res ~= genMetaMethodsConstr!(Slots)("QMetaSlot"); + + res ~= " + }\n"; + return res; +} + +string generateQMetaObject(string className) +{ + string res; + res ~= " + QMetaObject metaObject() { return staticMetaObject; } + private static QMetaObject _staticMetaObject; + private static QMetaObjectNative _nativeStaticMetaObject; + public static QMetaObject staticMetaObject() { return _staticMetaObject; } + protected static void createStaticMetaObject() { + assert(!_staticMetaObject); + alias BaseClassesTuple!(typeof(this))[0] BaseClass; + if (!BaseClass._staticMetaObject) + BaseClass.createStaticMetaObject; + auto base = BaseClass._staticMetaObject; + _nativeStaticMetaObject = QMetaObjectNative(base.nativeId, qt_meta_stringdata_" ~ className ~ ".ptr, + qt_meta_data_" ~ className ~ ".ptr, null ); + + _staticMetaObject = new QMetaObject(&_nativeStaticMetaObject, base); +// _staticMetaObject.construct!(typeof(this)); + _populateMetaInfo(); + } + static this() + { + createStaticMetaObject(); + }\n\n"; + return res; +} + +size_t commaCount(int argCount) +{ + size_t ret = 0; + if(argCount > 1) + ret = argCount - 1; + return ret; +} + +FunctionDef[] genFuncDefs(alias Funcs, alias newFunc)() +{ + typeof(return) res; + enum funcsCount = Funcs.at.length; + foreach(i, bogus; Repeat!(void, funcsCount)) + { + string args = replicate(commaCount((MetaEntryArgs!(Funcs.at[i].at)).length), ','); + string funcSig = MetaEntryName!(Funcs.at[i].at) ~ "(" ~ qtDeclArgs!(MetaEntryArgs!(Funcs.at[i].at))() ~ ")"; + res ~= newFunc(funcSig, args); + } + return res; +} + +string generateMetaInfo(string className, alias Signals, alias Slots)() +{ + string res = ""; + auto signalList = genFuncDefs!(Signals, newSignal)(); + auto slotList = genFuncDefs!(Slots, newSlot)(); + res ~= generateCode(className, signalList, slotList); + res ~= generate_qt_metacall!(Signals, Slots); + res ~= generateMetaObjectConstruction!(Signals, Slots); + res ~= generateQMetaObject(className); + return res; +} + +template Q_OBJECT_BIND() +{ + mixin ("enum lastSignalIndex_" ~ typeof(this).stringof ~ " = " ~ toStringNow!(lastSignalIndex!(typeof(this))) ~ ";"); +} + +template Q_OBJECT() +{ +// pragma(msg, toStringNow!(lastSignalIndex!(typeof(this)))); + mixin ("enum lastSignalIndex_" ~ typeof(this).stringof ~ " = " ~ toStringNow!(lastSignalIndex!(typeof(this))) ~ ";"); + + alias TupleWrapper!(findSymbols!(slotPrefix, typeof(this), ByOwner!(typeof(this)))) Slots; + alias TupleWrapper!(findSymbols!(signalPrefix, typeof(this), ByOwner!(typeof(this)))) Signals; +// pragma(msg, generateMetaInfo!((typeof(this)).stringof, Signals, Slots)()); + mixin(generateMetaInfo!((typeof(this)).stringof, Signals, Slots)()); +} diff -r b6984b290e46 -r f9559a957be9 qt/qtd/MetaMarshall.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/MetaMarshall.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,57 @@ +module qt.qtd.MetaMarshall; + +import std.traits; + +template isQObjectType(T) // is a QObject type that belongs to the library +{ + enum isQObjectType = is(typeof(mixin("T." ~ "__isQObjectType"))); +} + +template isObjectType(T) // is a QObject type that belongs to the library +{ + enum isQObjectType = is(typeof(mixin("T." ~ "__isObjectType"))); +} + +template isValueType(T) // is a QObject type that belongs to the library +{ + enum isQObjectType = is(typeof(mixin("T." ~ "__isValueType"))); +} + +template isNativeType(T) // type that doesn't require conversion i.e. is the same in C++ and D +{ + enum isNativeType = isNumeric!T || is(T == bool); +} + +// converts an argumnent from C++ to D in qt_metacall +string metaCallArgument(T)(string ptr) +{ + static if (isQObjectType!T) + return T.stringof ~ ".__getObject(*cast(void**)(" ~ ptr ~ "))"; + else static if (isNativeType!T) + return "*(cast(" ~ T.stringof ~ "*)" ~ ptr ~ ")"; + else + return "*(cast(" ~ T.stringof ~ "*)" ~ ptr ~ ")"; + //res = T.stringof; +} + +// converts a D argument type to C++ for registering in Qt meta system +string qtDeclArg(T)() +{ + static if (isQObjectType!T) + return T.stringof ~ "*"; + else static if (isNativeType!T) + return T.stringof; + else + return T.stringof; +} + +// converts an argument from D to C++ in a signal emitter +string convertSignalArgument(T)(string arg) +{ + static if (isQObjectType!T) + return arg ~ ".__nativeId"; + else static if (isNativeType!T) + return arg; + else + return arg; +} diff -r b6984b290e46 -r f9559a957be9 qt/qtd/ctfe/Format.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/ctfe/Format.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,693 @@ +/** + * Compile-Time String Formatting. + * + * Authors: Daniel Keep + * Copyright: See LICENSE. + */ +module qt.qtd.ctfe.Format; + +//debug = gb_Format_verbose; + +import Integer = qt.qtd.ctfe.Integer; +import String = qt.qtd.ctfe.String; +import Tuple = qt.qtd.util.Tuple; + +private +{ + string stringify(Args...)(size_t index, int alignment, + string opt, Args args) + { + if( index >= args.length ) + return "{invalid index " ~ Integer.format_ctfe(index) ~ "}"; + + if( alignment != 0 ) + return "{non-zero alignments not supported yet}"; + + foreach( i,_ ; Args ) + { + if( i == index ) + { + static if( is( Args[i] == char ) ) + { + string r; + r ~= args[i]; + return r; + } + else static if( is( Args[i] : long ) || is( Args[i] : ulong ) ) + { + int base = 10; + string prefix = ""; + + if( opt == "x" ) + base = 16; + + else if( opt == "xx" ) + { + base = 16; + prefix = "0x"; + } + else if( opt == "o" ) + base = 8; + + else if( opt == "b" ) + base = 2; + + return prefix ~ Integer.format_ctfe(args[i], base); + } + else static if( is( Args[i] : string ) ) + { + if( opt == "x" ) + { + return String.hexify_ctfe(args[i][]); + } + + if( opt == "q" ) + { + return String.escape_ctfe(args[i][]); + } + + if( opt == "l" ) + { + return Integer.format_ctfe(args[i].length); + } + + // If you don't slice, then the CALLER has to slice the + // string, otherwise CTFE barfs. + return args[i][]; + } + else static if( is( Args[i] Elem : Elem[] ) ) + { + if( opt == "l" ) + { + return Integer.format_ctfe(args[i].length); + } + + string r = "["; + foreach( ei, e ; args[i][] ) + { + if( ei != 0 ) + r ~= ", "; + r ~= stringify(0, alignment, opt, e); + } + r ~= "]"; + return r; + } + else + { + return "{cannot stringify "~Args[i].stringof~"}"; + } + } + } + + assert(false); + } + + version( Unittest ) + { + static assert( stringify(0, 0, "", 0) == "0" ); + static assert( stringify(0, 0, "", 1, -2, "abc") == "1" ); + static assert( stringify(1, 0, "", 1, -2, "abc") == "-2" ); + static assert( stringify(2, 0, "", 1, -2, "abc") == "abc" ); + + static assert( stringify(0, 0, "x", "abc") == `616263` ); + static assert( stringify(0, 0, "q", "abc") == `"abc"` ); + static assert( stringify(0, 0, "l", "abc") == `3` ); + + static assert( stringify(0, 0, "x", 0x4a) == "4a" ); + + static assert( stringify(0, 0, "", [1,2,3]) == "[1, 2, 3]" ); + static assert( stringify(0, 0, "l", [1,2,3]) == "3" ); + static assert( stringify(0, 0, "x", [9,10]) == "[9, a]" ); + static assert( stringify(0, 0, "", ["a","b"]) == "[a, b]" ); + static assert( stringify(0, 0, "q", ["a","b"]) == "[\"a\", \"b\"]" ); + + static assert( stringify(0, 0, "", 'a') == "a" ); + } +} + +/** + * Substitutes a set of arguments into a template string. + * + * The template string allows for the following escape forms: + * + * - $$ -- Literal dollar. + * - $* -- Next argument. + * - $n -- nth argument; 0-9 only. + * - ${} -- Next argument. + * - ${:f} -- Next argument, using format options "f". + * - ${n} -- nth argument. + * - ${n:f} -- nth argument, using format options "f". + * + * formatNamed allows the use of named arguments (given as alternating + * name,value pairs), but disallows "next" argument and indexed forms. + * + * Eventually, alignment and named arguments will be supported. + * + * Supported formatting options are: + * + * Integers: + * - x -- format integer in hexadecimal. + * - o -- format integer in octal. + * - b -- format integer in binary. + * + * Strings: + * - q -- quotes the string as a literal. + * - x -- formats as hexadecimal data. + * - l -- length of the string in decimal. + * + * Arrays: + * - l -- length of the array in decimal. + * - Other options are used to control element formatting. + * + * Params: + * tmpl = template string. + * args = arguments to substitute. + * Returns: + * formatted string. + */ + +string format_ctfe(Args...)(string tmpl, Args args) +{ + string r = ""; + int argPos = 0; + + while( tmpl.length > 0 ) + { + bool inExp = false; + + // Look for a $ + foreach( i,c ; tmpl ) + { + if (c == '$') + { + inExp = true; + r ~= tmpl[0..i]; + tmpl = tmpl[i+1..$]; + break; + } + } + + // If we didn't find a $, it's because we hit the end of the template. + if( !inExp ) + { + r ~= tmpl; + break; + } + + // So we're in an expansion/substitution. + + debug(gb_Format_verbose) r ~= "{in exp}"; + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + + // c is the next character, whilst tmpl is everything left in the + // template string. + char c = tmpl[0]; + tmpl = tmpl[1..$]; + + // $$ - escaped $. + if( c == '$' ) + { + debug(gb_Format_verbose) r ~= "{escaped $}"; + r ~= '$'; + continue; + } + + // $n - shortcut for ${n}. + if( '0' <= c && c <= '9' ) + { + debug(gb_Format_verbose) r ~= "{shorthand index}"; + r ~= stringify(c-'0', 0, "", args); + continue; + } + + // $* - shortcut for ${} + if( c == '*' ) + { + debug(gb_Format_verbose) r ~= "{shorthand next}"; + r ~= stringify(argPos++, 0, "", args); + continue; + } + + // This means we got a $ followed by something unexpected. + if( c != '{' ) + { + r ~= "{malformed substitution}"; + break; + } + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + + debug(gb_Format_verbose) + { + r ~= "{parse complex at '"; + r ~= c; + r ~= "':\"" ~ tmpl ~ "\"}"; + } + + // NOTE: We haven't updated c and tmpl yet. + + { + // arg will contain the index of the argument the user wanted + // substituted. + size_t arg = size_t.max; + // fmt will contain any additional formatting options. + string fmt = ""; + + // If we didn't get a : or }, that means we expect an index. + if( !( tmpl[0] == ':' || tmpl[0] == '}' ) ) + { + // So parse it. + auto used = Integer.parse_ctfe!(size_t)(tmpl, true); + + if( used == 0 ) + { + debug(gb_Format_verbose) r ~= "{used zero of \""~tmpl~"\"}"; + r ~= "{invalid argument index}"; + break; + } + + arg = Integer.parse_ctfe!(size_t)(tmpl); + tmpl = tmpl[used..$]; + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + } + else + { + // Otherwise, the index was elided, which means we want to use + // the index of the "next" argument. + arg = argPos; + ++ argPos; + } + + c = tmpl[0]; + tmpl = tmpl[1..$]; + + debug(gb_Format_verbose) + r ~= "{index " ~ Integer.format_ctfe(arg) ~ "}"; + + // If c is :, then we've got formatting options to parse + + if( c == ':' ) + { + debug(gb_Format_verbose) r ~= "{fmt string}"; + + // Look for the closing }. + size_t len = 0; + foreach( i,d ; tmpl ) + { + if( d == '}' ) + { + len = i; + break; + } + } + if( len == 0 ) + { + r ~= "{malformed format}"; + break; + } + fmt = tmpl[0..len]; + tmpl = tmpl[len..$]; + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + + c = tmpl[0]; + tmpl = tmpl[1..$]; + } + + // At this point, we should have the closing }. If not, someone's + // screwed up. + if( c != '}' ) + { + debug(gb_Format_verbose) + { + r ~= "{expected closing; got '"; + r ~= c; + r ~= "':\"" ~ tmpl ~ "\"}"; + } + r ~= "{malformed substitution}"; + break; + } + + // Stringify that bugger. + r ~= stringify(arg, 0, fmt, args); + + // When we fall off the end here, we'll continue with the + // remainder of tmpl, unless it's empty in which case we're + // finished. + } + } + + return r; +} + +version( Unittest ) +{ + static assert(format_ctfe("A: $$", "foo"[]) == "A: $"); + static assert(format_ctfe("B: a $$ c", "b"[]) == "B: a $ c"); + + static assert(format_ctfe("C: ${}", "foo"[]) == "C: foo"); + static assert(format_ctfe("D: a ${} c", "b"[]) == "D: a b c"); + + static assert(format_ctfe("E: $0", "foo"[]) == "E: foo"); + static assert(format_ctfe("F: a $0 c", "b"[]) == "F: a b c"); + + static assert(format_ctfe("G: $*", "foo"[]) == "G: foo"); + static assert(format_ctfe("H: a $* c", "b"[]) == "H: a b c"); + + static assert(format_ctfe("I: ${0}", "foo"[]) == "I: foo"); + static assert(format_ctfe("J: a ${0} c", "b"[]) == "J: a b c"); + + static assert(format_ctfe("K: ${} ${} ${}", 1, -2, "c"[]) == "K: 1 -2 c"); + static assert(format_ctfe("L: $* $* $*", 1, -2, "c"[]) == "L: 1 -2 c"); + static assert(format_ctfe("M: $0 $1 $2", 1, -2, "c"[]) == "M: 1 -2 c"); + static assert(format_ctfe("N: ${0} ${1} ${2}", 1, -2, "c"[]) == "N: 1 -2 c"); + + static assert(format_ctfe("O: ${2} ${0} ${1}", 1, -2, "c"[]) == "O: c 1 -2"); + + static assert(format_ctfe("P: ${:x} ${0:x} ${0:o} ${0:b}", 42) == "P: 2a 2a 52 101010"); + + static assert(format_ctfe("Q: ${0} ${0:q} ${0:x}", "abc"[]) == "Q: abc \"abc\" 616263"); + static assert(format_ctfe("R: ${0} ${0:q}", ["a","b","c"][]) == "R: [a, b, c] [\"a\", \"b\", \"c\"]"); + + const TORTURE_TMPL = ` + struct $*Enum + { + const Name = ${0:q}; + const string[${:l}] Members = ${1:q}; + + ${2} value() + { + return ${3:xx}; + } + } + `[]; + + const TORTURE_EXPECTED = ` + struct FooEnum + { + const Name = "Foo"; + const string[3] Members = ["bar", "quxx", "zyzzy"]; + + int value() + { + return 0x42; + } + } + `[]; + + const TORTURE_ACTUAL = format_ctfe(TORTURE_TMPL, + "Foo"[], ["bar"[],"quxx","zyzzy"][], + "int"[], 0x42); + + static assert( TORTURE_EXPECTED == TORTURE_ACTUAL ); +} + +private +{ + size_t findIndexByName(Args...)(string name, Args args) + { + foreach( i ; Tuple.Sequence!(0, Args.length, 2) ) + { + static if( !is( Args[i] : string ) ) + { + static assert(false, "expected string for argument " + ~ Integer.format_ctfe(i) ~ " in " ~ Args.stringof + ~ " not " ~ Args[i].stringof); + } + if( name == args[i][] ) + return i+1; + } + return size_t.max; + } + + version( Unittest ) + { + static assert( findIndexByName("a", "a", 0, "b", 1) == 1 ); + static assert( findIndexByName("b", "a", 0, "b", 1) == 3 ); + static assert( findIndexByName("c", "a", 0, "b", 1) == size_t.max ); + } +} + +/// ditto + +string formatNamed_ctfe(Args...)(string tmpl, Args args) +{ + string r = ""; + int argPos = 0; + + while( tmpl.length > 0 ) + { + bool inExp = false; + + // Look for a $ + foreach( i,c ; tmpl ) + { + if (c == '$') + { + inExp = true; + r ~= tmpl[0..i]; + tmpl = tmpl[i+1..$]; + break; + } + } + + // If we didn't find a $, it's because we hit the end of the template. + if( !inExp ) + { + r ~= tmpl; + break; + } + + // So we're in an expansion/substitution. + + debug(gb_Format_verbose) r ~= "{in exp}"; + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + + // c is the next character, whilst tmpl is everything left in the + // template string. + char c = tmpl[0]; + tmpl = tmpl[1..$]; + + // $$ - escaped $. + if( c == '$' ) + { + debug(gb_Format_verbose) r ~= "{escaped $}"; + r ~= '$'; + continue; + } + + // $a... - shortcut for $a... + if( String.isIdentStartChar_ctfe(c) ) + { + debug(gb_Format_verbose) r ~= "{shorthand name}"; + size_t i = 0; + while( i < tmpl.length ) + { + if( !String.isIdentChar_ctfe(tmpl[i]) ) + break; + ++ i; + } + string name = c ~ tmpl[0..i]; + tmpl = tmpl[i..$]; + r ~= stringify(findIndexByName(name, args), 0, "", args); + continue; + } + + // This means we got a $ followed by something unexpected. + if( c != '{' ) + { + r ~= "{malformed substitution}"; + break; + } + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + + debug(gb_Format_verbose) + { + r ~= "{parse complex at '"; + r ~= c; + r ~= "':\"" ~ tmpl ~ "\"}"; + } + + // NOTE: We haven't updated c and tmpl yet. + + { + // arg will contain the index of the argument the user wanted + // substituted. + size_t arg = size_t.max; + // fmt will contain any additional formatting options. + string fmt = ""; + + // If we didn't get a : or }, that means we expect a name. + if( !( tmpl[0] == ':' || tmpl[0] == '}' ) ) + { + // So parse it. + size_t i = 0; + while( i < tmpl.length ) + { + if( !String.isIdentChar_ctfe(tmpl[i]) ) + break; + ++ i; + } + string name = tmpl[0..i]; + tmpl = tmpl[i..$]; + + arg = findIndexByName(name, args); + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + } + else + { + // Otherwise, the name was elided. Kaboom! + r ~= "{substitution missing name}"; + break; + } + + c = tmpl[0]; + tmpl = tmpl[1..$]; + + debug(gb_Format_verbose) + r ~= "{index " ~ Integer.format_ctfe(arg) ~ "}"; + + // If c is :, then we've got formatting options to parse + + if( c == ':' ) + { + debug(gb_Format_verbose) r ~= "{fmt string}"; + + // Look for the closing }. + size_t len = 0; + foreach( i,d ; tmpl ) + { + if( d == '}' ) + { + len = i; + break; + } + } + if( len == 0 ) + { + r ~= "{malformed format}"; + break; + } + fmt = tmpl[0..len]; + tmpl = tmpl[len..$]; + + debug(gb_Format_verbose) r ~= "{fmt:"~fmt~"}"; + + if( tmpl.length == 0 ) + { + r ~= "{unterminated substitution}"; + break; + } + + c = tmpl[0]; + tmpl = tmpl[1..$]; + } + + // At this point, we should have the closing }. If not, someone's + // screwed up. + if( c != '}' ) + { + debug(gb_Format_verbose) + { + r ~= "{expected closing; got '"; + r ~= c; + r ~= "':\"" ~ tmpl ~ "\"}"; + } + r ~= "{malformed substitution}"; + break; + } + + // Stringify that bugger. + r ~= stringify(arg, 0, fmt, args); + + // When we fall off the end here, we'll continue with the + // remainder of tmpl, unless it's empty in which case we're + // finished. + } + } + + return r; +} + +version( Unittest ) +{ + static assert( formatNamed_ctfe("A: $$", "a"[], 0, "b"[], 1) == "A: $" ); + static assert( formatNamed_ctfe("B: $a", "a"[], 0, "b"[], 1) == "B: 0" ); + static assert( formatNamed_ctfe("C: $b", "a"[], 0, "b"[], 1) == "C: 1" ); + + static assert( formatNamed_ctfe("D: ${a}", "a"[], 0, "b"[], 1) == "D: 0" ); + static assert( formatNamed_ctfe("E: ${b}", "a"[], 0, "b"[], 1) == "E: 1" ); + + static assert( formatNamed_ctfe("F: $foo$bar", "foo"[], 0, "bar"[], 1) == "F: 01" ); + static assert( formatNamed_ctfe("G: ${foo}${bar}", "foo"[], 0, "bar"[], 1) == "G: 01" ); + + static assert( formatNamed_ctfe("H: ${foo:x}${bar:xx}", "foo"[], 0, "bar"[], 1) == "H: 00x1" ); + + const TORTURE_NAMED_TMPL = ` + struct ${name}Enum + { + const Name = ${name:q}; + const string[${members:l}] Members = ${members:q}; + + ${retType} value() + { + return ${value:xx}; + } + } + `[]; + + const TORTURE_NAMED_EXPECTED = ` + struct FooEnum + { + const Name = "Foo"; + const string[3] Members = ["bar", "quxx", "zyzzy"]; + + int value() + { + return 0x42; + } + } + `[]; + + const TORTURE_NAMED_ACTUAL = formatNamed_ctfe(TORTURE_NAMED_TMPL, + "name"[], "Foo"[], + "members"[], ["bar"[],"quxx","zyzzy"][], + "retType"[], "int"[], + "value"[], 0x42); + + static assert( TORTURE_NAMED_EXPECTED == TORTURE_NAMED_ACTUAL ); +} + diff -r b6984b290e46 -r f9559a957be9 qt/qtd/ctfe/Integer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/ctfe/Integer.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,169 @@ +/** + * CTFE Integer routines. + * + * Authors: Daniel Keep + * Copyright: See LICENSE. + */ +module qt.qtd.ctfe.Integer; + +/** + * Formats an integer as a string. You can optionally specify a different + * base; any value between 2 and 16 inclusive is supported. + * + * Params: + * v = value to format. + * base = base to use; defaults to 10. + * Returns: + * integer formatted as a string. + */ + +string format_ctfe(intT)(intT v, int base = 10) +{ + static if( !is( intT == ulong ) ) + { + return (v < 0) + ? "-" ~ format_ctfe(cast(ulong) -v, base) + : format_ctfe(cast(ulong) v, base); + } + else + { + assert( 2 <= base && base <= 16, + "base must be between 2 and 16; got " ~ format_ctfe(base, 10) ); + + string r = ""; + do + { + r = INT_CHARS[cast(size_t)(v % base)] ~ r; + v /= base; + } + while( v > 0 ); + return r; + } +} + +/** + * Parses an integer value from a string. You can optionally specify a + * different base; any value between 2 and 16 inclusive is supported. + * + * Note that this does not fail if it cannot consume the entire string; + * use returnUsed to determine the number of characters consumed. + * + * Params: + * str = string to parse. + * returnUsed = defaults to false; if set to true, returns the number of + * characters consumed from the string instead of the + * parsed value. + * base = base to use; defaults to 10. + * Returns: + * either the parsed integer or the number of characters consumed, + * depending on the value of returnUsed. + */ + +intT parse_ctfe(intT)(string str, bool returnUsed = false, int base = 10) +{ + auto origStr = str; + + assert( 2 <= base && base <= 16, + "base must be between 2 and 16; got " ~ format_ctfe(base, 10) ); + + bool neg = false; + if( str.length > 0 && str[0] == '-' ) + { + neg = true; + str = str[1..$]; + } + + if( intT.min == 0 && neg ) + assert(false, "underwhile while parsing \"" ~ origStr + ~ "\" as a " ~ intT.stringof ~ ": cannot represent negative " + ~ "values"); + + intT r = 0; + size_t used = 0; + + foreach( c ; str ) + { + int cv = -1; + + if( '0' <= c && c <= '9' ) + cv = c - '0'; + + else if( 'A' <= c && c <= 'Z' ) + cv = 10 + c - 'A'; + + else if( 'a' <= c && c <= 'z' ) + cv = 10 + c - 'a'; + + if( cv >= base || cv < 0 ) + break; + + auto oldR = r; + r = r*base + cast(intT) cv; + ++ used; + + if( r < oldR ) + assert(false, "overflow while parsing \"" ~ origStr + ~ "\" as a " ~ intT.stringof); + } + + if( neg ) + { + r = -r; + ++used; + } + + if( returnUsed ) + { + assert( used < intT.max, "overflow attempting to return " + ~ "number of characters consumed in a " ~ intT.stringof ); + + return used; + } + else + return r; +} + +/** + * Like parse_ctfe, except it will raise an error if the provided string + * cannot be parsed in its entirety. + * + * Params: + * str = the string to parse. + * base = base to use; defaults to 10. + * Returns: + * the parsed integer. + */ + +intT parseAll_ctfe(intT)(string str, int base = 10) +{ + auto used = parse_ctfe!(intT)(str, true, base); + assert( used == str.length, "could not parse entire string \"" ~ str + ~ "\"" ); + return parse_ctfe!(int)(str, false, base); +} + +private +{ + const INT_CHARS = "0123456789abcdef"; +} + +version( Unittest ) +{ + static assert( format_ctfe(0) == "0", "got: " ~ format_ctfe(0) ); + static assert( format_ctfe(1) == "1" ); + static assert( format_ctfe(-1) == "-1" ); + static assert( format_ctfe(42) == "42" ); + static assert( format_ctfe(0xf00, 16) == "f00" ); + static assert( format_ctfe(0123, 8) == "123" ); + + static assert( parse_ctfe!(long)("0") == 0 ); + static assert( parse_ctfe!(long)("1") == 1 ); + static assert( parse_ctfe!(long)("-1") == -1 ); + static assert( parse_ctfe!(long)("42") == 42 ); + static assert( parse_ctfe!(long)("f00", false, 16) == 0xf00 ); + static assert( parse_ctfe!(long)("123", false, 8) == 0123 ); + static assert( parse_ctfe!(long)("123ax", true) == 3 ); + static assert( parse_ctfe!(long)("123ax", true, 16) == 4 ); + + static assert( parseAll_ctfe!(long)("123") == 123 ); +} diff -r b6984b290e46 -r f9559a957be9 qt/qtd/ctfe/String.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/ctfe/String.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,317 @@ +/** + * CTFE String routines. + * + * Authors: Daniel Keep + * Copyright: See LICENSE. + */ +module qt.qtd.ctfe.String; + +import Integer = qt.qtd.ctfe.Integer; + +private +{ + const HEX_CHARS = "0123456789abcdef"; +} + +/** + * Escapes a string into an equivalent string literal. + * + * Params: + * str = string to escape. + * aggressive = if set, the function will escape all non-printing + * characters, non-space whitespace and newlines. Defaults + * to true. + * Returns: + * Escaped string literal. + */ +string escape_ctfe(string str, bool aggressive=true) +{ + string head = ""; + + foreach( i,c ; str ) + { + if( c == '"' || c == '\\' || c == '\0' ) + { + head = "\""~str[0..i]; + str = str[i..$]; + break; + } + + if( aggressive ) + { + if( c < 0x20 || c == 0x7f ) + { + head = "\""~str[0..i]; + str = str[i..$]; + break; + } + } + } + + if( head.length == 0 ) + return "\"" ~ str ~ "\""; + + string tail = ""; + + foreach( c ; str ) + { + if( c == '"' ) + tail ~= `\"`; + + else if( c == '\\' ) + tail ~= "\\\\"; + + else if( c == '\0' ) + tail ~= `\0`; + + else if( aggressive ) + { + switch( c ) + { + case '\?': + tail ~= `\?`; + break; + + case '\a': + tail ~= `\a`; + break; + + case '\b': + tail ~= `\b`; + break; + + case '\f': + tail ~= `\f`; + break; + + case '\n': + tail ~= `\n`; + break; + + case '\r': + tail ~= `\r`; + break; + + case '\t': + tail ~= `\t`; + break; + + case '\v': + tail ~= `\v`; + break; + + default: + if( c < 0x20 || c == 0x75 ) + { + tail ~= `\x`; + tail ~= HEX_CHARS[c/0xf]; + tail ~= HEX_CHARS[c&0xf]; + } + else + tail ~= c; + } + } + else + tail ~= c; + } + + return head ~ tail ~ "\""; +} + +version( Unittest ) +{ + static assert( escape_ctfe("abc") == "\"abc\"" ); + static assert( escape_ctfe("a\"c") == "\"a\\\"c\"" ); +} + +/** + * Turns an array of bytes into a hexadecimal string. + * + * Params: + * arr = array to hexify. + * grouping = if non-zero, specifies after how many bytes to insert a + * space. + * Returns: + * String of hex bytes. + */ + +string hexify_ctfe(ubyte[] arr, int grouping = 0) +{ + string r = ""; + int bytes = grouping; + foreach( b ; arr ) + { + if( bytes == 0 && grouping > 0 ) + { + r ~= ' '; + bytes = grouping; + } + + auto bh = b/16; + auto bl = b&15; + + assert( bh < 16 ); + assert( bl < 16 ); + + r ~= HEX_CHARS[bh]; + r ~= HEX_CHARS[bl]; + + if( grouping > 0 ) + -- bytes; + } + return r; +} + +/// ditto + +string hexify_ctfe(string arr, int grouping = 0) +{ + string r = ""; + int bytes = grouping; + foreach( b ; arr ) + { + if( bytes == 0 && grouping > 0 ) + { + r ~= ' '; + bytes = grouping; + } + + auto bh = b/16; + auto bl = b&15; + + assert( bh < 16 ); + assert( bl < 16 ); + + r ~= HEX_CHARS[bh]; + r ~= HEX_CHARS[bl]; + + if( grouping > 0 ) + -- bytes; + } + return r; +} + +version( Unittest ) +{ + static const ubyte[] DATA_1 = [0x00,0x01,0x02,0x03]; + static const ubyte[] DATA_2 = [0x0f,0x10,0xef,0xf0]; + + static assert( hexify_ctfe(DATA_1) == "00010203" ); + static assert( hexify_ctfe(DATA_2) == "0f10eff0" ); + + static assert( hexify_ctfe(DATA_1, 1) == "00 01 02 03" ); + static assert( hexify_ctfe(DATA_2, 1) == "0f 10 ef f0" ); + + static assert( hexify_ctfe(DATA_1, 2) == "0001 0203" ); + static assert( hexify_ctfe(DATA_2, 2) == "0f10 eff0" ); + + static assert( hexify_ctfe(DATA_1, 4) == "00010203" ); + static assert( hexify_ctfe(DATA_2, 4) == "0f10eff0" ); +} + +/** + * Pads a string. padl adds padding to the left, padr adds it to the right. + * Params: + * str = string to pad. + * len = length to pad to. + * padding = character to use for padding. Defaults to space. + * Returns: + * padded string. + */ + +string padl_ctfe(string str, int len, char padding = ' ') +{ + while( str.length < len ) + str = padding ~ str; + return str; +} + +/// ditto + +string padr_ctfe(string str, int len, char padding = ' ') +{ + while( str.length < len ) + str ~= padding; + return str; +} + +version( Unittest ) +{ + static assert( padl_ctfe("abc", 2) == "abc" ); + static assert( padl_ctfe("abc", 3) == "abc" ); + static assert( padl_ctfe("abc", 4) == " abc" ); + static assert( padl_ctfe("abc", 4, 'x') == "xabc" ); + + static assert( padr_ctfe("abc", 2) == "abc" ); + static assert( padr_ctfe("abc", 3) == "abc" ); + static assert( padr_ctfe("abc", 4) == "abc " ); + static assert( padr_ctfe("abc", 4, 'x') == "abcx" ); +} + +/** + * Returns the tail of a string after a given splitting character. The Rev + * variant returns the tail after the last instance of the splitting + * character. + */ + +string tail_ctfe(string str, char split) +{ + foreach( i,c ; str ) + { + if( c == split ) + return str[i+1..$]; + } + return str; +} + +/// ditto + +string tailRev_ctfe(string str, char split) +{ + foreach_reverse( i,c ; str ) + { + if( c == split ) + return str[i+1..$]; + } + return str; +} + +/** + * Determines whether a character is valid in an identifier in a + * non-initial position. + * + * Does not support the full range of valid D identifier characters. + */ + +bool isIdentChar_ctfe(char c) +{ + return ('a' <= c && c <= 'z') + || ('A' <= c && c <= 'Z') + || ('0' <= c && c <= '9') + || (c == '_'); +} + +/** + * Determines whether a character is valid in an identifier in an + * initial position. + * + * Does not support the full range of valid D identifier characters. + */ + +bool isIdentStartChar_ctfe(char c) +{ + return ('a' <= c && c <= 'z') + || ('A' <= c && c <= 'Z') + || (c == '_'); +} + +/** + * Returns a line spec suitable for mixing in. This can be used with string + * mixins to ensure compile errors appear on the "correct" line in the source. + */ + +string linespec_ctfe(string file, long line) +{ + return "#line "~Integer.format_ctfe(line)~" \"" ~ file ~ "\"\n"; +} + diff -r b6984b290e46 -r f9559a957be9 qt/qtd/util/Tuple.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/util/Tuple.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,64 @@ +/** + * Tuple-related stuff. + * + * Authors: Daniel Keep + * Copyright: See LICENSE. + */ +module qt.qtd.util.Tuple; + +import std.typetuple; + +/** + * Sequence can be used to generate a tuple of integer values. It can be + * used in one of three forms: + * + * ----- + * Sequence!(6) // ==> (0, 1, 2, 3, 4, 5) + * Sequence!(1,6) // ==> (1, 2, 3, 4, 5) + * Sequence!(1,6,2) // ==> (1, 3, 5) + * ----- + */ + +template Sequence(int max) +{ + static if( max <= 0 ) + alias TypeTuple!() Sequence; + else + alias TypeTuple!(Sequence!(max-1), max-1) Sequence; +} + +/// ditto + +template Sequence(int min, int max) +{ + static if( max <= min ) + alias TypeTuple!() Sequence; + else + alias TypeTuple!(Sequence!(min, max-1), max-1) Sequence; +} + +/// ditto + +template Sequence(int min, int max, int stride) +{ + static if( min >= max ) + alias TypeTuple!() Sequence; + else + alias TypeTuple!(min, Sequence!(min+stride, max, stride)) Sequence; +} + +version( Unittest ) +{ + static assert( Sequence!(3)[0] == 0 ); + static assert( Sequence!(3)[1] == 1 ); + static assert( Sequence!(3)[2] == 2 ); + static assert( Sequence!(3).length == 3 ); + + static assert( Sequence!(1, 3)[0] == 1 ); + static assert( Sequence!(1, 3)[1] == 2 ); + static assert( Sequence!(1, 3).length == 2 ); + + static assert( Sequence!(1, 5, 2)[0] == 1 ); + static assert( Sequence!(1, 5, 2)[1] == 3 ); + static assert( Sequence!(1, 5, 2).length == 2 ); +}