Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
75 views
in Technique[技术] by (71.8m points)

c++ - best way to do variant visitation with lambdas

I want to inline visitation of variant types with lambdas. At the moment i have the following code:

struct Foo {
    boost::variant< boost::blank , int , string , vector< int >  > var;

    template <typename T, typename IL , typename SL , typename VL>
    void ApplyOptionals( T& ref, IL&& intOption , SL&& stringOption , VL&& vectorOption ) {
        if (var.which() == 1) {
            intOption( ref , boost::get< int >(var) );
        } else if (var.which() ==2) {
            stringOption( ref , boost::get< string>(var) );
        } else if (var.which() == 3) {
            vectorOption( ref , boost::get< vector< int > >(var) );
        }
    };
};
// ...
myFooV.ApplyOptionals(
    obj,
    [](Obj& o, int v) -> void { cout << "int: " << v << endl; o.value = v; },
    [](Obj& o, string v) -> void{ cout << "string: " << v << endl; o.name = v; },
    [](Obj& o, vector< int >& v) -> void{ v.push_back(257); cout << " vector.. has elements: " << v.size() << endl; o.ids = v; }
);

However the main drawback of this approach is that it depends on the order of variant type parameters and doesn't detect at compile-time unhandled types like boost::static_visitor would do

Can i get the best of both approaches?


Working on the excellent answer from RMartinho, i'm trying to work out this error, it seems that variant thinks the operator() calls are amibiguous (i'm using g++ 4.5.1, it's like it couldn't see the lambda operators.

looking at this question request for member `...' is ambiguous in g++, it seems that c++ doesn't like multiple inheritance as a way to provide multiple overloads (even if the calls are completely non-ambiguous because of different signature)

#include <iostream>
#include <string>
#include <vector>
#include <boost/variant.hpp>

using namespace std;

typedef boost::variant< boost::blank , int , string , vector< int >  > var_t;

template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public boost::static_visitor<ReturnType>, public Lambdas... {
    lambda_visitor(Lambdas... lambdas) : Lambdas(lambdas)... { }
};

template <typename ReturnType>
struct lambda_visitor<ReturnType> : public boost::static_visitor<ReturnType> {
    lambda_visitor() {}
};


template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
    return { lambdas... };
    // you can use the following instead if your compiler doesn't
    // support list-initialization yet
    // return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}

int main() {
    vector< int > vit;
    vit.push_back(7);

    var_t myFooV = vit;

    auto visitor = make_lambda_visitor<void>(
        [](int v) -> void { cout << "int: " << v << endl; },
        [](string& v) -> void{ cout << "string: " << v << endl; },
        [](vector< int >& v) -> void{ v.push_back(27); boost::get< vector< int > >(myFooV).push_back(34);  cout << " vector.. has elements: " << v.size() << endl; }
    );

    cout << " and for the grand finale.. " << endl;

    boost::apply_visitor( visitor , myFooV );
};

This, gives me roughly a bunch of template boost errors, but the distinct part is:

boost_1_46_0/boost/variant/variant.hpp:832:32: error: request for member ‘operator()’ is ambiguous
test2.cpp:44:54: error: candidates are: main()::<lambda(std::vector<int>&)>
test2.cpp:43:47: error:                 main()::<lambda(std::string&)>
test2.cpp:42:55: error:                 main()::<lambda(int)>
boost_1_46_0/boost/variant/variant.hpp:832:32: error: return-statement with a value, in function returning 'void'

This is the whole error, just in case i'm missing some other relevant info:

boost_1_46_0/boost/variant/variant.hpp: In member function ‘boost::detail::variant::invoke_visitor<Visitor>::result_type boost::detail::variant::invoke_visitor<Visitor>::internal_visit(T&, int) [with T = std::vector<int>, Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, boost::detail::variant::invoke_visitor<Visitor>::result_type = void]’:
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:130:9:   instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl_invoke_impl(int, Visitor&, VoidPtrCV, T*, mpl_::true_) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T = std::vector<int>, typename Visitor::result_type = void, mpl_::true_ = mpl_::bool_<true>]’
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:173:9:   instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl_invoke(int, Visitor&, VoidPtrCV, T*, NoBackupFlag, int) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T = std::vector<int>, NoBackupFlag = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >::has_fallback_type_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:260:1:   instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl(int, int, Visitor&, VoidPtrCV, mpl_::false_, NoBackupFlag, Which*, step0*) [with Which = mpl_::int_<0>, step0 = boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<4l>, boost::blank, boost::mpl::l_item<mpl_::long_<3l>, int, boost::mpl::l_item<mpl_::long_<2l>, std::basic_string<char>, boost::mpl::l_item<mpl_::long_<1l>, std::vector<int>, boost::mpl::l_end> > > > >, boost::mpl::l_iter<boost::mpl::l_end> >, Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, NoBackupFlag = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >::has_fallback_type_, typename Visitor::result_type = void, mpl_::false_ = mpl_::bool_<false>]’
boost_1_46_0/boost/variant/variant.hpp:1776:13:   instantiated from ‘static typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::internal_apply_visitor_impl(int, int, Visitor&, VoidPtrCV) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/variant.hpp:1787:13:   instantiated from ‘typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::internal_apply_visitor(Visitor&) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/variant.hpp:1810:52:   instantiated from ‘typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::apply_visitor(Visitor&) [with Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/detail/apply_visitor_unary.hpp:60:43:   instantiated from ‘typename Visitor::result_type boost::apply_visitor(Visitor&, Visitable&) [with Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, Visitable = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >, typename Visitor::result_type = void]’
test2.cpp:49:40:   instantiated from here
boost_1_46_0/boost/variant/variant.hpp:832:32: error: request for member ‘operator()’ is ambiguous
test2.cpp:44:54: error: candidates are: main()::<lambda(std::vector<int>&)>
test2.cpp:43:47: error:                 main()::<lambda(std::string&)>
test2.cpp:42:55: error:                 main()::<lambda(int)>
boost_1_46_0/boost/variant/variant.hpp:832:32: error: return-statement with a value, in function returning 'void'

conclusion:

I want to add the final version of this utility, including tests:

lambda_visitor.h


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You could use variadic templates to take the lambdas, and build a variant visitor using inheritance. That would retain the compile time checks.

template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public static_visitor<ReturnType>, public Lambdas... {
    lambda_visitor(Lambdas... lambdas) : Lambdas(lambdas)... {}
};

And a little helper function to use argument type deduction (required for lambdas):

template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
    return { lambdas... };
    // you can use the following instead if your compiler doesn't
    // support list-initialization yet
    // return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}

Now you can make visitors like this:

auto visitor = make_lambda_visitor<int>([](int) { return 42; },
                                        [](std::string) { return 17; },
                                        [](std::vector<int>) { return 23; });

Note: due to a detail of the overload resolution process that I wasn't aware of, this elegant solution causes weird ambiguity errors :(

See the follow-up question for the fix.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...