Overloading function template based on lambda arguments - c++

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.

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;
}

Calling a function with a vector of arguments

I have a std::vector of arguments and I would like to call a function with them. Is there any way to do this?
In particular the function is the mysqlx select function and the arguments are the columns I'm trying to query; they will all be of type std::string. The purpose of the function is to reduce duplication in the codebase.
(This seems like a generally useful topic, but I could not find an answer through search. If I missed it and this has already been answered, please point me to the question and close this as a duplicate, thanks.)
You can do it, up to a compile time maximum number of arguments. It isn't pretty.
using result_type = // whatever
using arg_type = // whatever
using args_type = const std::vector<arg_type> &;
using function_type = std::function<result_type(args_type)>;
template <size_t... Is>
result_type apply_vector_static(args_type args, std::index_sequence<Is...>)
{
return select(args[Is]...);
}
template<size_t N>
result_type call_apply_vector(args_type args)
{
return apply_vector_static(args, std::make_index_sequence<N>());
}
template <size_t... Is>
std::map<size_t, function_type> make_funcs(std::index_sequence<Is...>)
{
return { { Is, call_apply_vector<Is> }... };
}
result_type apply_vector(args_type args)
{
// Some maximum limit
static const auto limit = std::make_index_sequence<50>();
static const auto funcs = make_funcs(limit);
return funcs.at(args.size())(args);
}
See it live

Template variable or template typedef in a scope

Why template variable or template typedef cannot be declared inside of a scope?
I would like to write a code in c++17 like this
auto foo = [](auto fun, auto... x) {
template <typename T>
using ReturnType = std::invoke_result_t<fun, T>;
if constexpr (!(std::is_same_v<void, ReturnType<decltype(x)>> || ... ||
false)) {
return std::tuple<ReturnType<decltype(x)>...>(fun(x)...);
} else {
(fun(x), ...);
return;
}
};
This code defines a function foo which takes a function fun and bunch of arguments x.... If all of the return types fun(x) are not void then return a tuple of results. If atleast one of the return types is void then just call all the functions but return void.
In this simple example, I can of course replace ReturnType<decltype(x)> with decltype(fun(x)), but in my use case the actual type is much more complicated and the above code serves only as a motivation.
Also, I hate writing ReturnType<decltype(x)>. I would much prefer writing ReturnType(x), but that is probably not possible.
Solution I do not like: Define template typedef outside of the function as
template<typename Fun, typename T>
using ReturnType = std::invoke_result_t<Fun,T>;
and then in the function use
ReturnType<decltype(fun),delctype(x)>
Which is getting long and I have to put every local type as a template parameter.
The code is actually simpler without introducing any helpers:
if constexpr ((!std::is_void_v<decltype(fun(x))> && ...)) {
return std::tuple(fun(x)...);
} else {
(fun(x), ...);
}
&& and || have default values for empty packs (true and false, respectively), so you don't have to turn them into unary operators. And you don't need invoke_result_t since you're just directly calling. And even if you did:
using F = decltype(fun);
if constexpr ((!std::is_void_v<std::invoke_result_t<F, decltype(x))> && ...)) {
return std::tuple(std::invoke(fun, x)...);
} else {
(fun(x), ...);
}
Not much longer.
That said, I find this construct not very helpful - given that you get wildly different results for the void and non-void cases. Maybe f(x) is still an X but f(y) is void, we'd get foo(x,x) being tuple<X,X> but foo(x,y) being void? Hard to code around.
I would suggest instead of dropping all the return types, just work around the broken ones. As in:
struct Void { };
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
REQUIRES(std::is_void_v<R>)>
Void invoke_void(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
return Void{};
}
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
REQUIRES(!std::is_void_v<R>)>
R invoke_void(F&& f, Args&&... args) {
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
And now, we can always just call the function and return it:
auto foo = [](auto fun, auto... x) {
return std::tuple(invoke_void(fun, x)...);
};

Make QtConcurrent::mapped work with lambdas

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.

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...);
}