Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

StackOverflow Point

StackOverflow Point Navigation

  • Web Stories
  • Badges
  • Tags
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Web Stories
  • Badges
  • Tags
Home/ Questions/Q 185867
Alex Hales
  • 0
Alex HalesTeacher
Asked: June 10, 20222022-06-10T08:41:24+00:00 2022-06-10T08:41:24+00:00

c++ – “Reimplementing” Connections to catch all custom types within specified signals in a QML hook

  • 0

[ad_1]

We are developing a test tool for Qt/QML apps based on the Petri’s Nets and looking for a legal hack to reimplement Connections a bit differently, but without any private API.

Consider a Petri’s transition declared in a QML component like:

Transition {
   act: {
       obj1.callAsyncOperation1();
       obj2.callAsyncOperation2();
      //...
   }

   TimeWaiter {
      timeout: 100
   }

   ObjectWaiter {
      target: obj1

      function onAsyncOperation1Done(arg1: int, arg2: CustomAnonymousType)
      {
         console.log("...");
      }

      function onAsyncOperation1Failed(message: string, fatal: bool)
      {
         console.error("...");
      }
   }
}
  1. act is called from c++ logic
  2. wait for any specified signal if some *Waiters are declared in a Transition
  3. go to the next step (according to the PN idea after an Action phase in a Transition we are switching to an Assertion stage declared in some Place but this out of scope and just FYI)

Just problem we have in the current implementation is accessing custom types within signals due to nature of binding with dynamically declared slots. What we need are connections between signals from a target and all declared slots to test& debug purposes + a bypass signal from a target to a Transition as a permit to move on.

a) values are undefined if types are not declared in slots at all
b) some kind of UB happens and app is crashing time to time if types are declared as variants
c) works as expected just if types are fully and properly(according to a signal) defined

The corner case of this story are custom types which don’t have typenames in the QML scope which are registered via qmlRegisterAnonymousType() or qRegisterMetaType().

Is there any method to make a connection between signals and slots with such arguments?
Now we are using QMetaObject::connect and finally, in qqmlvmemetaobject.cpp
it boils down to:

 for (uint ii = 0; ii < parameterCount; ++ii) {
      jsCallData->args[ii] = scope.engine->metaTypeToJS(arguments->arguments[ii + 1], a[ii + 1]);
  }

  const int returnType = methodData->propType();
  QV4::ScopedValue result(scope, function->call(jsCallData));

metaTypeToJS(...) are converting all undefined arguments to QV4::Value(Null) through intermediate QV4::Encode::undefined:

QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
{
    // ...  
    if (type < QMetaType::User) {
        switch (QMetaType::Type(type)) {
            case QMetaType::UnknownType:
            case QMetaType::Void:
                return QV4::Encode::undefined();

Real code where we are remembering declared slots:

void ObjectWaiter::componentComplete()
{
    const QMetaObject* meta_object(metaObject());
    if (!meta_object) {
        return ;
    }
    for (int m = meta_object->methodOffset(), count = meta_object->methodOffset() + meta_object->methodCount(); m < count; ++m) {
        const QMetaMethod meta_method(meta_object->method(m));
        if (meta_method.methodType() == QMetaMethod::MethodType::Slot) {
            QByteArray slot_name(meta_method.name().remove(0, 2));
            if (!slot_name.isEmpty()) {
                slot_name.front() = QChar(slot_name.front()).toLower().toLatin1();
                _slot_map.insert(slot_name, meta_method);
            }
        }
    }
    bind();
}

… and connecting signals to slots when target is changed:

void ObjectWaiter::bind()
{
    if (!_target || _slot_map.isEmpty()) {
        return ;
    }

    const QMetaObject* meta_object(_target->metaObject());
    if (!meta_object) {
        return ;
    }
    for (int m = meta_object->methodOffset(), count = meta_object->methodOffset() + meta_object->methodCount(); m < count; ++m) {
        const QMetaMethod meta_method(meta_object->method(m));
        if (meta_method.methodType() == QMetaMethod::MethodType::Signal) {
            QMap<QByteArray, QMetaMethod>::ConstIterator m(_slot_map.find(meta_method.name()));
            if (_slot_map.constEnd() != m) {
                std::unique_ptr<int[]> argv(new int[meta_method.parameterCount()]{});
                for (int p = 0; p < meta_method.parameterCount(); ++p) {
                    int parameter_type(meta_method.parameterType(p));
                    if (QMetaType::UnknownType != parameter_type) {
                        argv[p] = parameter_type;
                    } else {
                        void* arg[] = { &parameter_type, &p };
                        QMetaObject::metacall(_target, QMetaObject::RegisterMethodArgumentMetaType, meta_method.methodIndex(), arg);
                        if (parameter_type == -1) {
                            argv[p] = QMetaType::UnknownType;
                            qWarning(
                                    "ObjectWaiter: Unable to handle parameter '%s' of type '%s' of method '%s', use qRegisterMetaType to register it.",
                                    meta_method.parameterNames().at(p).constData(), meta_method.parameterTypes().at(p).constData(), meta_method.name().constData()
                            );
                        } else {
                            argv[p] = parameter_type;
                        }
                    }
                }

                if (!QMetaObject::connect(_target, meta_method.methodIndex(), this, m.value().methodIndex(), Qt::DirectConnection, argv.release())) {
                    qWarning("ObjectWaiter: Unable to make a dynamic connect");
                }
                if (!QObject::connect(_target, meta_method, this, QMetaMethod::fromSignal(&AbstractWaiter::done))) {
                    qWarning("ObjectWaiter: Unable to make the connection and won't be able to emit a stop signal at the end");
                }
            }
        }
    }
}

I was digging around Connections and QSpySingal implementations but could not find any other public API to reimplement ObjectWaiter, but Qt is huge and hopefully there are something I have missed.

[ad_2]

  • 0 0 Answers
  • 1 View
  • 0 Followers
  • 0
Share
  • Facebook
  • Report
Leave an answer

Leave an answer
Cancel reply

Browse

Sidebar

Ask A Question

Related Questions

  • xcode - Can you build dynamic libraries for iOS and ...

    • 0 Answers
  • bash - How to check if a process id (PID) ...

    • 318 Answers
  • database - Oracle: Changing VARCHAR2 column to CLOB

    • 290 Answers
  • What's the difference between HEAD, working tree and index, in ...

    • 285 Answers
  • Amazon EC2 Free tier - how many instances can I ...

    • 0 Answers

Stats

  • Questions : 43k

Subscribe

Login

Forgot Password?

Footer

Follow

© 2022 Stackoverflow Point. All Rights Reserved.

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.