Make QtConcurrent::mapped work with lambdas - c++

I am trying to use QtConcurrent::mapped into a QVector<QString>. I already tried a lot of methods, but it seems there are always problems with overloading.
QVector<QString> words = {"one", "two", "three", "four"};
using StrDouble = std::pair<QString, double>;
QFuture<StrDouble> result = QtConcurrent::mapped<StrDouble>(words, [](const QString& word) -> StrDouble {
return std::make_pair(word + word, 10);
});
This snippet returns the following error:
/home/lhahn/dev/cpp/TestLambdaConcurrent/mainwindow.cpp:23: error: no matching function for call to ‘mapped(QVector<QString>&, MainWindow::MainWindow(QWidget*)::<lambda(const QString&)>)’
});
^
I saw this post, which says that Qt cannot find the return value of the lambda, so you have to use std::bind with it. If I try this:
using StrDouble = std::pair<QString, double>;
using std::placeholders::_1;
auto map_fn = [](const QString& word) -> StrDouble {
return std::make_pair(word + word, 10.0);
};
auto wrapper_map_fn = std::bind(map_fn, _1);
QFuture<StrDouble> result = QtConcurrent::mapped<StrDouble>(words, wrapper_map_fn);
But the the error is still similar:
/home/lhahn/dev/cpp/TestLambdaConcurrent/mainwindow.cpp:28: error: no matching function for call to ‘mapped(QVector<QString>&, std::_Bind<MainWindow::MainWindow(QWidget*)::<lambda(const QString&)>(std::_Placeholder<1>)>&)’
QFuture<StrDouble> result = QtConcurrent::mapped<StrDouble>(words, wrapper_map_fn);
^
I also tried wrapping the lambda inside std::function but unfortunately similar results.
Note that this example is just for reproduction, I need a lambda because I am also capturing variables in my code.

The following compiles for me:
QVector<QString> words = {"one", "two", "three", "four"};
std::function<StrDouble(const QString& word)> func = [](const QString &word) {
return std::make_pair(word + word, 10.0);
};
QFuture<StrDouble> result = QtConcurrent::mapped(words, func);
Output of qDebug() << result.results():
(std::pair("oneone",10), std::pair("twotwo",10), std::pair("threethree",10), std::pair("fourfour",10))

Unfortunately that QtConcurrent::mapped does not support lambda function with captures. You could need a custom implementation. For example, you may make a one with AsyncFuture:
template <typename T, typename Sequence, typename Functor>
QFuture<T> mapped(Sequence input, Functor func){
auto defer = AsyncFuture::deferred<T>();
QList<QFuture<T>> futures;
auto combinator = AsyncFuture::combine();
for (int i = 0 ; i < input.size() ; i++) {
auto future = QtConcurrent::run(func, input[i]);
combinator << future;
futures << future;
}
AsyncFuture::observe(combinator.future()).subscribe([=]() {
QList<T> res;
for (int i = 0 ; i < futures.size(); i++) {
res << futures[i].result();
}
auto d = defer;
d.complete(res);
});
return defer.future();
}
Usage:
auto future = mapped<int>(input, func);
Complete Example:
https://github.com/benlau/asyncfuture/blob/master/tests/asyncfutureunittests/example.cpp#L326

QtConcurrent::map[ped] works with functor types that have the result_type member type. Thus you need to wrap the lambda in a class that provides such type. The std::function wrapper provides this, but it might have more overhead - thus we can make our own.
Taking code from How to extract lambda's Return Type and Variadic Parameters Pack back from general template<typename T>, we have:
#include <utility>
#include <type_traits>
template <class T> struct function_traits : function_traits<decltype(&T::operator())> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
// specialization for pointers to member function
using functor_type = ClassType;
using result_type = ReturnType;
using arg_tuple = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
};
template <class Callable, class... Args>
struct CallableWrapper : Callable, function_traits<Callable> {
CallableWrapper(const Callable &f) : Callable(f) {}
CallableWrapper(Callable &&f) : Callable(std::move(f)) {}
};
template <class F, std::size_t ... Is, class T>
auto wrap_impl(F &&f, std::index_sequence<Is...>, T) {
return CallableWrapper<F, typename T::result_type,
std::tuple_element_t<Is, typename T::arg_tuple>...>(std::forward<F>(f));
}
template <class F> auto wrap(F &&f) {
using traits = function_traits<F>;
return wrap_impl(std::forward<F>(f),
std::make_index_sequence<traits::arity>{}, traits{});
}
The wrapped functor, in addition to the result_type needed by Qt, also has the functor_type, arg_tuple, and arity.
Instead of passing the lambda directly, pass the wrapped functor:
auto result = QtConcurrent::mapped<StrDouble>(words, wrap([](const QString& word){
return std::make_pair(word + word, 10);
}));
The value returned by wrap is a functor that implements result_type.

