void marshal_from_sexp<SmokeClassWrapper>(MethodCall *m) { SEXP v = m->sexp(); SmokeType type = m->type(); SmokeObject *o = NULL /*, *coerced = NULL */; /* Not clear if we want to (partially) support this complicated C++ feature if (v != R_NilValue) { o = SmokeObject::fromSexp(v); if (!o->instanceOf(type.className())) { // attempt implicit conversion coerced = o->convertImplicitly(type); if (coerced) o = coerced; else error("Failed to coerce an instance of type '%s' to '%s'", o->className(), type.className()); } } */ o = from_sexp<SmokeObject *>(v, type); void *ptr = o ? o->castPtr(type.className(), m->returning() && !type.fitsStack()) : NULL; setItemValue(m, ptr); /* m->marshal(); if (coerced) delete coerced; */ return; }
QByteArray MethodCall::argKey(SEXP arg) const { const char * className = CHAR(asChar(getAttrib(arg, R_ClassSymbol))); const char *r = ""; if (arg == R_NilValue) r = "u"; else if (TYPEOF(arg) == INTSXP) { if (inherits(arg, "QtEnum")) r = className; else r = "i"; } else if (TYPEOF(arg) == REALSXP) r = "n"; else if (TYPEOF(arg) == STRSXP) r = "s"; else if(TYPEOF(arg) == LGLSXP) r = "B"; else if (TYPEOF(arg) == EXTPTRSXP) { SmokeObject *o = SmokeObject::fromSexp(arg); if (o == 0 || o->smoke() == 0) { r = "a"; } else { r = o->smoke()->classes[o->classId()].className; } } else { r = "U"; } return QByteArray(r); }
SEXP SmokeObject::sexpFromPtr(void *ptr, const Class *klass, bool allocated, bool copy) { SmokeObject *so = fromPtr(ptr, klass, allocated, copy); if (!so) return R_NilValue; else return so->sexp(); }
void *_unwrapSmoke(SEXP x, const char *type) { void *ans = NULL; if (!isNull(x)) { SmokeObject *so = SmokeObject::fromSexp(x); if (so) ans = so->castPtr(type); } return ans; }
SEXP qt_qdataFrameModel(SEXP rparent, SEXP useRoles, SEXP editable) { static Class *dataFrameModelClass = new NameOnlyClass("DataFrameModel", Class::fromName("QAbstractTableModel")); SmokeObject *so = SmokeObject::fromPtr(new DataFrameModel(unwrapSmoke(rparent, QObject), useRoles, editable), Class::fromName("QAbstractTableModel"), true); so->cast(dataFrameModelClass); return so->sexp(); }
extern "C" SEXP qt_qmetaInvoke(SEXP x, SEXP s_id, SEXP s_args) { SmokeObject *so = SmokeObject::fromSexp(x); int id = from_sexp<int>(s_id); QObject * qobj = reinterpret_cast<QObject *>(so->castPtr("QObject")); MocMethod method(so->smoke(), qobj->metaObject(), id); SEXP ret = method.invoke(x, s_args); if (method.lastError() > Method::NoError) error("Meta method invocation failed for: '%s::%s'", so->klass()->name(), method.name()); return ret; }
/* We catch all qt_metacall invocations */ extern "C" SEXP qt_qmetacall(SEXP x, SEXP s_call, SEXP s_id, SEXP s_args) { SmokeObject *so = SmokeObject::fromSexp(x); QMetaObject::Call call = enum_from_sexp<QMetaObject::Call>(s_call, SmokeType()); int id = from_sexp<int>(s_id); void **args = reinterpret_cast<void **>(from_sexp<void *>(s_args)); // Assume the target slot is a C++ one Smoke::StackItem i[4]; i[1].s_enum = call; i[2].s_int = id; i[3].s_voidp = args; so->invokeMethod("qt_metacall$$?", i); int ret = i[0].s_int; if (ret < 0) { return ScalarInteger(ret); } if (call != QMetaObject::InvokeMetaMethod) return ScalarInteger(id); QObject * qobj = reinterpret_cast<QObject *>(so->castPtr("QObject")); // get obj metaobject with a virtual call const QMetaObject *metaobject = qobj->metaObject(); // get method count int count = metaobject->methodCount(); QMetaMethod method = metaobject->method(id); if (method.methodType() == QMetaMethod::Signal) { // FIXME: this override of 'activate' is obsolete metaobject->activate(qobj, id, (void**) args); return ScalarInteger(id - count); } DynamicBinding binding(MocMethod(so->smoke(), metaobject, id)); QVector<SmokeType> stackTypes = binding.types(); MocStack mocStack = MocStack(args, stackTypes.size()); SmokeStack smokeStack = mocStack.toSmoke(stackTypes); binding.invoke(so, smokeStack.items()); mocStack.returnFromSmoke(smokeStack, stackTypes[0]); if (binding.lastError() == Method::NoError) warning("Slot invocation failed for %s::%s", so->klass()->name(), binding.name()); return ScalarInteger(id - count); }
Method* MocClass::findMethod(const MethodCall& call) const { Method *method = _delegate->findMethod(call); if (method) return(method); SmokeObject *o = call.target(); /* only QObjects have meta methods */ if (o && o->ptr() && o->instanceOf("QObject") && o->klass() == this) { /* unwrap the call */ QObject * qobject = reinterpret_cast<QObject *>(o->castPtr("QObject")); const QMetaObject * meta = qobject->metaObject(); /* get the method id */ int id = findMethodId(o->smoke(), meta, call.method()->name(), call.args()); if (id >= 0) method = new MocMethod(o->smoke(), meta, id); } return method; }
SmokeObject * SmokeObject::fromPtr(void *ptr, const Class *klass, bool allocated, bool copy) { if (!klass) error("Attempt to create SmokeObject with NULL class"); if (!ptr) error("Attempt to create SmokeObject with NULL pointer"); SmokeObject *so = instances[ptr]; if (!so) { so = new SmokeObject(ptr, klass, allocated); #ifdef MEM_DEBUG qDebug("%p: created for %p (%s)", so, ptr, klass->name()); #endif // record this ASAP, resolveClassId() needs it for virtual callbacks if (allocated) // do not record unallocated; not informed when deleted instances[so->ptr()] = so; #ifdef MEM_DEBUG else qDebug("%p: unallocated, not registering pointer", so); #endif so->cast(Class::fromSmokeId(so->smoke(), so->module()->resolveClassId(so))); /* it seems that all multiple inheritance in Qt goes through QObject or QEvent, so we can catch offset pointers at run-time */ // FIXME: what happens with other libraries? take QtRuby approach? #ifdef MEM_DEBUG if (allocated && so->klass() != klass) qDebug("%p: class switch %s::%s -> %s::%s", so, klass->smokeBase()->smoke()->moduleName(), klass->name(), so->klass()->smokeBase()->smoke()->moduleName(), so->klass()->name()); #endif if (so->ptr() != ptr) { // must be multiple inheritance, recache SmokeObject *tmp_so = instances[so->ptr()]; #ifdef MEM_DEBUG qDebug("%p: multiple inheritance detected, switch to %p", so, so->ptr()); #endif if (tmp_so) { #ifdef MEM_DEBUG qDebug("%p: replaced with existing %p", so, tmp_so); #endif delete so; so = tmp_so; copy = false; // don't think we every want to copy here } else instances[so->ptr()] = so; instances.remove(ptr); } if (copy) { // copy the data void *tmp_ptr = so->ptr(); so->_ptr = so->clonePtr(); instances[so->ptr()] = so; // update the instances hash after cloning instances.remove(tmp_ptr); #ifdef MEM_DEBUG qDebug("%p: copied to %p", so, so->ptr()); #endif so->_allocated = true; } } return so; }
/* C++ compilers select overloaded methods by ranking each argument in terms of its implicit conversions. There are three different types of implicit conversions: standard, user and ellipsis. We only consider the former, as the others probably are not relevant for Qt. Within standard conversions, there are three ranks: exact, promotion and conversion. We assign these scores 3, 2 and 1, respectively, while 0 indicates no conversion and -1 indicates an error (e.g. unsupported type). The method with better or same ranks for all of its parameters is selected. If there is a tie for the best method, there is an error (in our code). */ int scoreArg_basetype(SEXP arg, const SmokeType &type) { int score = 0; SEXP value = arg; int rtype = TYPEOF(value); unsigned short elem = type.elem(); switch(rtype) { // try the simple cases first case RAWSXP: switch(elem) { case Smoke::t_uchar: score = 3; break; case Smoke::t_short: case Smoke::t_ushort: score = 2; break; case Smoke::t_int: case Smoke::t_uint: case Smoke::t_long: case Smoke::t_ulong: case Smoke::t_float: case Smoke::t_double: score = 1; break; default: break; } break; case INTSXP: switch(elem) { case Smoke::t_enum: if (inherits(value, "QtEnum")) score = 2; else score = 1; break; case Smoke::t_int: score = 3; break; case Smoke::t_uint: if (inherits(value, "QtEnum")) { score = 3; // favor QFlags over enums break; } case Smoke::t_long: case Smoke::t_ulong: case Smoke::t_float: case Smoke::t_double: score = 2; break; case Smoke::t_short: case Smoke::t_ushort: case Smoke::t_uchar: score = 1; break; default: break; } break; case REALSXP: switch(elem) { case Smoke::t_double: score = 3; break; case Smoke::t_float: case Smoke::t_uint: // to distinguish int/uint score = 2; break; case Smoke::t_int: case Smoke::t_long: case Smoke::t_ulong: case Smoke::t_short: case Smoke::t_ushort: case Smoke::t_uchar: case Smoke::t_enum: score = 1; break; default: break; } break; case STRSXP: if (elem == Smoke::t_char && strlen(CHAR(asChar(value))) == 1) score = 2; break; case ENVSXP: if (elem == Smoke::t_class) { SmokeObject *o = SmokeObject::fromSexp(value); if (o) { const char *smokeClass = type.className(); if (o->className() == smokeClass) score = 3; else if (o->instanceOf(smokeClass)) score = 2; /* Not clear if we want to support this C++ feature else { Method *m = Class::fromSmokeType(type)->findImplicitConverter(o); if (m) { score = 1; delete m; } } */ } } break; case NILSXP: if (type.isPtr()) score = 1; break; default: break; } return score; }
QVariant qvariant_from_sexp(SEXP rvalue, int index) { QVariant variant; if (index == -1) { /* If a particular element is not selected, then non-lists of length one are considered scalars. Otherwise, collections. Except for raw vectors, which are naturally QByteArrays. */ if (TYPEOF(rvalue) == RAWSXP) return QVariant(from_sexp<QByteArray>(rvalue)); else if (TYPEOF(rvalue) == VECSXP || length(rvalue) > 1) { SEXP rlist = coerceVector(rvalue, VECSXP); if (getAttrib(rvalue, R_NamesSymbol) != R_NilValue) variant = asQVariantOfType(rlist, QMetaType::QVariantMap); else variant = asQVariantOfType(rlist, QMetaType::QVariantList); return variant; } index = 0; } switch(TYPEOF(rvalue)) { case RAWSXP: variant = qVariantFromValue(RAW(rvalue)[index]); break; case LGLSXP: // Rprintf("Logical\n"); // FIXME: by converting to 'bool' all NA become TRUE variant = QVariant((bool)LOGICAL(rvalue)[index]); break; case REALSXP: // Rprintf("Real\n"); variant = QVariant(REAL(rvalue)[index]); break; case INTSXP: // Rprintf("Integer\n"); { SEXP levels; if ((levels = getAttrib(rvalue, R_LevelsSymbol)) != R_NilValue) { int level = INTEGER(rvalue)[index]; SEXP level_str = NA_STRING; /*Rprintf("getting level: %d\n", level);*/ if (level != NA_INTEGER) level_str = STRING_ELT(levels, level - 1); variant = QVariant(sexp2qstring(level_str)); } else variant = QVariant(INTEGER(rvalue)[index]); break; } case STRSXP: // Rprintf("String\n"); variant = QVariant(sexp2qstring(STRING_ELT(rvalue, index))); break; case EXTPTRSXP: // Rprintf("External pointer\n"); variant = qVariantFromValue(unwrapPointer(rvalue, void)); break; case VECSXP: variant = from_sexp<QVariant>(VECTOR_ELT(rvalue, index)); break; case ENVSXP: { SmokeObject *so = SmokeObject::fromSexp(rvalue); if (so->instanceOf("QWidget")) variant = qVariantFromValue(reinterpret_cast<QWidget *>(so->castPtr("QWidget"))); else if (so->instanceOf("QObject")) variant = qVariantFromValue(reinterpret_cast<QObject *>(so->castPtr("QObject"))); else { QMetaType::Type type = (QMetaType::Type) QMetaType::type(so->className()); if (type) variant = asQVariantOfType(rvalue, type, false); else variant = qVariantFromValue(so->ptr()); } } break; case NILSXP: // invalid QVariant break; default: error("Converting to QVariant: unhandled R type"); } return variant; }
SEXP qt_qcast(SEXP x, SEXP klass) { SmokeObject *obj = SmokeObject::fromSexp(x); obj->cast(Class::fromSexp(klass)); return obj->sexp(); }