From: Manuel Nickschas Date: Wed, 7 Nov 2018 23:09:33 +0000 (+0100) Subject: funchelpers: Provide overload for invoking a member function X-Git-Tag: test-travis-01~97 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=353acdfb3bc68ba15e94061d8942c90b1c61ed61;ds=sidebyside funchelpers: Provide overload for invoking a member function 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. --- diff --git a/src/common/funchelpers.h b/src/common/funchelpers.h index ede4e43e..f1524ddb 100644 --- a/src/common/funchelpers.h +++ b/src/common/funchelpers.h @@ -140,3 +140,29 @@ boost::optional invokeWithArgsList(const Callable& c, const QVariantLi } return detail::invokeWithArgsList(c, args, std::make_index_sequence{}); } + +/** + * 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 +boost::optional 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(args)...); + }, args, std::make_index_sequence{}); +} diff --git a/tests/common/funchelperstest.cpp b/tests/common/funchelperstest.cpp index c40cadef..f28a1640 100644 --- a/tests/common/funchelperstest.cpp +++ b/tests/common/funchelperstest.cpp @@ -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()); - 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); } }