Related

Transform each of parameter pack's values based on a boolean criteria

I am trying to solve this problem in C++ TMP where in i need to convert one parameter pack types into another, and then convert back the types and also values. The conversion back part is based on a boolean criteria that whether an arg in Args... was transformed or not in the first place.
Basically, i have a pack(Args...). First, i transform this (for each args[i], call a transform function). It works like this:
For each arg in Args..., just create same type in transformed_args... unless it is one of following, in that case do following conversions:
Type In Args...
Type In transformed_Args...
SomeClass
shared_ptr to SomeClass
std::vector of SomeClass
std::vector of shared_ptr to SomeClass
everything else remains the same for ex:
int remains int
std::string remains std::string
I achieve this by template specialization, of course
For the next part, i take transformed_args..., publish a class and a functor. I receive call back on this functor from(C++generated Python using Pybind, not important though). Relevant bits of that class look like this...
template<typename C, typename...transformed_args..., typename... Args>
class SomeTemplateClass
{
MethodWrapper<C,void, Args...> func;
//.....
void operator()(transformed_args... targs)
{
//....
(*func.wrapped_method_inside)(transform_back_magic(targs)...) // this is want i want to achieve.
//transform_back_magic(targs)... is a plaeholder for code that checks if type of args[i]... != type of targs[i]... and then calls a tranform_back specialization on it else just return args[i].val
}
}
targs are in transformed_args... format, but underlying C++ function they are aimed for expects Args...
template<typename... Args, typename... transformed_args, ........whatever else is needed>
transform_back_magic(....)
{
if(Args[i].type != transformed_args[i].types)
tranform_back(targs[i]...);
}
the tranform_back function template logic is specialized for different cases and all logic is in place. But how to invoke that based on this boolean criteria is hitting my TMP knowledge limits. I just got started not many weeks ago.
Here i am listing down what i have created so far.
First of all this is what i need in pseudo code
template<typename C, typename... transformed_args, typename... Args>
class SomeTemplateClass
{
MethodWrapper<C,void, Args...> func;
void operator(transformed_args... targs)
{
**//In pseudo code, this is what i need**
Args... params = CreateArgsInstanceFromTransformedArgs(targs);
(*func.wrapped_method_inside)(params...);
}
}
In my attempt to implement this, so far I have decided on creating a tuple<Args...> object by copying data from targs(with conversions where ever required)
void operator(transformed_args... targs)
{
//....
auto mytup = call1(std::tuple<args...>(), std::make_index_sequence<sizeof...(Args)>,
std::make_tuple(targs...), targs...);
// mytup can be std::tuple<Args...>(transform_back(1st_targs), transform_back(2nd_targs)....). Once available i can write some more logic to extract Args... from this tuple and pass to(*func.wrapped_method_inside)(....)
(*func.wrapped_method_inside)(ArgsExtractorFromTuple(mytup)); // this part is not implemented yet, but i think it should be possible. This is not my primary concern at the moment
}
//call1
template<typename... Args, typename... Targs, std::size_t... N>
auto call1(std::tuple<Args...> tupA, std::index_sequence<N>..., std::tuple<Targs...> tupT, Targs ..)
{
auto booltup = tuple_creator<0>(tupA, tupT, nullptr); // to create a tuple of bools
auto ret1 = std::make_tuple<Args...>(call2(booltup, targs, N)...); // targs and N are expanded together so that i get indirect access to see the corresponding type in Args...
return ret1;
}
// tuple_creator is a recursive function template with sole purpose to create a boolean tuple.
// such that std::get<0>(booltup) = true,
//if tuple_element_t<0,std::tuple<Args...>> and tuple_element_t<0,std::tuple<targs...>> are same types else false
template<size_t I, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I == sizeof...(targs)>*)
{
return std::make_tuple(std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value);
}
template<size_t I = 0, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I < sizeof...(targs)>*)
{
auto ret1 = tuple_creator<I+1>(tupA, tupT, nullptr);
if(!I)
return ret1;
auto ret2 = std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value;
return std::tuple_cat(ret1, std::make_tuple(ret2));
}
template<typename TT, typename Tuple>
auto call2(Tuple boolyup, TT t, std::size_t I)
{
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
return ret;
}
transform_back is a template that uses a bool template param and enable_if based specialization to decide whether transform an argument back or not
below are the transform_back specialization for std::vector. Similarly i have others for when T = Class etc and so on
template<bool sameTypes, typename T>
std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value &&
is_class<remove_cvref_t<typename T::value_type_element_type>>::value
&& sameTypes), T>
transform_back(T val) // it was never transfoemd in first place, return as is
{
return val;
}
template<bool sameTypes, typename T>
std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value
&& is_class<remove_cvref_t<typename T::value_type_element_type>>::value
&& !sameTypes),
typename std::vector<typename T::value_type::element_type>>
transform(T val)
{
std::vector<T::value_type::element_type> t;
for(int i = 0 ; i < val.size(); ++i)
{
typename T::value_type::element_type obj = *val[i];
t.push_back(obj);
}
return t;
}
Both these specialization are same and only differ on sameTypes boolean variable
This code currently errors out in call2 method while trying to using
std::get
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
How can you help?
1)What could be the work around to std::get issue here? Just cant figure out a way to fit in std::size_t as template arg here instead of function arg to make it work at compile time.
Other than this:
2)If you can suggest an alternative approach to implement from top level.
Args... params = CreateArgsInstanceFromTransformedArgs(targs);
That would be great. The path i took is not very convincing personally to me.
If I understand correctly, you might do something like:
template <typename> struct Tag{};
std::shared_ptr<SomeClass> transform_to(Tag<std::shared_ptr<SomeClass>>, const SomeClass& s)
{
return std::make_shared<SomeClass>(s);
}
std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<std::shared_ptr<SomeClass>>>, const std::vector<SomeClass>& v)
{
std::vector<std::shared_ptr<SomeClass>> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(std::make_shared<SomeClass>(s));
}
return res;
}
const SomeClass& transform_to(Tag<SomeClass>, const std::shared_ptr<SomeClass>& s)
{
return *s;
}
std::vector<SomeClass> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
std::vector<SomeClass> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(*s);
}
return res;
}
template <typename T>
const T& transform_to(Tag<T>, const T& t) { return t; } // No transformations
And then
std::function<void (Args...)> func;
template <typename ... transformed_args>
void operator () (transformed_args... targs) const
{
func(transform_to(Tag<Args>(), targs)...);
}
Just explaining the use case here to add some context. Consider these three methods in C++ each represented with the function pointer SomeTemplateClass::func:
void foo(vector<shared_ptr<SomeClass>>) // 1
// Args... = vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>) // 2
// Args... = vector<SomeClass>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>, vector<shared_ptr<SomeClass>>) // 3
// Args... = vector<SomeClass>, vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>, vector<shared_ptr<SomeClass>>
One instance each of SomeTemplateClass is exposed to Python via Pybind. I do these transformations so that when foo is called from Python, any arg vector<T>(in C++) is received as vector<shared_ptr<T>> in SomeTemplateClass functor. This helps in to get handle to previously created objects T that i need.
But as you can see from 3 cases for foo, foo(vector<shared_ptr<T>>) does not need to be transformed to and subsequently not need to be transformed back. The case of 'tranform_to'is easily handled with template specialization, but while transforming back, vector<shared_ptr<T>> cant be blindly converted back to vector<T>. So (transform(targs...)) needs an additional logic to transform a particular arg (or targ) only when targ[i]::type != arg[i]::type
Building on Jarod's answer, i rather need something like this where in transform_to method for vector<shared_ptr> is further divided in two possible templates
template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
return v;
}
template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<<SomeClass>
transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
std::vector<SomeClass> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(*s);
}
return res;
}

