# HG changeset patch # User Max Samukha # Date 1276070936 -10800 # Node ID beaf4a2974d707aa57e3bac87cc13e8b5cbcf016 # Parent 49d0a43433e7d5daf574f9b6ef6fe9f6dd367e91 Autogeneration of QMetaType. First attempts at fixing qRegisterMetaType etc diff -r 49d0a43433e7 -r beaf4a2974d7 build/core.txt --- a/build/core.txt Thu Jun 03 10:12:29 2010 +0300 +++ b/build/core.txt Wed Jun 09 11:08:56 2010 +0300 @@ -1,19 +1,25 @@ configure_file(d${D_VERSION}/qt/QDefines.d.inc qt/QDefines.d) ## Module specific cpp files. -set (cpp_files qt_qtd/qtd_core qt_qtd/ArrayOpsPrimitive_shell - qt_core/QPoint_shell qt_core/QPointF_shell - qt_core/QSize_shell qt_core/QSizeF_shell - qt_core/QLine_shell qt_core/QLineF_shell - qt_core/QRect_shell qt_core/QRectF_shell - qt_core/QString_shell qt_core/QVariant_shell - qt_core/QModelIndex_shell qt_core/QMetaType_shell +set (cpp_files + qt_qtd/qtd_core + qt_qtd/ArrayOpsPrimitive_shell + qt_core/QPoint_shell + qt_core/QPointF_shell + qt_core/QSize_shell + qt_core/QSizeF_shell + qt_core/QLine_shell + qt_core/QLineF_shell + qt_core/QRect_shell + qt_core/QRectF_shell + qt_core/QString_shell + qt_core/QVariant_shell + qt_core/QModelIndex_shell qt_core/QMetaObject_shell) ## Module specific d files. set (d_qt_files QGlobal core/QString - core/QMetaType core/QMetaObject core/QTypeInfo core/QList @@ -34,6 +40,7 @@ MOC Array ArrayOpsPrimitive + QMetaTypeImpl util/Tuple ctfe/Integer ctfe/String @@ -84,5 +91,6 @@ QXmlStreamEntityResolver QFileSystemWatcher QDynamicPropertyChangeEvent + QMetaType ) diff -r 49d0a43433e7 -r beaf4a2974d7 cpp/qt_core/QMetaType_shell.cpp --- a/cpp/qt_core/QMetaType_shell.cpp Thu Jun 03 10:12:29 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ - -#include -#include "qtd_core.h" - -typedef void * Ctor (const void *copy); -typedef void Dtor(void *obj); - -QTD_EXTERN QTD_DLL_PUBLIC int qtd_registerType(char* namePtr, Ctor ctor, Dtor dtor) -{ - return QMetaType::registerType(namePtr, dtor, ctor); -} - -typedef void (*SaveOperator)(void *, void *); -typedef void (*LoadOperator)(void *, void *); - - -QTD_EXTERN QTD_DLL_PUBLIC void qtd_registerStreamOperators(const char *typeName, SaveOperator saveOp, - LoadOperator loadOp) -{ - QMetaType::registerStreamOperators(typeName, reinterpret_cast(saveOp), - reinterpret_cast(loadOp)); -} - - -QTD_EXTERN QTD_DLL_PUBLIC int qtd_MetatypeId(char *id) -{ - return QMetaType::type(id); -} diff -r 49d0a43433e7 -r beaf4a2974d7 d2/qt/core/QVariant.d --- a/d2/qt/core/QVariant.d Thu Jun 03 10:12:29 2010 +0300 +++ b/d2/qt/core/QVariant.d Wed Jun 09 11:08:56 2010 +0300 @@ -90,45 +90,51 @@ // Functions - private template getMetaId() + private int getMetaId(T)(string name) { - const char[] getMetaId = " - int i = qtd_MetatypeId(toStringz(name)); - if(i <= 0) - i = qRegisterMetaType!(T)(name);"; + static shared int sharedId; + static int id; + if (id == 0) + { + synchronized(qtdMoLock) + { + if (sharedId == 0) + sharedId = qRegisterMetaType!T(name); + } + id = sharedId; + } + return id; } static public QVariant fromValue(T)(T obj) { - QVariant var; - static if (is(T == class) || is(T == interface)) - { - string name = obj.classinfo.name; - mixin(getMetaId!()); - var = new QVariant(i, cast(void*)(obj)); - } - else static if (isDynamicArrayType!(T) || isStaticArrayType!(T) ) - { - string name = typeid(ElementTypeOfArray!(T)).toString ~ "[]"; - mixin(getMetaId!()); - auto darray = new DArrayToC; - darray.array = obj.dup; - var = new QVariant(i, cast(void*)(darray)); - } - else - { - string name = typeid(T).toString; - mixin(getMetaId!()); - auto data = new T; - *data = obj; - var = new QVariant(i, cast(void*)(data)); - } - return var; + QVariant var; + static if (is(T == class) || is(T == interface)) + { + string name = obj.classinfo.name; + // TODO: Still hacky. No need to pass name to id getter. + var = new QVariant(getMetaId!T(name), cast(void*)(obj)); + } + else static if (isDynamicArrayType!(T) || isStaticArrayType!(T) ) + { + string name = typeid(ElementTypeOfArray!(T)).toString ~ "[]"; + auto darray = new DArrayToC; + darray.array = obj.dup; + var = new QVariant(getMetaId!T(name), cast(void*)(darray)); + } + else + { + string name = typeid(T).toString; + auto data = new T; + *data = obj; + var = new QVariant(getMetaId!T(name), cast(void*)(data)); + } + return var; } static public QVariant opCall(T)(T obj) { - return fromValue(obj); + return fromValue(obj); } public this() { @@ -304,85 +310,87 @@ super(__qt_return_value); } + // TODO: No need for run time name. Reimplement. private final bool canConvertImpl(string name) { - int i = qtd_MetatypeId(toStringz(name)); - assert(i > 0); - return qtd_QVariant_canConvert(__nativeId, i); + int i = QMetaType.type(toStringz(name)); + assert(i > 0); + return qtd_QVariant_canConvert(__nativeId, i); } + // TODO: reimplement public final bool canConvert(Type)() { - static if ( is(Type == QBitArray) ) - return canConvertImpl("QBitArray"); - else static if ( is(Type == bool) ) - return canConvertImpl("bool"); - else static if ( is(Type == QByteArray) ) - return canConvertImpl("QByteArray"); - else static if ( is(Type == QDate) ) - return canConvertImpl("QDate"); - else static if ( is(Type == QDateTime) ) - return canConvertImpl("QDateTime"); - else static if ( is(Type == double) ) - return canConvertImpl("double"); - else static if ( is(Type == int) ) - return canConvertImpl("int"); - else static if ( is(Type == QLine) ) - return canConvertImpl("QLine"); - else static if ( is(Type == QLineF) ) - return canConvertImpl("QLineF"); - else static if ( is(Type == QLocale) ) - return canConvertImpl("QLocale"); - else static if ( is(Type == long) ) - return canConvertImpl("long"); - else static if ( is(Type == QPoint) ) - return canConvertImpl("QPoint"); - else static if ( is(Type == QPointF) ) - return canConvertImpl("QPointF"); - else static if ( is(Type == QRect) ) - return canConvertImpl("QRect"); - else static if ( is(Type == QRectF) ) - return canConvertImpl("QRectF"); - else static if ( is(Type == QRegExp) ) - return canConvertImpl("QRegExp"); - else static if ( is(Type == QSize) ) - return canConvertImpl("QSize"); - else static if ( is(Type == QSizeF) ) - return canConvertImpl("QSizeF"); - else static if ( is(Type == string) ) - return canConvertImpl("QString"); - else static if ( is(Type == QTime) ) - return canConvertImpl("QTime"); - else static if ( is(Type == uint) ) - return canConvertImpl("unsigned int"); // TODO: - else static if ( is(Type == ulong) ) - return canConvertImpl("unsigned long long"); // TODO: - else static if ( is(Type == QUrl) ) - return canConvertImpl("QUrl"); - else - { - static if( is( Type == class ) || is( Type == interface ) ) - { - Object object = cast(Object)qtd_QVariant_data(__nativeId); - if(object) - return cast(Type)(object) !is null; - return false; - } - else static if (isDynamicArrayType!(Type) || isStaticArrayType!(Type) ) - { - auto array = cast(DArrayToC*)qtd_QVariant_data(__nativeId); - return cast(Type)(array.array) !is null; - } - else - { - int i = qtd_MetatypeId(toStringz(typeid(Type).toString)); - return qtd_QVariant_canConvert(__nativeId, i); - } - } + static if ( is(Type == QBitArray) ) + return canConvertImpl("QBitArray"); + else static if ( is(Type == bool) ) + return canConvertImpl("bool"); + else static if ( is(Type == QByteArray) ) + return canConvertImpl("QByteArray"); + else static if ( is(Type == QDate) ) + return canConvertImpl("QDate"); + else static if ( is(Type == QDateTime) ) + return canConvertImpl("QDateTime"); + else static if ( is(Type == double) ) + return canConvertImpl("double"); + else static if ( is(Type == int) ) + return canConvertImpl("int"); + else static if ( is(Type == QLine) ) + return canConvertImpl("QLine"); + else static if ( is(Type == QLineF) ) + return canConvertImpl("QLineF"); + else static if ( is(Type == QLocale) ) + return canConvertImpl("QLocale"); + else static if ( is(Type == long) ) + return canConvertImpl("long"); + else static if ( is(Type == QPoint) ) + return canConvertImpl("QPoint"); + else static if ( is(Type == QPointF) ) + return canConvertImpl("QPointF"); + else static if ( is(Type == QRect) ) + return canConvertImpl("QRect"); + else static if ( is(Type == QRectF) ) + return canConvertImpl("QRectF"); + else static if ( is(Type == QRegExp) ) + return canConvertImpl("QRegExp"); + else static if ( is(Type == QSize) ) + return canConvertImpl("QSize"); + else static if ( is(Type == QSizeF) ) + return canConvertImpl("QSizeF"); + else static if ( is(Type == string) ) + return canConvertImpl("QString"); + else static if ( is(Type == QTime) ) + return canConvertImpl("QTime"); + else static if ( is(Type == uint) ) + return canConvertImpl("unsigned int"); // TODO: + else static if ( is(Type == ulong) ) + return canConvertImpl("unsigned long long"); // TODO: + else static if ( is(Type == QUrl) ) + return canConvertImpl("QUrl"); + else + { + static if( is( Type == class ) || is( Type == interface ) ) + { + Object object = cast(Object)qtd_QVariant_data(__nativeId); + if(object) + return cast(Type)(object) !is null; + return false; + } + else static if (isDynamicArrayType!(Type) || isStaticArrayType!(Type) ) + { + auto array = cast(DArrayToC*)qtd_QVariant_data(__nativeId); + return cast(Type)(array.array) !is null; + } + else + { + int i = QMetaType.type(toStringz(typeid(Type).toString)); + return qtd_QVariant_canConvert(__nativeId, i); + } + } } public final Type value(Type)() { static if ( is(Type == QBitArray) ) - return toBitArra; + return toBitArray; else static if ( is(Type == bool) ) return toBool; else static if ( is(Type == QByteArray) ) @@ -612,11 +620,11 @@ protected override void __deleteNative() { qtd_QVariant_destructor(__nativeId); } - + public alias void __isValueType; public alias void __isQtType_QVariant; - + struct QTypeInfo { enum bool isComplex = true; @@ -625,7 +633,7 @@ enum bool isPointer = false; enum bool isDummy = false; } - + static void* __constructNativeCopy(const void* orig) { return qtd_QVariant_QVariant_QVariant(cast(void*)orig); } @@ -633,11 +641,11 @@ static void* __constructPlacedNativeCopy(const void* orig, void* place) { return qtd_QVariant_placed_copy(orig, place); } - + public static void __deleteNativeObject(void* ptr) { qtd_QVariant_destructor(ptr); } - + public static void __callNativeDestructor(void* ptr) { qtd_QVariant_call_destructor(ptr); } diff -r 49d0a43433e7 -r beaf4a2974d7 d2/qtd/QMetaTypeImpl.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/d2/qtd/QMetaTypeImpl.d Wed Jun 09 11:08:56 2010 +0300 @@ -0,0 +1,141 @@ +module qtd.QMetaTypeImpl; + +import + qt.core.Qt, + qt.core.QDataStream, + qtd.QtdObject; + +// TODO: remove +import std.stdio; + +private struct DArrayToC +{ + void[] array; +} + +/** + */ +template MetaTypeOps(T) +{ + static void* construct(void* copy) + { + static assert (is(T : Object)); + return cast(void*)new T(cast(T)copy); + } + + static void destroy(void* ptr) + { + (cast(T)ptr).dispose(); + } +} + +/** + */ +template MetaTypeStreamOps(T) +{ + void save(QDataStream ds, const void* data) + { + writeln("Saving ", ds.__nativeId, " ", data); + } + + void load(QDataStream ds, void* data) + { + writeln("Loading ", ds.__nativeId, " ", data); + } +} + +/** + */ +int qRegisterMetaType(T, alias ops = MetaTypeOps)(string name = null) +{ + if (!name.length) + name = typeid(T).toString; //TODO: use compile time full name? + + alias ops!T.construct construct; + alias ops!T.destroy destroy; + + // TODO: only GNU C++ + extern(C) static void ctorShim() + { + asm + { + naked; + push EBP; + mov EBP, ESP; + mov EAX, 0x8[EBP]; + call construct; + leave; + ret; + } + } + + extern(C) static void dtorShim() + { + asm + { + naked; + push EBP; + mov EBP, ESP; + mov EAX, 0x8[EBP]; + call destroy; + leave; + ret; + } + } + + return qtd_registerType(toStringz(name), &dtorShim, &ctorShim); +} + + +// COMPILER BUG: cannot put this inside qRegisterMetaTypeStreamOperators +// COMPILER BUG 2: cannot use extern(C) with templated functions: extern(C) void foo(T)(){} +private template streamOpShim(alias op) +{ + extern(C) void streamOpShim() + { + asm + { + naked; + push EBP; + mov EBP, ESP; + mov EAX, 0x8[EBP]; + push EAX; + mov EAX, 0xC[EBP]; + call op; + leave; + ret; + } + } +} + +/** + */ +void qRegisterMetaTypeStreamOperators(T, alias ops = MetaTypeStreamOps)(string name = null) +{ + if (!name.length) + name = typeid(T).toString; + + static void save(void* ds, const void* data) + { + scope dataStream = new QDataStream(ds, QtdObjectFlags.nativeOwnership); + ops!T.save(dataStream, data); + } + + static void load(void* ds, void* data) + { + scope dataStream = new QDataStream(ds, QtdObjectFlags.nativeOwnership); + ops!T.load(dataStream, data); + } + + qtd_registerStreamOperators(toStringz(name), &streamOpShim!save, &streamOpShim!load); +} + +/** + */ +private extern(C) +{ + void qtd_registerStreamOperators(in char *typeName, VoidFunc saveOp, VoidFunc loadOp); + int qtd_registerType(in char* namePtr, VoidFunc ctor, VoidFunc dtor); + int qtd_QMetaType_type_nativepointerchar(in char* typeName0); +} + diff -r 49d0a43433e7 -r beaf4a2974d7 generator/cppimplgenerator.cpp --- a/generator/cppimplgenerator.cpp Thu Jun 03 10:12:29 2010 +0300 +++ b/generator/cppimplgenerator.cpp Wed Jun 09 11:08:56 2010 +0300 @@ -899,9 +899,9 @@ void CppImplGenerator::writeInitCallbacks(QTextStream &s, const AbstractMetaClass *java_class) { - QString initArgs = "pfunc_abstr *virts"; + QString initArgs = "VoidFunc *virts"; if (java_class->name() == "QObject") - initArgs += ", pfunc_abstr *sigs"; + initArgs += ", VoidFunc *sigs"; s << "QTD_EXTERN QTD_DLL_EXPORT void qtd_" << java_class->name() << QString("_initCallBacks(%1) {").arg(initArgs) << endl; diff -r 49d0a43433e7 -r beaf4a2974d7 generator/dgenerator.cpp --- a/generator/dgenerator.cpp Thu Jun 03 10:12:29 2010 +0300 +++ b/generator/dgenerator.cpp Wed Jun 09 11:08:56 2010 +0300 @@ -1726,9 +1726,9 @@ void DGenerator::writeFlagsSetter(QTextStream &s, const AbstractMetaClass *d_class) { - if (d_class->isInterface() || d_class->isNamespace()) + if (d_class->isInterface()) s << INDENT << "void __setFlags(QtdObjectFlags flags, bool val);"; - else // COMPILER BUG: + else if (!d_class->isNamespace()) // COMPILER BUG: s << INDENT << "void __setFlags(QtdObjectFlags flags, bool val) { super.__setFlags(flags, val); }"; } @@ -1844,7 +1844,8 @@ auxFile.isDone = true; auxFile.stream << "module " << auxModName << ";" << endl << endl; - bool staticInit = d_class->isQObject() || d_class->typeEntry()->isValue() || (cpp_shared && d_class->generateShellClass() && !d_class->isInterface()); + bool staticInit = d_class->isQObject() || d_class->typeEntry()->isValue() + || (cpp_shared && d_class->generateShellClass() && !d_class->isInterface() && !d_class->isNamespace()); if (staticInit) { auxFile.isDone = false; diff -r 49d0a43433e7 -r beaf4a2974d7 generator/typesystem.cpp --- a/generator/typesystem.cpp Thu Jun 03 10:12:29 2010 +0300 +++ b/generator/typesystem.cpp Wed Jun 09 11:08:56 2010 +0300 @@ -779,6 +779,7 @@ case StackElement::Include: attributes["file-name"] = QString(); attributes["location"] = QString(); + attributes["protection"] = QString(); break; case StackElement::CustomMetaConstructor: attributes["name"] = topElement.entry->name().toLower() + "_create"; @@ -1406,13 +1407,34 @@ locationNames["java"] = Include::TargetLangImport; } + if (locationNames.isEmpty()) { + locationNames["global"] = Include::IncludePath; + locationNames["local"] = Include::LocalPath; + locationNames["java"] = Include::TargetLangImport; + } + if (!locationNames.contains(location)) { m_error = QString("Location not recognized: '%1'").arg(location); return false; } Include::IncludeType loc = locationNames[location]; - Include inc(loc, attributes["file-name"]); + + QString protection = attributes["protection"]; + if (!protection.isEmpty()) { + if (loc != Include::TargetLangImport) { + m_error = QString("Protection attribute is allowed only if 'java' location is specified"); + return false; + } + + if (protection != "public" + || protection != "private" + || protection != "package") { + m_error = QString("Import protection is not recognized: '%1'").arg(protection); + } + } + + Include inc(loc, protection, attributes["file-name"]); ComplexTypeEntry *ctype = static_cast(element->entry); if (topElement.type & StackElement::ComplexTypeEntryMask) { @@ -1645,8 +1667,12 @@ return "#include <" + name + '>'; else if (type == LocalPath) return "#include \"" + name + "\""; - else - return "import " + name + ";"; + else { + QString result; + if (!protection.isEmpty()) + result += protection + " "; + return result + "import " + name + ";"; + } } QString Modification::accessModifierString() const diff -r 49d0a43433e7 -r beaf4a2974d7 generator/typesystem.h --- a/generator/typesystem.h Thu Jun 03 10:12:29 2010 +0300 +++ b/generator/typesystem.h Wed Jun 09 11:08:56 2010 +0300 @@ -73,12 +73,21 @@ }; Include() : type(IncludePath) { } - Include(IncludeType t, const QString &nam) : type(t), name(nam) { }; + Include(IncludeType t, const QString prot, const QString &nam) : + type(t), + protection(prot), + name(nam) { }; + + Include(IncludeType t, const QString &nam) : + type(t), + protection(QString()), + name(nam) { }; bool isValid() { return !name.isEmpty(); } IncludeType type; QString name; + QString protection; QString toString() const; diff -r 49d0a43433e7 -r beaf4a2974d7 generator/typesystem_core.xml --- a/generator/typesystem_core.xml Thu Jun 03 10:12:29 2010 +0300 +++ b/generator/typesystem_core.xml Wed Jun 09 11:08:56 2010 +0300 @@ -427,7 +427,6 @@ - @@ -1977,6 +1976,27 @@ + + + + + + + +QTD_EXTERN QTD_DLL_PUBLIC int qtd_registerType(char* namePtr, VoidFunc dtor, VoidFunc ctor) +{ + return QMetaType::registerType(namePtr, (QMetaType::Destructor)dtor, (QMetaType::Constructor)ctor); +} + +QTD_EXTERN QTD_DLL_PUBLIC void qtd_registerStreamOperators(const char *typeName, VoidFunc saveOp, + VoidFunc loadOp) +{ + QMetaType::registerStreamOperators(typeName, (QMetaType::SaveOperator)saveOp, + (QMetaType::LoadOperator)loadOp); +} + + + diff -r 49d0a43433e7 -r beaf4a2974d7 include/qtd_core.h --- a/include/qtd_core.h Thu Jun 03 10:12:29 2010 +0300 +++ b/include/qtd_core.h Wed Jun 09 11:08:56 2010 +0300 @@ -17,7 +17,7 @@ #ifdef CPP_SHARED - QTD_EXTERN typedef void (*pfunc_abstr)(); + QTD_EXTERN typedef void (*VoidFunc)(); #define QTD_EXPORT_DECL(MODULE, TYPE, NAME, ARGS) \ QTD_EXTERN typedef TYPE (*qtd_##NAME##_t)ARGS; \ @@ -25,7 +25,7 @@ #define QTD_EXPORT(MODULE, NAME) \ QTD_EXTERN { QTD_##MODULE##_DLL_PUBLIC qtd_##NAME##_t qtd_##NAME; } \ - QTD_EXTERN QTD_DLL_EXPORT void qtd_set_##NAME(pfunc_abstr func) { qtd_##NAME = (qtd_##NAME##_t)func; } + QTD_EXTERN QTD_DLL_EXPORT void qtd_set_##NAME(VoidFunc func) { qtd_##NAME = (qtd_##NAME##_t)func; } #endif diff -r 49d0a43433e7 -r beaf4a2974d7 mini/test1/build.bat --- a/mini/test1/build.bat Thu Jun 03 10:12:29 2010 +0300 +++ b/mini/test1/build.bat Wed Jun 09 11:08:56 2010 +0300 @@ -1,1 +1,2 @@ -dmd main.d libqtdcore.lib -I../../ \ No newline at end of file +set LIB=E:\d-projects\qtd-trunk\output\build\lib +dmd main.d ..\..\output\build\lib\qtdcore.lib -I../../d2 -I../../output/build \ No newline at end of file diff -r 49d0a43433e7 -r beaf4a2974d7 mini/test1/main.d --- a/mini/test1/main.d Thu Jun 03 10:12:29 2010 +0300 +++ b/mini/test1/main.d Wed Jun 09 11:08:56 2010 +0300 @@ -1,31 +1,62 @@ -import qt.core.QCoreApplication; +import qt.core.QMetaType; + +import std.stdio; +import std.conv; +import qtd.QtdObject; + +class A +{ + string name; -version(Tango) { import tango.io.Stdout; } else { import std.stdio; } + this(A copy) + { + writeln("Creating new from ", copy.name); + name = "Copy of " ~ copy.name; + } -int main(string[] args) + this(string name) + { + this.name = name; + } + + void dispose() + { + writeln("Disposing ", name); + } +} + +void main(string[] args) { - auto app = new QCoreApplication(args); - - auto parent = new QObject(); - parent.setObjectName("papa"); - auto child1 = new QObject(parent); - child1.setObjectName("child1"); - auto child2 = new QObject(parent); - child2.setObjectName("child2"); - auto child3 = new QObject(parent); - child3.setObjectName("child3"); - - auto cd = parent.children; - Stdout(parent.children.length).newline; + int id = qRegisterMetaType!A(); + qRegisterMetaTypeStreamOperators!A(); + + foreach (i; 0..10) + { + writeln("Iter ", i); + + void foo(int x, int y, int z) + { + auto a = new A("A" ~ to!string(i)); + auto b = cast(A)QMetaType.construct(id, cast(void*)a); + writeln(b.name); + + QMetaType.destroy(id, cast(void*)a); + QMetaType.destroy(id, cast(void*)b); - Stdout(app.arguments).newline; - foreach(child; cd) - Stdout(child.objectName).newline; - - app.setLibraryPaths(["freakin", "bloody", "awesome!"]); + scope ds = new QDataStream(cast(void*)3, QtdObjectFlags.nativeOwnership); + QMetaType.save(ds, id, cast(void*)i); + QMetaType.load(ds, id, cast(void*)i); + writeln("Done iterating ", x, " ", y, " ", z); + } - Stdout(app.libraryPaths).newline; - - return 5; -// return app.exec(); + foo(i + 1, i + 2, i + 3); + } + /+ + + writeln("Great!"); + + + writeln("Even greater!"); + +/ + }