I would like to visit a "recursive" std::variant using lambdas and overload-creating functions (e.g. boost::hana::overload).
Let's assume I have a variant type called my_variant which can store one an int, a float or a vector<my_variant>:
struct my_variant_wrapper;
using my_variant =
std::variant<int, float, std::vector<my_variant_wrapper>>;
struct my_variant_wrapper
{
my_variant _v;
};
(I'm using a wrapper my_variant_wrapper class in order to define the variant type recursively.)
I want to recursively visit the variant printing different things depending on the stored types. Here's a working example using a struct-based visitor:
struct struct_visitor
{
void operator()(int x) const { std::cout << x << "i\n"; }
void operator()(float x) const { std::cout << x << "f\n"; }
void operator()(const std::vector<my_variant_wrapper>& x) const
{
for(const auto& y : x) std::visit(*this, y._v);
}
};
Calling std::visit with the above visitor prints the desired output:
my_variant v{
std::vector<my_variant_wrapper>{
my_variant_wrapper{45},
std::vector<my_variant_wrapper>{
my_variant_wrapper{1}, my_variant_wrapper{2}
},
my_variant_wrapper{33.f}
}
};
std::visit(struct_visitor{}, v);
// Prints:
/*
45i
1i
2i
33f
*/
I would like to locally create the visitor as a series of overloaded lambdas using boost::hana::overload and boost::hana::fix.
fix is an implementation of the Y-combinator, which can be used to implement recursion in type-deduced lambdas. (See this question for more information.)
This is what I tried, and expected to work:
namespace bh = boost::hana;
auto lambda_visitor = bh::fix([](auto self, const auto& x)
{
bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[&self](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(self, z._v);
})(x);
});
My reasoning is as follows:
boost::hana::fix returns an unary generic lambda that can be used as the visitor for an std::variant.
boost::hana::fix takes a binary generic lambda where the first parameter is an unary function that allows recursion of the lambda, and the second parameter is the initial argument for the body of the lambda.
Calling boost::hana::overload with handlers for all the possible types inside my_variant creates some sort of visitor which is equivalent to struct_visitor.
Using self instead of lambda_visitor inside the const std::vector<my_variant_wrapper>& overload should allow recursion to work properly.
Immediately calling the created overload with bh::overload(...)(x) should trigger the recursive visitation.
Unfortunately, as you can see in this wandbox example, the lambda_visitor example fails to compile, spewing out a lot of almost-undecipherable template-heavy errors:
...
/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main():: [with auto:2 = boost::hana::fix_t >; auto:3 = int]' before deduction of 'auto'
{ return f(fix(f), static_cast(x)...); }
...
The error seems similar to what I would get without using boost::hana::fix:
auto lambda_visitor = bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(lambda_visitor, z._v);
});
std::visit(lambda_visitor, v);
error: use of 'lambda_visitor' before deduction of 'auto'
for(const auto& z : y) std::visit(lambda_visitor, z._v);
What am I doing wrong? Is it possible to achieve local recursive variant visitation using fix, overload and a set of lambdas?
My intuition was that lambda_visitor would have been "equivalent" to struct_visitor, thanks to the indirection offered by fix.
Let's pick a simpler example. We want to implement gcd using the fix-point combinator. First go might be something like:
auto gcd = bh::fix([](auto self, int a, int b) {
return b == 0 ? a : self(b, a%b);
});
std::cout << gcd(12, 18);
This fails to compile with gcc ultimately producing this error:
/usr/local/boost-1.61.0/include/boost/hana/functional/fix.hpp:74:50: error: use of 'main()::<lambda(auto:2, int, int)> [with auto:2 = boost::hana::fix_t<main()::<lambda(auto:2, int, int)> >]' before deduction of 'auto'
{ return f(fix(f), static_cast<X&&>(x)...); }
^
The lambda we're passing to fix() has a deduced return type. But how do we deduce it? There's only a single return statement, and that one is recursive! We need to give the compiler some help. Either we need to break apart our return statement so that one has a clear type:
auto gcd = bh::fix([](auto self, int a, int b) {
if (b == 0) {
return a;
}
else {
return self(b, a%b);
}
});
or simply provide the return type explicitly:
auto gcd = bh::fix([](auto self, int a, int b) -> int {
return b == 0 ? a : self(b, a%b);
});
Both of these options compile and work.
The same is true of your original example. If you just specify that the lambda returns void, everything works:
auto lambda_visitor = bh::fix([](auto self, const auto& x) -> void
// ^^^^^^^^
{
bh::overload(
[](int y){ std::cout << y << "i\n"; },
[](float y){ std::cout << y << "f\n"; },
[&self](const std::vector<my_variant_wrapper>& y)
{
for(const auto& z : y) std::visit(self, z._v);
})(x);
});
std::visit(lambda_visitor, v);
Related
I'm trying to write the “plus2” and “plus3” functions that implement the functions, respectively, and are defined as follows.
f is a function such that expression (x) (y) should result in the value of x + y.
g is a function such that expression (x) (y) (z) should result in the value of x + y + z. Here, x, y, z are integers.
#include <functional>
#include <iostream>
std::function<int(int)> plus2(int n) {
return [n](int x) { return x + n; };
}
std::function<int(int)> plus3(int f(int), int n) {
return [f, n](int x) { return f(n) + x; };
}
int main() {
std::cout<<plus2(1)(2)<<" "<<plus3(1)(2)(3);
return 0;
}
OUTPUT should be: 3 6
I get an error
too few arguments to function ‘std::function<int(int)> plus3(int
(*)(int), int)’
Function plus2 works fine. Could you explain me where I'm making mistake in function plus3?
Function plus2 works fine.
Because plus2 is a function that expects 1 parameter and returns a function that expects 1 parameter.
plus3 on the other hand is a function that expects 2 parameters. You cannot call it with only one parameter. You can turn it into a function that expects one parameter and returns a callable that returns a callable.
I suppose this is just an exercise to learn lambdas, because otherwise I see no reason to write the functions like this. At least you should use auto return type to avoid unnecessary conversion to std::function (*):
#include <iostream>
auto plus3(int n) {
return [n](int a) { return [b=a+n](int c){ return b+c;} ;};
}
int main(int argc, char* argv[]){
std::cout << plus3(1)(2)(3);
}
(*): std::function is not the type you should use whenever you want to pass a callable around. Rather it is the type to be used when you need one type that can store any kind of callable. std::function uses type erasure to achieve that. If you do not need that then you do not need std::function.
It looks like you are practicing the subject of currying in functional programming (Currying - Wikipedia).
If you want to write plus3 as a curried function you can do the following (no change in plus2):
#include <functional>
#include <iostream>
auto plus2(int n) {
return [n](int x) { return x + n; };
}
auto plus3(int n) {
return [n](int m) {
return [n, m](int x) { return x + n + m; }; };
}
int main() {
std::cout << plus2(1)(2) << " " << plus3(1)(2)(3);
return 0;
}
plus3 should take an int and produce a function.
It should not take a function and an int.
The resulting function should take an int and then proceed as plus2.
It's a nice touch to reuse plus2:
std::function<std::function<int(int)>(int)> plus3(int n) {
return [n](int x) { return plus2(n+x); };
}
Slightly more readable with a type alias:
using int_to_int = std::function<int(int)>;
std::function<int_to_int(int)> plus3(int n) {
return [n](int x) { return plus2(n+x); };
}
and even more readable with auto (although it's not equivalent since it doesn't give the same type):
auto plus3(int n) {
return [n](int x) { return plus2(n+x); };
}
and now you can generalize:
template<size_t n>
auto plus(int x)
{
return [x](int y) { return plus<n-1>(x+y); };
}
template<>
auto plus<1>(int x) { return x; }
int main() {
std::cout << plus<2>(1)(2) << std::endl << plus<4>(1)(2)(3)(4) << std::endl;
}
Can we use auto keyword instead of template?
Consider following example :
#include <iostream>
template <typename T>
T max(T x, T y) // function template for max(T, T)
{
return (x > y) ? x : y;
}
int main()
{
std::cout << max<int>(1, 2) << '\n'; // instantiates and calls function max<int>(int, int)
std::cout << max<int>(4, 3) << '\n'; // calls already instantiated function max<int>(int, int)
std::cout << max<double>(1, 2) << '\n'; // instantiates and calls function max<double>(double, double)
return 0;
}
So we can write it this way too :
#include <iostream>
auto max(auto x, auto y)
{
return (x > y) ? x : y;
}
int main()
{
std::cout << max(1, 2) << '\n';
std::cout << max(4, 3) << '\n';
std::cout << max(1, 2) << '\n';
return 0;
}
So, why should use auto keyword instead of template?
As #HolyBlackCat said in the comment, the snippets are not the same.
In the first snippet when you use templates, you confine the arguments of T max(T x, T y) to be of the same type. So if you take the template approach, the following code will not work:
int x = 3;
double y = 5.4;
max(3, 5.4);
However, if you take the second approach, you can compare two different data types (if permitted, of course). This is because both argument's auto will decide what it's going to get independently, so comparing a int and double in the second approach is totally valid.
Finally I found the answer to my question:
We can use abbreviated function templates if we're using the C++20 language standard. They are simpler to type and understand because they produce less syntactical clutter.
Note that our two snippets are not the same. The top one enforces that x and y are the same type, whereas the bottom one does not.
Is it possible to get a variant's member called x even if it has a different type in each alternative?
#include <variant>
class A {
int x = 0;
};
class B {
int x = 1;
};
class C {
bool x = false;
};
std::variant<A, B, C> variant = A{};
template<typename R> R getValue(std::variant<A, B, C>& variant ) {
//return the value of x here
}
On Walk over variants that share the same member and get it?, I asked the same question, but it didn't specify that I wanted to get different types of values. This answer talks about std::visit() but that does not accept a lambda that returns different types.
This is what you look for, I believe:
template<typename... Ts>
auto getValue(std::variant<Ts...>& v )
{
using R = std::common_type_t<decltype(Ts::x)...>;
return std::visit([](auto &obj){return static_cast<R>(obj.x);}, v);
}
Above code doesn't require you to state the type you want back but rather deduces it by using std::common_type of the variable x in all the different possible values in your variant.
Code: http://coliru.stacked-crooked.com/a/a1f54902ea1a9db3
One more approach, again using visit. :) One thing to realize is that visit doesn't just take a single visitor, but can take a full overload set of visitor functions, allowing different functions to handle the different values.
Thus you can flip the problem around and instead of trying to return one of the different values from your variant, you can provide handlers for the values and send the value to the proper handler.
For example, using the overloaded function-call operator in a struct:
struct A { int x = 0; };
struct B { std::string x = "2"; };
struct C { bool x = true; };
int main() {
struct Process {
void operator()(A const& a) const {
cout << "Got an A: " << a.x << '\n';
}
void operator()(B const& b) const {
cout << "Got a B: " << b.x << '\n';
}
void operator()(C const& c) const {
cout << "Got a C: " << c.x << '\n';
}
};
var = A{};
std::visit(Process{}, var);
var = B{};
std::visit(Process{}, var);
var = C{};
std::visit(Process{}, var);
}
A "modern" popular alternate syntax utilizing a bunch of c++17 features (including CTAD, variadic templates, deduction guides, inheriting from lambdas, and variadic using declarations in just a few lines) also looks nice, and is approximately equivalent to the above, though the first 2 lines defining the overloaded class could be considered library code even if they do not make full sense yet.
// probably library code
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
struct A { int x = 0; };
struct B { std::string x = "2"; };
struct C { bool x = true; };
int main() {
// and then we have this nice, concise syntax:
auto process = [](auto & variant) {
std::visit(
overloaded{
[](A const& a) { cout << "Got an A: " << a.x << '\n'; },
[](B const& b) { cout << "Got a B: " << b.x << '\n'; },
[](C const& c) { cout << "Got a C: " << c.x << '\n'; }
},
var);
};
var = A{};
process(var);
var = B{};
process(var);
var = C{};
process(var);
}
Here's both versions together in Compiler Explorer: https://godbolt.org/z/8T91W4T9G
With the signature and template of your getValue function, you would actually need to manually decide the type of R when calling it. So in main, you would call it like:
std::cout << getValue<int>(variant); // prints 0;
And if that is what you intended, then you can simply cast the .x to type R:
template<typename R>
R getValue(std::variant<A, B, C>& v )
{
return std::visit([](auto &obj){return R(obj.x);}, v);
}
However, I highly doubt that's what you intended.
I am relatively new to C++, so trying to find the best way to store different types of values under one entity.
Context is, I have a function which returns a struct and one of the values can be of different types. At any time, only one value will be set.
Best case for me would be if I can do something like this:
Union Union1 {
int x;
char y;
double z;
}
executor(Union1 union1) {
if(union1.x.isSet()) {
doSomethingUsingX();
} else if(union1.y.isSet()) {
doSomethingElseUsingY();
}
}
I dont think this is possible because c++ unions use same memory space.
So is there something I can use?
If you use C++17 or newer, try std::variant. See https://en.cppreference.com/w/cpp/utility/variant
If older than C++17 you can try boost::variant
The variant is a better way to do a union. It also records what kind of thing is stored in it, and you can use std::visit to call a type overloaded function on a variant.
Like this, adapted from the cppreference example:
#include <iostream>
#include <variant>
#include <vector>
// helper type for the visitor #4
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::vector<std::variant<int, char, double>> v;
v.emplace_back(1);
v.emplace_back('c');
v.emplace_back(2.7);
for (const auto &x : v) {
std::visit(
overloaded{[](auto arg) { std::cout << "unknown " << arg << '\n'; },
[](int arg) { std::cout << "int " << arg << '\n'; },
[](double arg) { std::cout << "double " << arg << '\n'; },
[](char arg) { std::cout << "char " << arg << '\n'; }},
x);
}
return 0;
}
Also this blog post I found online seems to help explain how that works:
https://pabloariasal.github.io/2018/06/26/std-variant/
You could use an enum to report the return type:
typedef enum
{
x,
y,
z
}returnType;
typedef struct
{
returnType rt;
union
{
int x;
char y;
double z;
};
}myStruct;
//...
myStruct AFunctionWithMyStructReturnType(int arg)
{
myStruct ms;
if(arg == 0)
{
ms.rt = returnType.x;
ms.val = 10000;
}
else if(arg == 1)
{
ms.rt = returnType.y;
ms.val = 200;
}
else
{
ms.rt = returnType.z;
ms.val = 3.14;
}
return ms;
}
//...
myStruct S = AFunctionWithMyStructReturnType(arg);
switch(S.rt)
{
case returnType.x:
processX(S.val);
break;
case returnType.y:
processY(S.val);
break;
case returnType.z:
processZ(S.val);
break;
}
I have a question continuing the post Function passed as template argument. In the provided code:
#include <iostream>
void add1(int &v)
{
v+=1;
}
void add2(int &v)
{
v+=2;
}
template <void (*T)(int &)>
void doOperation()
{
int temp=0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
what about a third function which has a different parameter set layout, e.g.
double add3(double v1, double v2)
{
return v1+v2;
}
If this is not achievable using template at all, how do we pass an arbitrary function to another function? And how do we handle the parameter set with all kinds of possibilities? I know python may be able to do it by passing a tuple (kwargs**), but not sure about C/C++.
One form of passing a generic function to be called is a callable templated type:
#include <functional>
#include <iostream>
template<typename F>
void callFoo(F f) {
f();
}
int main() {
callFoo(std::bind([](int a, int b) {std::cout << a << ' ' << b;}, 5, 6));
}
callFoo takes a callable type, F, and calls it. Around this call, you can, for example, do timer work to time the function. In main, it's called with a lambda that has two parameters and the values given to those parameters bound to it. callFoo can then call it without storing the arguments. This is very similar to taking a parameter with the type std::function<void()>.
If, however, you don't want to use std::bind, you can pass in the arguments separately with a couple changes:
template<typename F, typename... Args>
void callFoo(F f, Args... args) { //ignoring perfect forwarding
f(args...);
}
int main() {
callFoo(/*lambda*/, 5, 6);
}
In these cases, passing void functions makes sense. Indeed, return values can be used as parameters and passed in with std::ref. If you plan on returning what the function returns, you'll have to handle the special case of the return type being void, as you can't assign to a void variable and return that. At this point, it's easier to direct you to my previous question on the matter. My use case for it turned out to be moot, but the solution works great for other uses.
This could possibly lead you closer to what you want:
#include <iostream>
void add1(int &v)
{
v+=1;
}
double add2(double v1, double v2)
{
return v1 + v2;
}
// 1 param version
template< class aRetType, class tP1 >
aRetType doOperation( aRetType (*aFunction)( tP1 aP1 ), tP1 valP1 )
{
return aFunction( valP1 );
}
// 2 param version
template< class aRetType, class tP1, class tP2 >
aRetType doOperation( aRetType (*aFunction)( tP1 aP1, tP2 aP2 ), tP1 valP1, tP2 valP2 )
{
return aFunction( valP1, valP2 );
}
// 3 param version and up is not given, but you get the trick.
int main()
{
int iTemp = 8;
doOperation< void, int& >( add1, iTemp );
std::cout << "Result is " << iTemp << std::endl;
double iResult;
iResult = doOperation< double, double, double >( add2, 2.2, 8.8);
std::cout << "Result is " << iResult << std::endl;
}