Overloading function template based on lambda arguments

I am trying to create a function where callers can pass in lambdas with a certain set of known parameters (the return type can be anything the caller wants)
Let's say the possible lambda signatures are
T (Widget*)
T (Widget2*)
I've tried a couple things but I can't get any of them to work
struct Widget2{
int count = 0;
};
class Widget {
public:
template<typename Fn, typename T = std::invoke_result_t<Fn, Widget*>>
T doIt(Fn&& fn)
{
return fn(_sibling);
}
template<typename Fn, typename T = std::invoke_result_t<Fn, Widget2*>>
T doIt(Fn&& fn)
{
return fn(_other);
}
private:
Widget* _sibling = nullptr;
Widget2* _other = nullptr;
};
I've also tried doing something like this
template<typename Fn>
auto doIt(Fn&& fn)
{
if (std::is_invocable_v<Fn, Widget*>)
return fn(_sibling);
else if (std::is_invocable_v<Fn, Widget2*>)
return fn(_other);
}
this code in a compiler: https://wandbox.org/permlink/wMAuw5XXnc0zTjlk
I'd rather avoid having to add multiple functions like doItWidget1, doItWidget2 and so on.
Is there a way I can basically overload functions based on the arguments of the incoming lambda?
You can use enable_if to sfinae away the wrong overload
template<typename Fn, std::enable_if_t<std::is_invocable_v<Fn, Widget*>, int> = 0>
auto doIt(Fn&& fn)
{
return fn(_sibling);
}
template<typename Fn, std::enable_if_t<std::is_invocable_v<Fn, Widget2*>, int> = 0>
auto doIt(Fn&& fn)
{
return fn(_other);
}
Note that you don't even need to figure out the return type T, you can just use auto to do that.
Here's a demo.

Check if function return type is the same as an STL container type value

I'm working with a struct that takes a generic function and a generic STL container, but i want to make a type check in the constructor in order to raise an error if the return type of the function is different from the constructor type: is it possible to do something like this without changing the template?
template<class Function, class Container>
struct task{
Function f;
Container& c;
task(Function func, Container& cont):f(func), c(cont){
//error if mismatch between container type and function return type
}
};
int multiply(int x){ return x*10; }
int main(){
vector<int> v;
int c=10;
auto stateless = [](float x){ return x*10;};
auto stateful = [&c](int x){ return x*c;};
task t(multiply, v); //SAME TYPE: OKAY!
task tt(stateless, v); //TYPE MISMATCH: ERROR!
return 0;
}
thank you for your help
Not sure to understand completely but... if the "generic funcion" isn't a generic-lambda or a template operator() in a class/struct... you tagged C++17 so you can use deduction guides so you can deduce the type returned from the function using std::function's deduction guides.
Something as
decltype(std::function{std::declval<Function>()})::result_type
For the value type of the container is usually available the value_type type.
So, defining a couple of using types inside the body of the struct, you can write
template <typename F, typename C>
struct task
{
using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
using vtype = typename C::value_type;
// ...
task (F func, C & cont) : f{func}, c{cont}
{ static_assert( std::is_same<rtype, vtype>{} );}
};
But observe that the static_assert() inside the constructor use only elements that aren't specific of the constructor.
This way, if you have to develop (by example) ten constructors, you have to write ten times the same static_assert() inside the ten constructors bodies.
I suggest to place the static_assert() inside the body of the struct so you have to write it only one time.
I mean
template <typename F, typename C>
struct task
{
using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
using vtype = typename C::value_type;
static_assert( std::is_same<rtype, vtype>{} );
// ...
};
The following is a full compiling example
#include <vector>
#include <functional>
template <typename F, typename C>
struct task
{
using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
using vtype = typename C::value_type;
static_assert( std::is_same<rtype, vtype>{} );
F f;
C & c;
task (F func, C & cont) : f{func}, c{cont}
{ }
};
int multiply (int x)
{ return x*10; }
int main ()
{
std::vector<int> v;
int c=10;
auto stateless = [](float x){ return x*10;};
auto stateful = [&c](int x){ return x*c;};
task t1(multiply, v); // compile
task t2(stateful, v); // compile
task t3(stateless, v); // compilation error
}
But remember: this function doen't works with generic-lambdas.
In that case I don't know how to solve the problem and I suppose isn't solvable at all without knowing the type of the input parameters.
You can use static_assert with std::is_same to check type equality at compile time.
If your lambda function always takes no parameters, you can use decltype(f())
to get the function return type, else you will need
std::result_of / std::invoke_result or a function traits implementation.
#include <type_traits>
template<class Function, class Container>
struct task{
Function f;
Container& c;
task(Function func, Container& cont):f(func), c(cont){
static_assert(
std::is_same<
decltype(f()), // type of function return value
typename Container::value_type // type of values stored in container
>::value,
"incompatible function" // error message
);
}
};
I see no way to go ahead without using any kind of helper template to determine the parameter list here!
So the following solution is still based on Is it possible to figure out the parameter type and return type of a lambda?
For having function pointers and callable classes like lambdas, it only needs an specialized template instance.
template <typename CLASS>
struct function_traits_impl
: public function_traits_impl<decltype(&CLASS::operator())>
{};
template <typename CLASS, typename RET, typename... ARGS>
struct function_traits_impl< RET(CLASS::*)(ARGS...) const>
{
using args_type = std::tuple<ARGS...>;
using ret_type = RET;
};
template <typename CALLABLE > struct function_traits: public function_traits_impl< CALLABLE >{};
template< typename RET, typename... ARGS >
struct function_traits< RET(*)(ARGS...) >
{
using args_type = std::tuple<ARGS...>;
using ret_type = RET;
};
template < typename CLASS, typename CONTAINER, typename RET, typename ... ARGS> struct task;
template< typename CLASS, typename CONTAINER, typename RET, typename ... ARGS >
struct task< CLASS, CONTAINER, RET, std::tuple<ARGS...> >
{
using FUNC = std::function< RET(ARGS...)>;
FUNC func;
CONTAINER cont;
task( FUNC _func, CONTAINER& _cont): func{_func}, cont{_cont}
{
static_assert(
std::is_same<
//decltype( func( std::declval<PARMS>()...) ), // but is already known from given template parms!
RET,
typename CONTAINER::value_type
>::value,
"wrong return type, did not match with container type"
);
}
};
template <typename FUNC, typename CONTAINER >
task(FUNC, CONTAINER) -> task< FUNC, CONTAINER, typename function_traits<FUNC>::ret_type, typename function_traits<FUNC>::args_type>;
int Any( int ) { return 0; }
float WrongAny( int, int ) { return 1.1; }
int main()
{
std::vector<int> v;
//task t1{ [](int, int)->float { return 0; } , v}; // fails with assert as expected
task t2{ [](int, int)->int { return 0; } , v}; //Works!
task t3{ &Any , v}; // Works
//task t4{ &WrongAny, v }; fails as expected
}
This solution simply uses user defined deduction guide to forward the found parms from the trait which is helpful as you also use c++17.
Hint:
Generic lambdas cant be used, because if the parameters to call the lambda are unknown, how you could determine the parameters "automatically". It is quite easy to specify the parameters with the call and get the return type, but passing an generic lambda or an object with overloaded call operator needs to specify which of the functions/methods are should be used. So if you need generic lambdas or overloaded methods in class objects simply specify params manually! There can't be a trick in any language which allows you to give a set of optional calls and determine automatically which call should be used if no other information is available. As said: If params for the call are present, simply use them!
Remark:
If you use this solution, you only get a single template instance for all calls with same parameter set to the function call which may save some memory ;) But it uses a std::function to store teh callable which takes some runtime... You have now two solutions which differs in the results but both are usable ;)

