Related
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;
}
Suppose I have a set of types :
constexpr std::tuple<int,double,string> my_types;
a set of values to identify them:
constexpr std::array<const char*,3> my_ids = {"int","double","string"}; // const char* instead of string to be constexpr-compatible
and an overload set
template<class T> bool my_fun(my_type complex_object) { /* some treatment depending on type T */ }
I have a manually dispatching function like that:
string my_disp_fun(my_type complex_object) {
const char* id = get_info(complex_object);
using namespace std::string_literals;
if (id == "int"s) {
return my_fun<int>(complex_object);
} else if (id == "double"s) {
return my_fun<double>(complex_object);
} else if (id == "string"s) {
return my_fun<string>(complex_object);
} else {
throw;
}
}
Because I see this pattern coming again and again with a different my_fun every time, I would like to replace it by something like that:
struct my_mapping {
static constexpr std::tuple<int,double,string> my_types;
static constexpr std::array<const char*,3> my_ids = {"int","double","string"}; // const char* instead of string to be constexpr-compatible
}
string my_disp_fun(my_type complex_object) {
const char* id = get_info(complex_object);
return
dispatch<my_mapping>(
id,
my_fun // pseudo-code since my_fun is a template
);
}
How to implement the dispatch function? I am pretty confident it can be done but so far, I can't think of a reasonably nice API that would still be implementable with template metaprograming tricks.
I am sure people already had the need for this kind of problem. Is there a name for this pattern? I don't really know how to even qualify it in succinct technical terms...
Side question: is it related to the pattern matching proposal? I'm not sure because the paper seems more interested in the matching part, not generating branchs from that, right ?
Leverage variant.
template<class T>struct tag_t{using type=T};
template<class T>constexpr tag_t<T> tag={};
template<class...Ts>
using tag_enum = std::variant<tag_t<Ts>...>;
now tag_enum is a type stores a type at runtime as a value. Its runtime representation is an integer (!), but C++ knows that integer represents a specific type.
We now just have to map your strings to integers
using supported_types=tag_enum<int, double, std::string>;
std::unordered_map<std::string, supported_types> name_type_map={
{"int", tag<int>},
{"double", tag<double>},
{"string", tag<std::string>},
};
this map can be built from an array and a tuple if you want, or made global somewhere, or made into a function.
The point is, a mapping of any kind to a tag_enum can be used to auto dispatch a function.
To see how:
string my_disp_fun(my_type complex_object) {
const char* id = get_info(complex_object);
return std::visit( [&](auto tag){
return my_fun<typename decltype(tag)::type>( complex_object );
}, name_type_map[id] };
}
refactoring this to handle whatever level of automation you want should be easy.
If you adopt the convention that you pass T as a tag_t as the first argument it gets even easier to refactor.
#define RETURNS(...)\
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define MAKE_CALLER_OF(...) \
[](auto&&...args) \
RETURNS( (__VA_ARGS__)(decltype(args)(args)...) )
now you can easily wrap a template function into an object
template<class F>
auto my_disp_fun(my_type complex_object F f) {
const char* id = get_info(complex_object);
return std::visit( [&](auto tag){
return f( tag, complex_object );
}, name_type_map[id] }; // todo: handle failure to find it
}
then
std::string s = my_disp_fun(obj, MAKE_CALLER_OF(my_fun));
does the dispatch for you.
(In theory we could pass the template parameter in the macro, but the above macros are generically useful, while one that did wierd tag unpacking are not).
Also we can make a global type map.
template<class T>
using type_entry = std::pair<std::string, tag_t<T>>;
#define TYPE_ENTRY_EX(NAME, X) type_entry<X>{ NAME, tag<X> }
#define TYPE_ENTRY(X) TYPE_ENTRY_EX(#X, X)
auto TypeTable = std::make_tuple(
TYPE_ENTRY(int),
TYPE_ENTRY(double),
TYPE_ENTRY_EX("string", std::string)
);
template<class Table>
struct get_supported_types_helper;
template<class...Ts>
struct get_supported_types_helper<std::tuple<type_entry<Ts>...>> {
using type = tag_enum<Ts...>;
};
template<class Table>
using get_supported_types = typename get_supported_types_helper<Table>::type;
From that you can do things like make the unordered map from the TypeTable tuple automatically.
All of this is just to avoid having to mention the supported types twice.
Since your functions have the same signature, you can use a std::map to map the ids to function pointers, eg:
template<class T>
std::string my_fun(my_type complex_object)
{
/* some treatment depending on type T */
return ...;
}
using my_func_type = std::string(*)(my_type);
const std::map<std::string, my_func_type> my_funcs = {
{"int", &my_fun<int>},
{"double", &my_fun<double>},
{"string", &my_fun<std::string>}
};
std::string my_disp_fun(my_type complex_object)
{
const char *id = get_info(complex_object);
auto iter = my_funcs.find(id);
if (iter == my_funcs.end())
throw ...;
return iter->second(complex_object);
}
Demo
I'm not sure that this is what you are looking for. But you can do it without the need to hold an additional array with the types:
// overload visitor trick
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// deduction guide
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::tuple<int, double, const char*> tup = {10, 2.5, "hello"};
auto f = overloaded {
[](int arg){std::cout << arg << " + 3 = " << arg + 3 << std::endl;},
[](double arg){std::cout << arg << " * 2 = " << arg * 2 << std::endl;},
[](const std::string& arg){std::cout << "string: " << arg << std::endl;}
};
std::apply([&](const auto&... e){ (f(e), ...);}, tup);
}
Code: http://coliru.stacked-crooked.com/a/3bfdd35f89ceeff9
Suppose I have a variable constructors, which is a tuple of constructor functions represented in variadic generic lambdas.
// types for constructors
using type_tuple = std::tuple<ClassA, ClassB, ClassC>;
// Get a tuple of constructors(variadic generic lambda) of types in type_tuple
auto constructors = execute_all_t<type_tuple>(get_construct());
// For definitions of execute_all_t and get_construct, see link at the bottom.
I can instantiate an object with:
// Create an object using the constructors, where 0 is index of ClassA in the tuple.
ClassA a = std::get<0>(constructors)(/*arguments for any constructor of ClassA*/);
Is it possible to index the type in runtime with a magic_get like below?
auto obj = magic_get(constructors, 0)(/*arguments for any constructor of ClassA*/);
// Maybe obj can be a std::variant<ClassA, ClassB, ClassC>, which contains object of ClassA?
Edit: Ideally obj should be an instance of ClassA. If not possible, I can accept obj to be std::variant<ClassA, ClassB, ClassC>.
Please check out the minimal reproducible example: Try it online!
A similar question: C++11 way to index tuple at runtime without using switch
.
You might have your runtime get return std::variant, something like:
template <typename ... Ts, std::size_t ... Is>
std::variant<Ts...> get_impl(std::size_t index,
std::index_sequence<Is...>,
const std::tuple<Ts...>& t)
{
using getter_type = std::variant<Ts...> (*)(const std::tuple<Ts...>&);
getter_type funcs[] = {+[](const std::tuple<Ts...>& tuple)
-> std::variant<Ts...>
{ return std::get<Is>(tuple); } ...};
return funcs[index](t);
}
template <typename ... Ts>
std::variant<Ts...> get(std::size_t index, const std::tuple<Ts...>& t)
{
return get_impl(index, std::index_sequence_for<Ts...>(), t);
}
Then you might std::visit your variant to do what you want.
Demo
or for your "factory" example:
int argA1 = /*..*/;
std::string argA2 = /*..*/;
int argB1 = /*..*/;
// ...
auto obj = std::visit(overloaded{
[&](const A&) -> std::variant<A, B, C> { return A(argA1, argA2); },
[&](const B&) -> std::variant<A, B, C> { return B(argB1); },
[&](const C&) -> std::variant<A, B, C> { return C(); },
}, get(i, t))
This can probably be done more nicely, but here is an attempt according to your requirements in the comments.
Requires C++17, works on Clang, but gives an Internal Compiler Error on GCC.
It does require though, that you make the constructing function SFINAE-friendly, otherwise there is no way of checking whether it can be called:
So use
return [](auto... args) -> decltype(U(args)...) { return U(args...); };
instead of
return [](auto... args) { return U(args...); };
The behavior of this function given arguments tup and index is as follows:
It returns a lambda that when called with a list of arguments will return a std::variant of all the types that could result from calls of the form std::get<i>(tup)(/*arguments*/). Which one of these is actually called and stored in the returned variant is decided at runtime through the index argument. If index refers to a tuple element that cannot be called as if by std::get<index>(tup)(/*arguments*/), then an exception is thrown at runtime.
The intermediate lambda can be stored and called later. Note however that it saves a reference to the tup argument, so you need to make sure that the argument out-lives the lambda if you don't call and discard it immediately.
#include <tuple>
#include <type_traits>
#include <variant>
#include <utility>
#include <stdexcept>
template<auto V> struct constant_t {
static constexpr auto value = V;
using value_type = decltype(value);
constexpr operator value_type() const {
return V;
}
};
template<auto V>
inline constexpr auto constant = constant_t<V>{};
template<auto V1, auto V2>
constexpr auto operator+(constant_t<V1>, constant_t<V2>) {
return constant<V1+V2>;
}
template<typename T>
struct wrap_t {
using type = T;
constexpr auto operator+() const {
return static_cast<wrap_t*>(nullptr);
}
};
template<typename T>
inline constexpr auto wrap = wrap_t<T>{};
template<auto A>
using unwrap = typename std::remove_pointer_t<decltype(A)>::type;
template <typename Tup>
auto magic_get(Tup&& tup, std::size_t index) {
return [&tup, index](auto&&... args) {
// Get the input tuple size
constexpr auto size = std::tuple_size_v<std::remove_const_t<std::remove_reference_t<Tup>>>;
// Lambda: check if element i of tuple is invocable with given args
constexpr auto is_valid = [](auto i) {
return std::is_invocable_v<decltype(std::get<i>(tup)), decltype(args)...>;
};
// Lambda: get the wrapped return type of the invocable element i of tuple with given args
constexpr auto result_type = [](auto i) {
return wrap<std::invoke_result_t<decltype(std::get<i>(tup)), decltype(args)...>>;
};
// Recursive lambda call: get a tuple of wrapped return type using `result_type` lambda
constexpr auto valid_tuple = [=]() {
constexpr auto lambda = [=](auto&& self, auto i) {
if constexpr (i == size)
return std::make_tuple();
else if constexpr (is_valid(i))
return std::tuple_cat(std::make_tuple(result_type(i)), self(self, i + constant<1>));
else
return self(self, i + constant<1>);
};
return lambda(lambda, constant<std::size_t{0}>);
}();
// Lambda: get the underlying return types as wrapped variant
constexpr auto var_type =
std::apply([](auto... args) { return wrap<std::variant<unwrap<+args>...>>; }, valid_tuple);
/**
* Recursive lambda: get a variant of all underlying return type of matched functions, which
* contains the return value of calling function with given index and args.
*
* #param self The lambda itself
* #param tup A tuple of functions
* #param index The index to choose from matched (via args) functions
* #param i The running index to reach `index`
* #param j The in_place_index for constructing in variant
* #param args The variadic args for callling the function
* #return A variant of all underlying return types of matched functions
*/
constexpr auto lambda = [=](auto&& self, auto&& tup, std::size_t index, auto i, auto j,
auto&&... args) -> unwrap<+var_type> {
if constexpr (i == size)
throw std::invalid_argument("index too large");
else if (i == index) {
if constexpr (is_valid(i)) {
return unwrap<+var_type>{std::in_place_index<j>,
std::get<i>(tup)(decltype(args)(args)...)};
} else {
throw std::invalid_argument("invalid index");
}
} else {
return self(self, decltype(tup)(tup), index, i + constant<1>, j + constant<is_valid(i)>,
decltype(args)(args)...);
}
};
return lambda(lambda, std::forward<Tup>(tup), index, constant<std::size_t{0}>,
constant<std::size_t{0}>, decltype(args)(args)...);
};
}
In C++20, you can simplify this by
using std::remove_cvref_t<Tup> instead of std::remove_const_t<std::remove_reference_t<Tup>>
changing the definition of unwrap to:
template<auto A>
using unwrap = typename decltype(A)::type;
and using it as unwrap<...> instead of unwrap<+...>, which also allows removing the operator+ from wrap_t.
The purpose of wrap/unwrap:
wrap_t is meant to turn a type into a value that I can pass into functions and return from them without creating an object of the original type (which could cause all kinds of issues). It is really just an empty struct templated on the type and a type alias type which gives back the type.
I wrote wrap as a global inline variable, so that I can write wrap<int> instead of wrap<int>{}, since I consider the additional braces annoying.
unwrap<...> isn't really needed. typename decltype(...)::type does the same, it just gives back the type that an instance of wrap represents.
But again I wanted some easier way of writing it, but without C++20 this is not really possible in a nice way. In C++20 I can just pass the wrap object directly as template argument, but that doesn't work in C++17.
So in C++17 I "decay" the object to a pointer, which can be a non-type template argument, with an overloaded operator+, mimicking the syntax of the common lambda-to-function-pointer trick using the unary + operator (but I could have used any other unary operator).
The actual pointer value doesn't matter, I only need the type, but the template argument must be a constant expression, so I let it be a null pointer. The latter requirement is why I am not using the built-in address-of operator & instead of an overloaded +.
Since C++14 we can use generic lambdas:
auto generic_lambda = [] (auto param) {};
This basically means that its call operator is templated based on the parameters marked as auto.
The question is how to create a lambda that can accept a variadic number of parameters similarly to how a variadic function template would work ? If this is not possible what is the closest thing that could be used the same way ?
How would you store it ? Is it possible in a std::function ?
I am not sure what your intention is but instead of storing it in a std::function you can use the lambda itself to capture the params.
This is an example discussed on the boost mailing list. It is used in the boost::hana implementation
auto list = [](auto ...xs) {
return [=](auto access) { return access(xs...); };
};
auto head = [](auto xs) {
return xs([](auto first, auto ...rest) { return first; });
};
auto tail = [](auto xs) {
return xs([](auto first, auto ...rest) { return list(rest...); });
};
auto length = [](auto xs) {
return xs([](auto ...z) { return sizeof...(z); });
};
// etc...
// then use it like
auto three = length(list(1, '2', "3"));
Syntax
How do you create a variadic generic lambda ?
You can create a variadic generic lambda with the following syntax:
auto variadic_generic_lambda = [] (auto... param) {};
Basically you just add ... between auto (possibly ref qualified) and your parameter pack name.
So typically using universal references would give:
auto variadic_generic_lambda = [] (auto&&... param) {};
Usage
How do you use the parameters ?
You should consider the variadic generic parameter as having a template parameter pack type, because it is the case. This more or less implies that most if not all usage of those parameters will require templates one way or the other.
Here is a typical example:
#include <iostream>
void print(void)
{
}
template <typename First, typename ...Rest>
void print(const First& first, Rest&&... Args)
{
std::cout << first << std::endl;
print(Args...);
}
int main(void)
{
auto variadic_generic_lambda = [] (auto... param)
{
print(param...);
};
variadic_generic_lambda(42, "lol", 4.3);
}
Storage
How do you store a variadic generic lambda ?
You can either use auto to store a lambda in a variable of its own type, or you can store it in a std::function but you will only be able to call it with the fixed signature you gave to that std::function :
auto variadic_generic_lambda = [] (auto... param) {};
std::function<void(int, int)> func = variadic_generic_lambda;
func(42, 42); // Compiles
func("lol"); // Doesn't compile
What about collections of variadic generic lambdas ?
Since every lambda has a different type you cannot store their direct type in the usual homogeneous containers of the STL. The way it is done with non generic lambdas is to store them in a corresponding std::function which will have a fixed signature call and that won't restrain anything since your lambda is not generic in the first place and can only be invoked that way:
auto non_generic_lambda_1 = [] (int, char) {};
auto non_generic_lambda_2 = [] (int, char) {};
std::vector<std::function<void(int, char)>> vec;
vec.push_back(non_generic_lambda_1);
vec.push_back(non_generic_lambda_2);
As explained in the first part of this storage section if you can restrain yourself to a given fixed call signature then you can do the same with variadic generic lambdas.
If you can't you will need some form of heterogenous container like:
std::vector<boost::variant>
std::vector<boost::any>
boost::fusion::vector
See this question for an example of heterogenous container.
What else ?
For more general informations on lambdas and for details on the members generated and how to use the parameters within the lambda see:
http://en.cppreference.com/w/cpp/language/lambda
How does generic lambda work in C++14?
How to call a function on all variadic template args?
What is the easiest way to print a variadic parameter pack using std::ostream?
Consider this
#include <iostream>
namespace {
auto out_ = [] ( const auto & val_)
{
std::cout << val_;
return out_ ;
};
auto print = [](auto first_param, auto... params)
{
out_(first_param);
// if there are more params
if constexpr (sizeof...(params) > 0) {
// recurse
print(params...);
}
return print;
};
}
int main()
{
print("Hello ")("from ")("GCC ")(__VERSION__)(" !");
}
(wandbox here) This "print" lambda is:
Variadic
Recursive
Generic
Fast
And no templates in sight. (just underneath :) ) No C++ code that looks like radio noise. Simple, clean and most importantly:
Easy to maintain
No wonder "it feels like a new language".
I'd like to create a function that takes a weak pointer and any kind of functor (lambda, std::function, whatever) and returns a new functor that only executes the original functor when the pointer was not removed in the meantime (so let's assume there is a WeakPointer type with such semantics). This should all work for any functor without having to specify explicitly the functor signature through template parameters or a cast.
EDIT:
Some commenters have pointed out that std::function - which I used in my approach - might not be needed at all and neither might the lambda (though in my original question I also forgot to mention that I need to capture the weak pointer parameter), so any alternative solution that solves the general problem is of course is also highly appreciated, maybe I didn't think enough outside the box and was to focused on using a lambda + std::function. In any case, here goes what I tried so far:
template<typename... ArgumentTypes>
inline std::function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const std::function<void(ArgumentTypes...)>&& fun)
{
return [=] (ArgumentTypes... args)
{
if(pWeakPointer)
{
fun(args...);
}
};
}
This works well without having to explicitly specify the argument types if I pass an std::function, but fails if I pass a lambda expression. I guess this because the std::function constructor ambiguity as asked in this question. In any case, I tried the following helper to be able to capture any kind of function:
template<typename F, typename... ArgumentTypes>
inline function<void(ArgumentTypes...)> wrap(WeakPointer pWeakPointer, const F&& fun)
{
return wrap(pWeakPointer, std::function<void(ArgumentTypes...)>(fun));
}
This now works for lambdas that don't have parameters but fails for other ones, since it always instantiates ArgumentTypes... with an empty set.
I can think of two solution to the problem, but didn't manage to implement either of them:
Make sure that the correct std::function (or another Functor helper type) is created for a lambda, i.e. that a lambda with signature R(T1) results in a std::function(R(T1)) so that the ArgumentTypes... will be correctly deduced
Do not put the ArgumentTypes... as a template parameter instead have some other way (boost?) to get the argument pack from the lambda/functor, so I could do something like this:
-
template<typename F>
inline auto wrap(WeakPointer pWeakPointer, const F&& fun) -> std::function<void(arg_pack_from_functor(fun))>
{
return wrap(pWeakPointer, std::function<void(arg_pack_from_functor(fun))(fun));
}
You don't have to use a lambda.
#include <iostream>
#include <type_traits>
template <typename F>
struct Wrapper {
F f;
template <typename... T>
auto operator()(T&&... args) -> typename std::result_of<F(T...)>::type {
std::cout << "calling f with " << sizeof...(args) << " arguments.\n";
return f(std::forward<T>(args)...);
}
};
template <typename F>
Wrapper<F> wrap(F&& f) {
return {std::forward<F>(f)};
}
int main() {
auto f = wrap([](int x, int y) { return x + y; });
std::cout << f(2, 3) << std::endl;
return 0;
}
Assuming the weak pointer takes the place of the first argument, here's how I would do it with a generic lambda (with move captures) and if C++ would allow me to return such a lambda:
template<typename Functor, typename Arg, typename... Args>
auto wrap(Functor&& functor, Arg&& arg)
{
return [functor = std::forward<Functor>(functor)
, arg = std::forward<Arg>(arg)]<typename... Rest>(Rest&&... rest)
{
if(auto e = arg.lock()) {
return functor(*e, std::forward<Rest>(rest)...);
} else {
// Let's handwave this for the time being
}
};
}
It is possible to translate this hypothetical code into actual C++11 code if we manually 'unroll' the generic lambda into a polymorphic functor:
template<typename F, typename Pointer>
struct wrap_type {
F f;
Pointer pointer;
template<typename... Rest>
auto operator()(Rest&&... rest)
-> decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )
{
if(auto p = lock()) {
return f(*p, std::forward<Rest>(rest)...);
} else {
// Handle
}
}
};
template<typename F, typename Pointer>
wrap_type<typename std::decay<F>::type, typename std::decay<Pointer>::type>
wrap(F&& f, Pointer&& pointer)
{ return { std::forward<F>(f), std::forward<Pointer>(pointer) }; }
There are two straightforward options for handling the case where the pointer has expired: either propagate an exception, or return an out-of-band value. In the latter case the return type would become e.g. optional<decltype( f(*pointer.lock(), std::forward<Rest>(rest)...) )> and // Handle would become return {};.
Example code to see everything in action.
[ Exercise for the ambitious: improve the code so that it's possible to use auto g = wrap(f, w, 4); auto r = g();. Then, if it's not already the case, improve it further so that auto g = wrap(f, w1, 4, w5); is also possible and 'does the right thing'. ]