我正在寻找一种通用方式来调用std ::函数,该函数使用qvariantlist的参数。此版本有效,但有缺点,必须指定模板参数:
template <typename... T>
struct VariantFunc {
static void Invoke(std::function<void(T...)> f, const QVariantList& args)
{
InvokeHelper(f, args);
}
private:
template <typename T1>
static void InvokeHelper(std::function<void(T1)> f, const QVariantList& args)
{
f(args.at(0).value<T1>());
}
template <typename T1, typename T2>
static void InvokeHelper(std::function<void(T1, T2)> f, const QVariantList& args)
{
f(args.at(0).value<T1>(), args.at(1).value<T2>());
}
template <typename T1, typename T2, typename T3>
static void InvokeHelper(std::function<void(T1, T2, T3)> f, const QVariantList& args)
{
f(args.at(0).value<T1>(), args.at(1).value<T2>(), args.at(2).value<T3>());
}
};
auto args = QVariantList() << 100 << QString("hello") << QJsonValue(1234);
auto f = [](int i, QString s, QJsonValue j) { qDebug() << i << s << j; };
VariantFunc<int, QString, QJsonValue>::Invoke(f, args);
我想拥有这样的实现:
struct VariantFunc2 {
template<typename Func>
static void Invoke(Func f, const QVariantList& args)
{
// ???
}
};
auto args = QVariantList() << 100 << QString("hello") << QJsonValue(1234);
auto f = [](int i, QString s, QJsonValue j) { qDebug() << i << s << j; };
VariantFunc2::Invoke(f, args);
有没有办法在C 11中执行此操作?显然
这是C 14实现。它不使用std::function
,以避免与上述类相关的开销。相反,它使用可可的完美转发。此外,Invoke
函数还返回调用可可的结果。
template <typename F>
struct function_traits;
template <typename R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)> {};
template <typename R, class... Args>
struct function_traits<R(Args...)> {};
template <typename C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&, Args...)>
{
using args = std::tuple<Args...>;
};
template <typename F, typename Args, std::size_t ... I>
auto Invoke_impl(F&& f, const QVariantList& args, std::index_sequence<I ...>)
{
return f(args.at(I).value<std::remove_const_t<std::remove_reference_t<std::tuple_element_t<I, Args>>>>() ...);
}
template <typename Func>
auto Invoke(Func&& f, const QVariantList& args)
{
using F = typename std::remove_reference<Func>::type;
using Args = typename function_traits<decltype(&F::operator())>::args;
using Indices = std::make_index_sequence<std::tuple_size<Args>::value>;
return Invoke_impl<Func, Args>(std::forward<Func>(f), args, Indices{});
}
int main()
{
QString msg = "From Lambda";
auto f = [msg](int i, const QString& s, const QJsonValue& j)
{
qDebug() << msg << i << s << j;
return 5;
};
auto args = QVariantList() << 11 << "hello" << QJsonValue(123.456);
qDebug() << Invoke(f, args);
}
将其包裹在一堂课上,这使它变得更加困难。您希望包装T...
是Invoke
的模板参数
namespace detail {
template <typename... T, std::size_t... I>
void invoke_helper(std::function<void(T...)> f, const QVariantList& args, std::index_sequence<I...>)
{
f(args.at(I).value<T>()...);
}
template <typename... T>
void deduce_invoke(std::function<void(T...)> f, const QVariantList& args)
{
std::index_sequence_for<T...> idxs;
invoke_helper<T...>(std::move(f), args, idxs);
}
}
template <typename Func>
void Invoke(Func&& f, const QVariantList& args)
{
detail::deduce_invoke(std::function{f}, args);
}
这将针对明确的情况推断T...
。
这与std::apply
共享比std::invoke
更具相似性,因此我认为Apply
是一个更好的名称。
关键是函子的正确类型扣除。
namespace FunctorHelper {
template <typename R, typename... Args>
struct VariantFunc {
static R invoke(const std::function<R(Args...)>& f, const QVariantList& args)
{
std::index_sequence_for<Args...> idxs;
return invoke_helper(f, args, idxs);
}
private:
template <std::size_t... I>
static R invoke_helper(const std::function<R(Args...)>& f, const QVariantList& args, std::index_sequence<I...>)
{
return f(args.at(I).value<std::remove_const<std::remove_reference<Args>::type>::type>()...);
}
};
template <typename F>
struct function_traits;
// function pointer
template <typename R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};
template <typename R, class... Args>
struct function_traits<R(Args...)>
{};
// const member function pointer
template <typename C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&, Args...)>
{
static constexpr std::size_t arity = sizeof...(Args);
static R deduce_invoke(const std::function<R(Args...)>& f, const QVariantList& args)
{
return VariantFunc<R, Args...>::invoke(f, args);
}
};
} // namespace
template <typename Func>
void Invoke(Func f, const QVariantList& args)
{
using call_type = FunctorHelper::function_traits<decltype(&Func::operator())>;
call_type::deduce_invoke(f, args);
}
void main()
{
QString msg = "From Lambda";
auto f = [msg](int i, const QString& s, const QJsonValue& j)
{
qDebug() << msg << i << s << j;
};
auto args = QVariantList() << 11 << "hello" << QJsonValue(123.456);
Invoke(f, args);
// "From Lambda" 11 "hello" QJsonValue(double, 123.456)
}