hana::tuple to auto && ... args

Is there a way to use something like :
constexpr auto foo = hana::make_tuple(hana::type_c<Foo1>,hana::type_c<Foo2>);
with something like:
template < typename ... Ts >
struct Final {
constexpr Final(Ts && ... args) {}
};
hana::unpack(foo, [] (auto && ... args) { return Final(args...); });
Because with that code, unpack can't deduce lambda/function type.
Basically I want to create a type which takes a list of arguments but I have a tuple which contains the arguments.
The problem is in your lambda:
[](auto && ... args){ return Final(args...); }
// ~~~~~~~
Final isn't a type, it's a class template. As such, you need to explicitly provide the types. Something like:
[](auto&&... args){ return Final<decltype(args)...>(
std::forward<decltype(args)>(args)...); }
In C++17, with template deduction for class template parameters, the Ts&& does not function as a forwarding reference (see related answer), so the implicit deduction guide would not match your usage anyway as you are only providing lvalues and the guide requires revalues. But this would work:
[](auto... args){ return Final(std::move(args)...); }
If I understand your question correctly, what you're actually looking for is
template <typename ...Ts>
struct Final { ... };
constexpr auto foo = hana::make_tuple(hana::type_c<Foo1>,hana::type_c<Foo2>);
auto final_type = hana::unpack(foo, [](auto ...args) {
return Final<typename decltype(args)::type...>;
});
// now, final_type is a hana::type<Final<Foo1, Foo2>>
You can also achieve the same thing using hana::template_:
constexpr auto foo = hana::make_tuple(hana::type_c<Foo1>,hana::type_c<Foo2>);
auto final_type = hana::unpack(foo, hana::template_<Final>);
The problem I see with Barry's answer is that you'll end up creating a Final<decltype(hana::type_c<Foo1>), decltype(hana::type_c<Foo2>)>, which is probably not what you want.

