Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Passing variable-length args in container for QMetaObject::invokeMethod
Forum Updated to NodeBB v4.3 + New Features

Passing variable-length args in container for QMetaObject::invokeMethod

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 4 Posters 988 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • F Offline
    F Offline
    flatland3r
    wrote on last edited by flatland3r
    #1

    I have a use case where I'm using QMetaObject::invokeMethod to call any of a variety of slot functions within my application which require various numbers of function parameters. What I'd like to do is as follows:

    QList<QMetaMethodArgument> args;
    for (int i = 0; i < num_args_required; i++){
      args.append(arg);
    }
    QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,args);
    

    This doesn't work because I suspect that invokeMethod is treating the container itself as the arg, thus not matching any of my target function definitions. Indeed, no errors occur but my functions do not get called.

    Is there a way to do this? If not, I'll have to use a cumbersome, pyramid-like case statement similar to this to pass them discretely:

    switch(num_args_required){
    case 1:
      QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1); break;
    case 2:
      QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2); break;
    case 3:
      QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2,arg3); break; 
    // etc... up to 10 params
    }
    

    Should also mention that the way this was implemented in Qt5.15 was that, because 10 params was the maximum amount, I just always passed in 10 params with the unused ones being set to empty QGenericArgument objects. Doing this in Qt6.5 with QMetaMethodArgument seg faults.

    SGaistS kshegunovK TomZT 3 Replies Last reply
    0
    • F flatland3r

      I have a use case where I'm using QMetaObject::invokeMethod to call any of a variety of slot functions within my application which require various numbers of function parameters. What I'd like to do is as follows:

      QList<QMetaMethodArgument> args;
      for (int i = 0; i < num_args_required; i++){
        args.append(arg);
      }
      QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,args);
      

      This doesn't work because I suspect that invokeMethod is treating the container itself as the arg, thus not matching any of my target function definitions. Indeed, no errors occur but my functions do not get called.

      Is there a way to do this? If not, I'll have to use a cumbersome, pyramid-like case statement similar to this to pass them discretely:

      switch(num_args_required){
      case 1:
        QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1); break;
      case 2:
        QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2); break;
      case 3:
        QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2,arg3); break; 
      // etc... up to 10 params
      }
      

      Should also mention that the way this was implemented in Qt5.15 was that, because 10 params was the maximum amount, I just always passed in 10 params with the unused ones being set to empty QGenericArgument objects. Doing this in Qt6.5 with QMetaMethodArgument seg faults.

      SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      You might be able to do something using tuples and some template black magic but if you really have the need of ten or more parameters, you really should consider using a dedicated struct or class to pass them to your slot.

      Note: I insiste highly on might and I would not recommend it.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      F 1 Reply Last reply
      1
      • SGaistS SGaist

        Hi and welcome to devnet,

        You might be able to do something using tuples and some template black magic but if you really have the need of ten or more parameters, you really should consider using a dedicated struct or class to pass them to your slot.

        Note: I insiste highly on might and I would not recommend it.

        F Offline
        F Offline
        flatland3r
        wrote on last edited by
        #3

        @SGaist Thanks, I appreciate the response.

        Not sure offhand whether there's enough commonality between all the slots in my application to make an unbloated struct, but I agree that's a promising way to circumvent the problem. Bummer that I can't just use a container object directly.

        1 Reply Last reply
        0
        • F flatland3r

          I have a use case where I'm using QMetaObject::invokeMethod to call any of a variety of slot functions within my application which require various numbers of function parameters. What I'd like to do is as follows:

          QList<QMetaMethodArgument> args;
          for (int i = 0; i < num_args_required; i++){
            args.append(arg);
          }
          QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,args);
          

          This doesn't work because I suspect that invokeMethod is treating the container itself as the arg, thus not matching any of my target function definitions. Indeed, no errors occur but my functions do not get called.

          Is there a way to do this? If not, I'll have to use a cumbersome, pyramid-like case statement similar to this to pass them discretely:

          switch(num_args_required){
          case 1:
            QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1); break;
          case 2:
            QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2); break;
          case 3:
            QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2,arg3); break; 
          // etc... up to 10 params
          }
          

          Should also mention that the way this was implemented in Qt5.15 was that, because 10 params was the maximum amount, I just always passed in 10 params with the unused ones being set to empty QGenericArgument objects. Doing this in Qt6.5 with QMetaMethodArgument seg faults.

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by
          #4

          I wasn't quite able to follow what exactly is required, but is it this?

          template <typename ... Args>
          auto methodFor(QObject * target, const char * action)
          {
              return [context = QPointer<QObject>(target), slot = QByteArray(action)] (Args && ... args) -> void {
                  if (!context)
                      return;
                  QMetaObject::invokeMethod(context.data(), slot.data(), Qt::QueuedConnection, QArgument<Args>(QMetaType::fromType<Args>().name(), std::forward<Args>(args))...);
              };
          }
          

          Usage (basically wraps the call, the name of the slot and the object in a free function):

          auto noArgs = methodFor(this, "someMethod");
          auto twoArgs = methodFor<int, float>(this, "anotherMethod");
          

          Then called like:

          if (foo)
              noArgs();
          else 
              twoArgs(0, 1.2f);
          

          Read and abide by the Qt Code of Conduct

          F 1 Reply Last reply
          0
          • kshegunovK kshegunov

            I wasn't quite able to follow what exactly is required, but is it this?

            template <typename ... Args>
            auto methodFor(QObject * target, const char * action)
            {
                return [context = QPointer<QObject>(target), slot = QByteArray(action)] (Args && ... args) -> void {
                    if (!context)
                        return;
                    QMetaObject::invokeMethod(context.data(), slot.data(), Qt::QueuedConnection, QArgument<Args>(QMetaType::fromType<Args>().name(), std::forward<Args>(args))...);
                };
            }
            

            Usage (basically wraps the call, the name of the slot and the object in a free function):

            auto noArgs = methodFor(this, "someMethod");
            auto twoArgs = methodFor<int, float>(this, "anotherMethod");
            

            Then called like:

            if (foo)
                noArgs();
            else 
                twoArgs(0, 1.2f);
            
            F Offline
            F Offline
            flatland3r
            wrote on last edited by
            #5

            @kshegunov Your response answers my question of whether or not that it's technically possible to pass a variable number of args to a single invokeMethod() call (it is). However, your solution doesn't avoid the problem of a pyramid case statement and adds bloat for the template method abstraction:

            auto oneArgs = methodFor<int>(this, "oneArgSlot");
            auto twoArgs = methodFor<int, int>(this, "twoArgsSlot");
            auto threeArgs = methodFor<int, int, int>(this, "threeArgsSlot");
            // ...and more
            

            then:

            switch(num_args_required){
            case 1:
                oneArgs(arg1); break;
            case 2:
                twoArgs(arg1,arg2); break;
            case 3:
                threeArgs(arg1,arg2,arg3); break;
            // ...and more
            }
            
            kshegunovK 1 Reply Last reply
            0
            • F flatland3r

              @kshegunov Your response answers my question of whether or not that it's technically possible to pass a variable number of args to a single invokeMethod() call (it is). However, your solution doesn't avoid the problem of a pyramid case statement and adds bloat for the template method abstraction:

              auto oneArgs = methodFor<int>(this, "oneArgSlot");
              auto twoArgs = methodFor<int, int>(this, "twoArgsSlot");
              auto threeArgs = methodFor<int, int, int>(this, "threeArgsSlot");
              // ...and more
              

              then:

              switch(num_args_required){
              case 1:
                  oneArgs(arg1); break;
              case 2:
                  twoArgs(arg1,arg2); break;
              case 3:
                  threeArgs(arg1,arg2,arg3); break;
              // ...and more
              }
              
              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #6

              @flatland3r said in Passing variable-length args in container for QMetaObject::invokeMethod:

              However, your solution doesn't avoid the problem of a pyramid case statement

              Let me try to answer this with a question. It's all fine and cool when we use pseudo code, but how num_args_required is determined? Is it supposed to be deduced by a size of an array/list? What if the array doesn't contain the correct types (e.g. QVariantList)? You do realize Qt is not a language, and C++ is statically typed, right?

              If you can break down the problem, to something that is concrete, maybe I could be able to help. At least to me, it seems you're describing how va_start/va_arg/va_end operate.

              and adds bloat for the template method abstraction:

              In this particular case it's completely irrelevant, because

              1. Bloat is a relative term, QMetaObject::invokeMethod is already a bloat and expanding a single template function isn't really very bloat-y.
              2. It's compile-time bloat, which even if annoying is irrelevant (with exceptions).

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              0
              • JonBJ JonB referenced this topic on
              • F flatland3r

                I have a use case where I'm using QMetaObject::invokeMethod to call any of a variety of slot functions within my application which require various numbers of function parameters. What I'd like to do is as follows:

                QList<QMetaMethodArgument> args;
                for (int i = 0; i < num_args_required; i++){
                  args.append(arg);
                }
                QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,args);
                

                This doesn't work because I suspect that invokeMethod is treating the container itself as the arg, thus not matching any of my target function definitions. Indeed, no errors occur but my functions do not get called.

                Is there a way to do this? If not, I'll have to use a cumbersome, pyramid-like case statement similar to this to pass them discretely:

                switch(num_args_required){
                case 1:
                  QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1); break;
                case 2:
                  QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2); break;
                case 3:
                  QMetaObject::invokeMethod(target,action,Qt::QueuedConnection,arg1,arg2,arg3); break; 
                // etc... up to 10 params
                }
                

                Should also mention that the way this was implemented in Qt5.15 was that, because 10 params was the maximum amount, I just always passed in 10 params with the unused ones being set to empty QGenericArgument objects. Doing this in Qt6.5 with QMetaMethodArgument seg faults.

                TomZT Offline
                TomZT Offline
                TomZ
                wrote on last edited by
                #7

                @flatland3r what worked for me is code listed on https://forum.qt.io/topic/145618/qmetamethod-how-to-use-after-the-changes

                basically always passing in 10 arguments and Qt would ignore the ones that were unneeded.

                I don't know how to do this anymore since the QMeta stuff has replaced the QGeneric* classes. Which I filed a bugreport about on QTBUG-114362

                TomZT 1 Reply Last reply
                0
                • TomZT TomZ

                  @flatland3r what worked for me is code listed on https://forum.qt.io/topic/145618/qmetamethod-how-to-use-after-the-changes

                  basically always passing in 10 arguments and Qt would ignore the ones that were unneeded.

                  I don't know how to do this anymore since the QMeta stuff has replaced the QGeneric* classes. Which I filed a bugreport about on QTBUG-114362

                  TomZT Offline
                  TomZT Offline
                  TomZ
                  wrote on last edited by TomZ
                  #8

                  ok, I know how to do this but its really really ugly.

                  It essentially is 2 sets of 10 calls to invoke in my sourcecode and some logic to decide which of the invoke() methods I actually call.

                  I have 10 calls based on the amount of arguments there are. So, Qt is awesome it deprecated the methods only to force me to write 10 of them..

                  Then the invoke returns false if you pass in a default-constructed QMetaMethodReturnArgument which indicates 'void'.
                  No, you have to call it without that argument if the method returns void. So... Yeah, another set of 10 calls..

                  I'm hoping I missed something, because this is beneath Qt

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved