funchelpers: Provide overload for invoking a member function
authorManuel Nickschas <sputnick@quassel-irc.org>
Wed, 7 Nov 2018 23:09:33 +0000 (00:09 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 18 Nov 2018 10:06:43 +0000 (11:06 +0100)
To avoid having to write boilerplate code for invoking a member
function pointer on an object, provide an appropriate overload
for invokeWithArgsList().

Extend the test case accordingly.

src/common/funchelpers.h
tests/common/funchelperstest.cpp

index ede4e43..f1524dd 100644 (file)
@@ -140,3 +140,29 @@ boost::optional<QVariant> invokeWithArgsList(const Callable& c, const QVariantLi
     }
     return detail::invokeWithArgsList(c, args, std::make_index_sequence<tupleSize>{});
 }
+
+/**
+ * Invokes the given member function pointer on the given object with the arguments contained in the given variant list.
+ *
+ * The types contained in the given QVariantList are converted to the types expected by the member function.
+ * If the invocation is successful, the returned optional contains a QVariant with the return value,
+ * or an invalid QVariant if the member function returns void.
+ * If the conversion fails, or if the argument count does not match, this function returns boost::none
+ * and the member function is not invoked.
+ *
+ * @param c    Callable
+ * @param args Arguments to be given to the member function
+ * @returns An optional containing a QVariant with the return value if the member function could be invoked with
+ *          the given list of arguments; otherwise boost::none
+ */
+template<typename R, typename C, typename ...Args>
+boost::optional<QVariant> invokeWithArgsList(C* object, R(C::*func)(Args...), const QVariantList& args)
+{
+    if (sizeof...(Args) != args.size()) {
+        qWarning().nospace() << "Argument count mismatch! Expected: " << sizeof...(Args) << ", actual: " << args.size();
+        return boost::none;
+    }
+    return detail::invokeWithArgsList([object, func](Args&&... args) {
+        return (object->*func)(std::forward<decltype(args)>(args)...);
+    }, args, std::make_index_sequence<sizeof...(Args)>{});
+}
index c40cade..f28a164 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "funchelpers.h"
 
-TEST(FuncHelpersTest, invokeWithArgsList)
+TEST(FuncHelpersTest, invokeLambdaWithArgsList)
 {
     int intVal{};
     QString stringVal{};
@@ -37,8 +37,7 @@ TEST(FuncHelpersTest, invokeWithArgsList)
 
     // Good case
     {
-        QVariantList argsList{42, "Hello World"};
-        auto ret = invokeWithArgsList(callable, argsList);
+        auto ret = invokeWithArgsList(callable, {42, "Hello World"});
         ASSERT_TRUE(ret);
         EXPECT_FALSE(ret->isValid());  // Callable returns void, so the returned QVariant should be invalid
         EXPECT_EQ(42, intVal);
@@ -47,8 +46,7 @@ TEST(FuncHelpersTest, invokeWithArgsList)
 
     // Too many arguments
     {
-        QVariantList argsList{23, "Hi Universe", 2.3};
-        ASSERT_FALSE(invokeWithArgsList(callable, argsList));
+        ASSERT_FALSE(invokeWithArgsList(callable, {23, "Hi Universe", 2.3}));
         // Values shouldn't have changed
         EXPECT_EQ(42, intVal);
         EXPECT_EQ("Hello World", stringVal);
@@ -56,8 +54,7 @@ TEST(FuncHelpersTest, invokeWithArgsList)
 
     // Too few arguments
     {
-        QVariantList argsList{23};
-        ASSERT_FALSE(invokeWithArgsList(callable, argsList));
+        ASSERT_FALSE(invokeWithArgsList(callable, {23}));
         // Values shouldn't have changed
         EXPECT_EQ(42, intVal);
         EXPECT_EQ("Hello World", stringVal);
@@ -70,42 +67,83 @@ TEST(FuncHelpersTest, invokeWithArgsList)
         QVariant v{wrong};
         ASSERT_FALSE(v.canConvert<QString>());
 
-        QVariantList argsList{23, wrong};
-        ASSERT_FALSE(invokeWithArgsList(callable, argsList));
+        ASSERT_FALSE(invokeWithArgsList(callable, {23, wrong}));
         // Values shouldn't have changed
         EXPECT_EQ(42, intVal);
         EXPECT_EQ("Hello World", stringVal);
     }
 }
 
-TEST(FuncHelpersTest, invokeWithArgsListAndReturnValue)
+TEST(FuncHelpersTest, invokeLambdaWithArgsListAndReturnValue)
 {
+    int intVal{};
+    QString stringVal{};
+
+    auto callable = [&intVal, &stringVal](int i, const QString& s)
+    {
+        intVal = i;
+        stringVal = s;
+        return -i;
+    };
+
+    // Good case
+    {
+        auto ret = invokeWithArgsList(callable, {42, "Hello World"});
+        ASSERT_TRUE(ret);
+        ASSERT_TRUE(ret->isValid());
+        EXPECT_EQ(-42, *ret);
+        EXPECT_EQ(42, intVal);
+        EXPECT_EQ("Hello World", stringVal);
+    }
+
+    // Failed invocation
+    {
+        ASSERT_FALSE(invokeWithArgsList(callable, {23}));
+    }
+}
+
+class Object
+{
+public:
+    void voidFunc(int i, const QString& s)
+    {
+        intVal = i;
+        stringVal = s;
+    }
+
+    int intFunc(int i)
+    {
+        return -i;
+    }
+
+    int intVal{};
+    QString stringVal{};
+};
+
+TEST(FuncHelpersTest, invokeMemberFunction)
+{
+    Object object;
+
+    // Good case
+    {
+        auto ret = invokeWithArgsList(&object, &Object::voidFunc, {42, "Hello World"});
+        ASSERT_TRUE(ret);
+        EXPECT_FALSE(ret->isValid());  // Callable returns void, so the returned QVariant should be invalid
+        EXPECT_EQ(42, object.intVal);
+        EXPECT_EQ("Hello World", object.stringVal);
+    }
+
+    // Good case with return value
+    {
+        auto ret = invokeWithArgsList(&object, &Object::intFunc, {42});
+        ASSERT_TRUE(ret);
+        EXPECT_EQ(-42, *ret);
+    }
+
+    // Too few arguments
     {
-        int intVal{};
-        QString stringVal{};
-
-        auto callable = [&intVal, &stringVal](int i, const QString& s)
-        {
-            intVal = i;
-            stringVal = s;
-            return -i;
-        };
-
-        // Good case
-        {
-            QVariantList argsList{42, "Hello World"};
-            auto ret = invokeWithArgsList(callable, argsList);
-            ASSERT_TRUE(ret);
-            ASSERT_TRUE(ret->isValid());
-            EXPECT_EQ(-42, *ret);
-            EXPECT_EQ(42, intVal);
-            EXPECT_EQ("Hello World", stringVal);
-        }
-
-        // Failed invocation
-        {
-            QVariantList argsList{23};
-            ASSERT_FALSE(invokeWithArgsList(callable, argsList));
-        }
+        auto ret = invokeWithArgsList(&object, &Object::voidFunc, {23});
+        ASSERT_FALSE(ret);
+        EXPECT_EQ(42, object.intVal);
     }
 }