getting template type from lambda auto

I'm in a bit of a pickle, following up my previous question and using a code similar to the one I posted here.
I use a variadic template function which accepts variadic objects
It packs them into a tuple
Iterates them using the visitor idiom
Binds for each object a callback
Instead of the original minified example shown below:
template <typename... Args>
void make_classes(Args... args)
{
auto t = std::tuple<Args...>(args...);
unsigned int size = std::tuple_size<decltype(t)>::value;
auto execute = [](auto & obj){ obj.operator()(); };
for (int i = 0; i < size; i++) {
visit_at(t, i, execute);
}
}
I am trying to understand how I can deduce the template type of the auto lambda, so that I can bind it:
template <typename... Args>
void make_classes(Args... args)
{
auto t = std::tuple<Args...>(args...);
unsigned int size = std::tuple_size<decltype(t)>::value;
auto execute = [](auto & obj){
// obtain type of obj as T?
auto callback = std::bind(&T::deserialise, obj, std::placeholders::_1);
// do something else here using this callback.
};
for (int i = 0; i < size; i++) {
visit_at(t, i, execute);
}
}
There's a catch: the parameter objects are non-copyable (although I could change that), but I would like to know if/how the above could work by deducing the template type packed in the tuple as obtained by the visitor.
If I can't deduce the type inside the lambda, can I somehow store it within the tuple (e.g.,: type & object) in order to later extract it?
Just use another lambda:
auto callback = [&obj](auto& x){
obj.deserialise(x);
};
std::bind is rarely useful. (If you really want to copy obj, you can drop the leading &.)
Moreover, you don't actually need a tuple...
template <class F, class... Args>
void for_each_arg(F&& f, Args&&... args) {
using swallow = int[];
(void)swallow{0,
(void(f(std::forward<Args>(args))), 0)...
};
}
template <typename... Args>
void make_classes(Args... args)
{
for_each_arg([](auto& obj){
auto callback = [&obj](auto& x) { obj.deserialise(x); };
// do something with callback
}, args...);
}