I'm looking for a generic way to call a std::function with arguments from a QVariantList. This Version works but has the drawback the template parameters must be specified:
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);
I'd like to have such an implementation:
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);
Is there a way to do this in C++11?
Obviously the deduction guides for std::function provides a solution, but not with C++11.
Here is a C++14 implementation. It does not use std::function, to avoid the overhead associated with said class. Instead it uses perfect forwarding of the callable. Additionally, the Invoke function returns the result of invoking the callable.
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);
}
Wrapping it in a class just makes this harder. You want the pack T... to be a template parameter of 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);
}
This will deduce T... for unambiguous cases.
This shares more similarity with std::apply than with std::invoke, so I think Apply would be a better name.
The Key is the correct type deduction from functor.
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)
}
Related
I have a class memoize which deducts the variadic argument list of a function pointer passed to it as a template parameter (see code below).
I would like to be able to do the same thing for a lambda, but I don't seem to find the right way to do it. Ideally it should work when uncommenting and completing the last line of the main() function below (or see on https://godbolt.org/z/Y8G47h4GK).
#include <tuple>
#include <iostream>
template<typename... Ts> struct pack { };
template <class> struct pack_helper;
template <class R, class... Args>
struct pack_helper<R(Args...)> {
using args = pack<Args...>;
};
template <class R, class... Args>
struct pack_helper<R(*)(Args...)> {
using args = pack<Args...>;
};
template <class F, class = typename pack_helper<F>::args>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = std::invoke_result_t<F, Args...>;
memoize(F &&f) : _f(std::forward<F>(f)) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
// auto l_mem = memoize<?>(?);
}
The following should do the trick. Note that the pack_helper logic has been modified, even for function pointers.
template<typename... Ts> struct pack { };
// helper functions to retrieve arguments for member function pointers
template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...));
template<class Result, class Type, class ...Args>
pack<Args...> lambda_pack_helper(Result(Type::*) (Args...) const);
// helper functions: if called with an instance of the first parameter type of memorize, the return type is the one used as the second one
// overload for function
template<class Result, class ...Args>
pack<Args...> pack_helper(Result(Args...));
// overload for function pointer
template<class Result, class ...Args>
pack<Args...> pack_helper(Result (*)(Args...));
// sfinae will prevent this overload from being considered for anything not providing an operator()
// for those types we use the return type of helper function lambda_pack_helper
template<class T>
decltype(lambda_pack_helper(&T::operator())) pack_helper(T);
template <class F, class = decltype(pack_helper(std::declval<F>()))>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = std::invoke_result_t<F, Args...>;
memoize(F&& f) : _f(std::move(f)) {}
memoize(F const& f) : _f(f) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
auto l_mem = memoize<decltype(l)>(l);
std::cout << l_mem(3) << '\n';
}
I found another way (see below) but I think fabian's answer above is better!
#include <iostream>
template<typename... Ts> struct pack { };
template <class> struct pack_helper;
template <class R, class... Args>
struct pack_helper<R(Args...)> {
using args = pack<Args...>;
};
template <class R, class... Args>
struct pack_helper<R(*)(Args...)> {
using args = pack<Args...>;
};
// ---- for lambdas
template <class T>
struct pack_helper : public pack_helper<decltype(&T::operator())>
{};
template <class LambdaClass, class R, class... Args>
struct pack_helper<R(LambdaClass::*)(Args...) const> {
using args = pack<Args...>;
};
template <class F, class = typename pack_helper<F>::args>
class memoize;
template <class F, class... Args>
class memoize<F, pack<Args...>>
{
public:
using result_type = decltype(std::declval<F>()(std::declval<Args>()...));
memoize(F&& f) : _f(std::forward<F>(f)) {}
memoize(F const& f) : _f(f) {}
result_type operator()(Args... args) { return _f(args...); }
private:
F _f;
};
int test(int x) { return x + 1; }
int main() {
auto test_mem = memoize<decltype(&test)>(&test);
std::cout << test_mem(2) << '\n';
auto l = [](int x) -> int { return x + 1; };
auto l_mem = memoize<decltype(l)>(l);
std::cout << l_mem(3) << '\n';
}
What is the problem with this?
struct foo {
void process(int, char, bool) {}
};
foo myfoo;
template <typename Method> struct thing {
void doit() {
Method m = Method{};
(myfoo.*m)(5, 'a', true);
}
};
int main() {
thing<decltype(&foo::process)> t;
t.doit();
}
I think this isolates the problem. What is the workaround if I have to use the type Method, as in the case of my original post below?
Original post:
In the following attempted test:
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
As you can predict, func is supposed to carry out
foo.play('c', true); bar.jump(5, 2, 4.5); baz.run(6.8);
My implementation of the Functor class so far (ignoring perfect forwarding and such for now) is
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
where my last line using std::invoke is just to test the concept before I continue. It however will not compile on either GCC 7.2 or clang 6.0, so I cannot continue with the generalization. Any workaround here, or a completely different implementation altogether?
Here is everything I have so far:
namespace utilities {
template <std::size_t N, typename... Ts>
struct nth_element : std::tuple_element<N, std::tuple<Ts...>> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
}
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs, typename... Members> struct many_members_h;
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs>
struct many_members_h<Rs, Ts, ArgsPacks, AllArgs> {
using return_types = Rs;
using classes = Ts;
using args_packs = ArgsPacks;
using all_args = AllArgs;
};
template <typename... Rs, typename... Ts, typename... ArgsPacks, typename... AllArgs, typename R, typename T, typename... Args, typename... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Ts...>, std::tuple<ArgsPacks...>, std::tuple<AllArgs...>, R(T::*)(Args...), Rest...> :
many_members_h<std::tuple<Rs..., R>, std::tuple<Ts..., T>, std::tuple<ArgsPacks..., std::tuple<Args...>>, std::tuple<AllArgs..., Args...>, Rest...> { };
template <typename... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
// Testing
#include <iostream>
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
Taking your smaller first example, note that decltype(&foo::process) is the type called void (foo::*)(int, char, bool).
This type does not contain or imply any association with the original function foo::process itself. Just like the type int doesn't let you get the value of some particular int elsewhere in your program, or the type SomeClass doesn't let you refer to a SomeClass object elsewhere in your program, the type alone doesn't carry a value or identity.
The expression Method{} value-initializes this pointer to member type. Which means the resulting value is a null pointer value. Which means calling it is undefined behavior (and on many systems is likely to result in a segfault).
If you're using C++17 mode, you could use a template <auto Method> non-type parameter and simply pass &foo::process (without using decltype) as the template argument. Some SFINAE techniques could enforce that the argument is actually a pointer to member function, and some helper traits could be used to get the class type and parameter list tuple.
Or if you're using a standard earlier than C++17, you'll have to either make the function pointer a function argument, or make it a template parameter which follows the type, as in template <typename MethodType, MethodType Method>, then call as thing<decltype(&foo::process), &foo::process>.
Thanks to aschepler's answer and advice to use auto... instead of typename... for the member function pointers, I was able to carry the original goal:
#include <tuple>
#include <functional> // std::invoke
#include <type_traits>
#include <utility>
namespace utilities {
template <std::size_t N, auto I, auto... Is>
struct nth_element : nth_element<N - 1, Is...> { };
template <auto I, auto... Is>
struct nth_element<0, I, Is...> {
static constexpr decltype(I) value = I;
};
template <std::size_t N, typename Pack> struct nth_index;
template <std::size_t N, std::size_t... Is>
struct nth_index<N, std::index_sequence<Is...>> : nth_element<N, Is...> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
template <typename F, typename T, typename Tuple, std::size_t... Is>
decltype(auto) invoke_with_tuple_h (F&& f, T&& t, Tuple&& tuple, std::index_sequence<Is...>&&) {
return std::invoke(std::forward<F>(f), std::forward<T>(t), std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename T, typename Tuple>
decltype(auto) invoke_with_tuple (F&& f, T&& t, Tuple&& tuple) {
return invoke_with_tuple_h (std::forward<F>(f), std::forward<T>(t), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
template <typename PartialSums, std::size_t Sum, std::size_t... Is> struct all_partial_sums_h;
template <std::size_t... PartialSums, std::size_t Sum>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum> {
using type = std::index_sequence<PartialSums..., Sum>;
using type_without_last_sum = std::index_sequence<PartialSums...>; // We define this because this is what we need actually.
};
template <std::size_t... PartialSums, std::size_t Sum, std::size_t First, std::size_t... Rest>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum, First, Rest...> :
all_partial_sums_h<std::index_sequence<PartialSums..., Sum>, Sum + First, Rest...> { };
template <typename Pack> struct all_partial_sums;
template <std::size_t... Is>
struct all_partial_sums<std::index_sequence<Is...>> : all_partial_sums_h<std::index_sequence<>, 0, Is...> { };
template <typename Pack> struct pack_size;
template <template <typename...> class P, typename... Ts>
struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
template <typename PackOfPacks> struct get_pack_sizes;
template <template <typename...> class P, typename... Packs>
struct get_pack_sizes<P<Packs...>> {
using type = std::index_sequence<pack_size<Packs>::value...>;
};
}
template <typename Method> struct method_traits;
template <typename R, typename C, typename... Args>
struct method_traits<R(C::*)(Args...)> {
using return_type = R;
using class_type = C;
using args_type = std::tuple<Args...>;
};
template <typename Rs, typename Cs, typename ArgsPacks, auto... Members> struct many_members_h;
template <typename Rs, typename Cs, typename ArgsPacks>
struct many_members_h<Rs, Cs, ArgsPacks> {
using return_types = Rs;
using classes = Cs;
using args_packs = ArgsPacks;
};
template <typename... Rs, typename... Cs, typename... ArgsPacks, auto F, auto... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Cs...>, std::tuple<ArgsPacks...>, F, Rest...> :
many_members_h<std::tuple<Rs..., typename method_traits<decltype(F)>::return_type>, std::tuple<Cs..., typename method_traits<decltype(F)>::class_type>, std::tuple<ArgsPacks..., typename method_traits<decltype(F)>::args_type>, Rest...> { };
template <auto... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <auto... Members>
struct Functor {
using m = many_members<Members...>;
using starting_points = typename utilities::all_partial_sums<typename utilities::get_pack_sizes<typename m::args_packs>::type>::type;
template <typename... Args>
typename m::return_types operator()(Args&&... args) const {
constexpr std::size_t M = sizeof...(Members);
auto t = std::make_tuple(std::forward<Args>(args)...);
auto objects = utilities::tuple_head<M>(t);
auto arguments = utilities::extract_subtuple<M, sizeof...(Args) - M>(t);
return call(objects, arguments, std::make_index_sequence<M>{});
}
private:
template <typename Tuple1, typename Tuple2, std::size_t... Is>
auto call (Tuple1& objects, const Tuple2& args, std::index_sequence<Is...>&&) const { // perfect forwarding to do later
return std::make_tuple(call_helper<Is>(objects, args)...);
}
template <std::size_t N, typename Tuple1, typename Tuple2>
auto call_helper (Tuple1& objects, const Tuple2& args) const { // perfect forwarding to do later
constexpr std::size_t s = std::tuple_size_v<std::tuple_element_t<N, typename m::args_packs>>;;
constexpr std::size_t a = utilities::nth_index<N, starting_points>::value;
const auto args_tuple = utilities::extract_subtuple<a, s>(args);
return utilities::invoke_with_tuple (utilities::nth_element<N, Members...>::value, std::get<N>(objects), args_tuple);
}
};
// Testing
#include <iostream>
struct Foo { int play (char c, bool b) { std::cout << std::boolalpha << "Foo::play(" << c << ", " << b << ") called.\n"; return 3; } };
struct Bar { double jump (int a, short b, float c) { std::cout << "Bar::jump(" << a << ", " << b << ", " << c << ") called.\n"; return 5.8; } };
struct Baz { char run (double d) { std::cout << "Baz::run(" << d << ") called.\n"; return 'b'; } };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<&Foo::play, &Bar::jump, &Baz::run> func;
const auto tuple = func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
std::cin.get();
}
Output:
Baz::run(6.8) called.
Bar::jump(5, 2, 4.5) called.
Foo::play(c, true) called.
Is there any way in c++11/14 to write variadic template function like this:
template <typename ReturnType, typename Args...>
std::function<ReturnType()> bindArgumentsFromTuple
(std::function<ReturnType(Args... args)> f,
const std::tuple<Args...>& t)
that binds elements of tuple t to function f as arguments (such that the number of elements and its types are identical in function arguments and tuple)?
Example of usage:
void dummy_print(int i, double d)
{
std::cout << i << "," << d << '\n';
}
void dummy_print2(int i, double d, std::string& s)
{
std::cout << i << "," << d << "," << s << '\n';
}
int main() {
std::tuple<int,double> t(77,1.1);
std::tuple<int,double,std::string> t2(1,2.0,"aaa");
auto f1 = bindArgumentsFromTuple(dummy_print, t);
f1();
auto f2 = bindArgumentsFromTuple(dummy_print2, t2);
f2();
return 0;
}
template <typename F, typename T, std::size_t... indices>
auto bindTuple(F&& f, T&& t, std::index_sequence<indices...>) {
return std::bind(std::forward<F>(f), std::get<indices>(std::forward<T>(t))...);
}
template <typename F, typename T>
auto bindTuple(F&& f, T&& t) {
return bindTuple(std::forward<F>(f), std::forward<T>(t),
std::make_index_sequence<std::tuple_size<std::decay_t<T>>{}>{});
}
The above will support anything tuple-esque, i.e. std::array, std::tuple, etc., while regarding placeholder arguments.
Demo.
With std::index_sequence, you may do something like:
template <typename ReturnType, typename ...Args, std::size_t ... Is>
std::function<ReturnType()>
bindArgumentsFromTuple_impl(std::function<ReturnType(Args...)> f,
const std::tuple<Args...>& t, std::index_sequence<Is...>)
{
return [=]() { return f(std::get<Is>(t)...);};
}
template <typename ReturnType, typename ...Args>
std::function<ReturnType()>
bindArgumentsFromTuple(std::function<ReturnType(Args...)> f,
const std::tuple<Args...>& t)
{
return bindArgumentsFromTuple_impl(f, t, std::index_sequence_for<Args...>{});
}
Live Demo
I want to call one variadic function from another variadic function in the following manner:
template <typename ...Ts>
void f(Ts const & ...) { /* ... */ }
template <typename ...Args>
void g(Args const & ...args)
{
// shall call f(arg0.a(), arg0.b(), arg1.a(), arg1.b(), ...)
}
I have done it the following way:
struct sentinel_t { };
template <typename Arg, typename ...Args>
void g_impl(Arg const & arg, Args const & ...args)
{
g_impl(args..., arg.a(), arg.b());
}
template <typename ...Ts>
void g_impl(sentinel_t , Ts const & ...ts)
{
f(ts...);
}
template <typename ...Args>
void g(Args const & ...args)
{
g_impl(args..., sentinel_t{});
}
Is there another/better way to implement this pattern?
template <class... Args> void g(Args const &... args) {
impl::apply([](auto const &... p) { f(p...); },
std::tuple_cat(std::forward_as_tuple(args.a(), args.b())...));
}
until std::apply will be standardized you can make your own pretty simple with c++14 (taken from the referenced paper):
namespace impl {
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple> decltype(auto) apply(F &&f, Tuple &&t) {
using Indices =
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices{});
}
}
You may do
namespace detail
{
// dispatcher
template <typename T>
decltype(auto) call_a_b(std::integral_constant<std::size_t, 0u>, const T& arg) {return arg.a();}
// dispatcher
template <typename T>
decltype(auto) call_a_b(std::integral_constant<std::size_t, 1u>, const T& arg) {return arg.b();}
template <std::size_t... Is, typename Tuple>
void call_f_a_b(std::index_sequence<Is...>, const Tuple& tuple)
{
f(call_a_b(std::integral_constant<std::size_t, Is % 2>{}, std::get<Is / 2>(tuple))...);
}
}
template <typename...Ts>
void g(const Ts&... args)
{
return detail::call_f_a_b(std::make_index_sequence<2 * sizeof...(Ts)>{}, std::tie(args...));
// call f(arg0.a(), arg0.b(), arg1.a(), arg1.b(), ...)
}
Live Demo
I have the following code :
template<size_t sz,typename T=float> class Vec{
T v[sz];
Vec(const T& val,const T&... nv){
//how do i assign `sz` number of first arguments into `this->v` array
}
}
I want to create constructor, that receive generic number of constructor argument, and assign the first sz number of arguments into member variable of v
what I want to do, is to be able doing like this: Vec<3> var(1.0,2.0,3.0);
This is possible, but complicated. Here is some code that does it. It may be possible to eliminate the holder type, but I leave that as an exercise for the reader. This has been tested with g++ 4.6.
#include <iostream>
#include <typeinfo>
template<size_t ... Indices> struct indices_holder
{};
template<size_t index_to_add,typename Indices=indices_holder<> >
struct make_indices_impl;
template<size_t index_to_add,size_t...existing_indices>
struct make_indices_impl<index_to_add,indices_holder<existing_indices...> >
{
typedef typename make_indices_impl<
index_to_add-1,
indices_holder<index_to_add-1,existing_indices...> >::type type;
};
template<size_t... existing_indices>
struct make_indices_impl<0,indices_holder<existing_indices...> >
{
typedef indices_holder<existing_indices...> type;
};
template<size_t max_index>
typename make_indices_impl<max_index>::type make_indices()
{
return typename make_indices_impl<max_index>::type();
}
template<unsigned index,typename ... U>
struct select_nth_type;
template<unsigned index,typename T,typename ... U>
struct select_nth_type<index,T,U...>
{
typedef typename select_nth_type<index-1,U...>::type type;
static type&& forward(T&&,U&&... u)
{
return select_nth_type<index-1,U...>::forward(static_cast<U&&>(u)...);
}
};
template<typename T,typename ... U>
struct select_nth_type<0,T,U...>
{
typedef T type;
static type&& forward(T&&t,U&&...)
{
return static_cast<T&&>(t);
}
};
template<unsigned index,typename ... U>
typename select_nth_type<index,U...>::type&& forward_nth(U&&... u)
{
return static_cast<typename select_nth_type<index,U...>::type&&>(
select_nth_type<index,U...>::forward(
static_cast<U&&>(u)...));
}
template<size_t sz,typename T=float> struct Vec{
struct holder
{
T data[sz];
};
holder v;
template<typename ... U>
struct assign_helper
{
template<size_t... Indices>
static holder create_array(indices_holder<Indices...>,Vec* self,U&&... u)
{
holder res={{static_cast<T>(forward_nth<Indices>(u...))...}};
return res;
}
};
template<typename ... U>
Vec(U&&... u):
v(assign_helper<U...>::create_array(make_indices<sz>(),this,static_cast<U&&>(u)...))
{}
};
int main()
{
Vec<3> v(1.2,2.3,3.4,4.5,5.6,7.8);
std::cout<<"v[0]="<<v.v.data[0]<<std::endl;
std::cout<<"v[1]="<<v.v.data[1]<<std::endl;
std::cout<<"v[2]="<<v.v.data[2]<<std::endl;
}
I believe this satisfies all the requirements:
template <size_t sz,typename T,typename... Args> struct Assign;
template <typename T,typename First,typename...Rest>
struct Assign<1,T,First,Rest...> {
static void assign(T *v,const First &first,const Rest&... args)
{
*v = first;
}
};
template <size_t sz,typename T,typename First,typename... Rest>
struct Assign<sz,T,First,Rest...> {
static void assign(T *v,const First &first,const Rest&... rest)
{
*v = first;
Assign<sz-1,T,Rest...>::assign(v+1,rest...);
}
};
template<size_t sz,typename T=float>
struct Vec{
T v[sz];
template <typename... Args>
Vec(const T& val,const Args&... nv){
Assign<sz,T,T,Args...>::assign(v,val,nv...);
}
};
This is another technique which is much simpler if it is ok not to have at least sz parameters:
template<size_t sz,typename T=float>
struct Vec {
T v[sz];
template <typename... Args>
Vec(const T& val,const Args&... nv)
{
T data[] = {val,static_cast<const T &>(nv)...};
int i=0;
for (; i<sz && i<(sizeof data)/sizeof(T); ++i) {
v[i] = data[i];
}
for (; i<sz; ++i) {
v[i] = T();
}
}
};
First declare this utility function:
template <typename T> inline void push(T* p) {}
template <typename T, typename First, typename... Args>
inline void push(T* p, First&& first, Args&&... args)
{
*p = first;
push(++p, std::forward<Args>(args)...);
}
Then, in your class:
template<size_t sz,typename T=float>
class Vec
{
T v[sz];
template <typename... Args>
Vec(T first, Args&&... args) // << we have changed const T& to T&&
{
//how do i assign `sz` number of first arguments into `this->v` array
// like this:
push(&v[0], first, std::forward<Args>(args)...);
}
}
sz is a template argument, you can directly use it in your code.
The following could work:
template <typename T, std::size_t N>
struct Foo
{
T arr[N];
template <typename ...Args> Foo(Args &&... args) : arr{std::forward<Args>(args)...} { }
};
Usage:
Foo<int, 3> a(1,2,3);
This allows you to construct the array elements from anything that's convertible to T. You can obtain the number of parameters (which can be anything not exceeding N) with sizeof...(Args).
Can you make use of something like this?
template <typename... Args> class Vec {
std :: tuple <Args...> m_args;
Vec (const Foo & a, const Bar & b, Args&&... args)
: m_args (args...)
{
// ... something with a, b
}
};
There are a few rules to constrain you:
you can only have one template... Args-style packed argument list per template class
methods which use it must have the templated arguments on the right-hand-side
You need to unpack the argument pack while keeping a count and do the necessary runtime operation in a functor. This should get you started:
template<unsigned, typename...>
struct unroll;
template<unsigned size, typename Head, typename... Tail>
struct unroll<size, Head, Tail...> {
void operator()(Head&& h, Tail&&... tail) {
// do your stuff, pass necessary arguments through the ctor of the
// struct
unroll<size - 1, Tail...>()(std::forward<Tail>(tail)...);
}
};
template<typename Head, typename... Tail>
struct unroll<1, Head, Tail...> {
void operator()(Head&& h, Tail&&... tail) {
// do your stuff the last time and do not recurse further
}
};
int main()
{
unroll<3, int, double, int>()(1, 3.0, 2);
return 0;
}
The following almost works (and for the last N arguments instead of the first, but hey). Perhaps someone can help with the compile error in the comments below:
#include <iostream>
void foo (int a, int b) {
std :: cout << "3 args: " << a << " " << b << "\n";
}
void foo (int a, int b, int c) {
std :: cout << "3 args: " << a << " " << b << " " << c << "\n";
}
template <int n, typename... Args>
struct CallFooWithout;
template <typename... Args>
struct CallFooWithout <0, Args...> {
static void call (Args... args)
{
foo (args...);
}
};
template <int N, typename T, typename... Args>
struct CallFooWithout <N, T, Args...> {
static void call (T, Args... args)
{
CallFooWithout <N-1, Args...> :: call (args...);
// ambiguous class template instantiation for 'struct CallFooWithout<0, int, int, int>'
// candidates are: struct CallFooWithout<0, Args ...>
// struct CallFooWithout<N, T, Args ...>
}
};
template <int n, typename... Args>
void call_foo_with_last (Args... args)
{
CallFooWithout <sizeof...(Args)-n, Args...> :: call (args...);
}
int main ()
{
call_foo_with_last <2> (101, 102, 103, 104, 105);
call_foo_with_last <3> (101, 102, 103, 104, 105);
}
I don't see why it's ambiguous because 0 is more specialised than N so that should satisfy the partial order ?!?!?
By contrast, the below is fine.
template <int N, typename... T>
struct Factorial
{
enum { value = N * Factorial<N - 1,T...>::value };
};
template <typename... T>
struct Factorial<0, T...>
{
enum { value = 1 };
};
void foo()
{
int x = Factorial<4,int>::value;
}
What's the difference?