Boost binary static_visitor and apply_visitor

12,490

Solution 1

@Boaz Yaniv's answer is 100% correct. The boost::apply_visitor<> docs state directly:

Overloads accepting two operands invoke the binary function call operator of the given visitor on the content of the given variant operands.

Yaniv's suggested approach for remedying that – taking a Visibility object in the visitor's constructor – is also the proper fix. You indicated that such an approach did not work for you; I'll warrant that the problem was in your attempt and not in the approach. ;-] Here's code that compiles:

#include <boost/variant.hpp>

struct LandSearchParameter { };
struct WaterSearchParameter { };
struct EnvironmentalFactors { };

typedef boost::variant<
    LandSearchParameter,
    WaterSearchParameter
> SearchParameter;

enum Visibility
{
    CLEAR,
    CLOUDY,
    FOG,
    SMOKE
};

struct DetectionGenerator : boost::static_visitor<double>
{
    DetectionGenerator(EnvironmentalFactors const& factors, Visibility vis)
      : mFactors(factors),
        mVis(vis)
    { }

    double operator ()(LandSearchParameter const&) const { return 0.; }
    double operator ()(WaterSearchParameter const&) const { return 0.; }

private:
    EnvironmentalFactors mFactors;
    Visibility mVis;
};

int main()
{
    SearchParameter param = LandSearchParameter();
    EnvironmentalFactors const envFactors;
    DetectionGenerator const detectGen(envFactors, CLOUDY);
    double const prob = boost::apply_visitor(detectGen, param);
}

If this approach continues to fail to work for you then you'll need to edit your question and update it with your actual, current code.

P.S. Your approach of making Visibility a single-type boost::variant<> should work also, though it seems silly to me. For reference, this compiles:

#include <boost/variant.hpp>

struct LandSearchParameter { };
struct WaterSearchParameter { };
struct EnvironmentalFactors { };

typedef boost::variant<
    LandSearchParameter,
    WaterSearchParameter
> SearchParameter;

enum VisibilityT
{
    CLEAR,
    CLOUDY,
    FOG,
    SMOKE
};

typedef boost::variant<VisibilityT> Visibility;

struct DetectionGenerator : boost::static_visitor<double>
{
    explicit DetectionGenerator(EnvironmentalFactors const& factors)
      : mFactors(factors)
    { }

    double operator ()(LandSearchParameter const&, VisibilityT const) const
    { return 0.; }

    double operator ()(WaterSearchParameter const&, VisibilityT const) const
    { return 0.; }

private:
    EnvironmentalFactors mFactors;
};

int main()
{
    SearchParameter param = LandSearchParameter();
    EnvironmentalFactors const envFactors;
    DetectionGenerator const detectGen(envFactors);
    Visibility vis = CLOUDY;
    double const prob = boost::apply_visitor(detectGen, param, vis);
}

Solution 2

boost::apply_visitor only takes a visitor that takes a single argument, so it won't accept your visitor. You may be able to fix it with some binding, but I think a nicer solution would be just to add the Visibility argument at your DetectionGenerator class constructor:

class DetectionGenerator : public boost::static_visitor<double>{
public:

    DetectionGenerator(const EnvironmentalFactors& factors, Visibility vis);

    double operator()(const LandSearchParameter& land) const;
    double operator()(const WaterSearchParameter& water) const;

private:

    const EnvironmentalFactors mFactors;
    const Visibility mVis;
};

SearchParameter param = globeCover.generateSearch(lat, lon, altitude);
Visibility vis = weather.generateVisibility(lat, lon, altitude, bearing);
DetectionGenerator detectGen(envFactors, vis);
double prob = boost::apply_visitor(detectGen, param);

And as for binary visitation, I don't think this is what you're looking for. It allows apply_visitor to take two arguments, but both arguments must be variants.

Share:
12,490

Related videos on Youtube

wheaties
Author by

wheaties

I come here to learn new things and share what I've learned with others. I'm not perfect and I don't claim to be. I'm also not the best developer the world has ever seen and got into the whole software development thing later in life. However, with every day I spend hacking outside work I grow. My work five years ago would make me cry and in five years time I'll probably say the same thing. Such is life, no? If you ever have a question or would like a more indepth answer to what I can provide here (i.e. when I'm not taking a break from work) shoot me a comment over at my blog. I like a challenge and love collaborative work.

Updated on June 04, 2022

Comments

  • wheaties
    wheaties almost 2 years

    I have the following code:

    typedef boost::variant<LandSearchParameter, WaterSearchParameter> SearchParameter;
    
    enum Visibility{
        CLEAR,
        CLOUDY,
        FOG,
        SMOKE
    };
    
    class DetectionGenerator : public boost::static_visitor<double>{
    public:
    
        DetectionGenerator(const EnvironmentalFactors& factors);
    
        double operator()(const LandSearchParameter& land, Visibility vis) const;
        double operator()(const WaterSearchParameter& water, Visibility vis) const;
    
    private:
    
        const EnvironmentalFactors mFactors;
    };
    

    but if I try to use it with boost::apply_visitor in the following manner:

    SearchParameter param = globeCover.generateSearch(lat, lon, altitude);
    Visibility vis = weather.generateVisibility(lat, lon, altitude, bearing);
    DetectionGenerator detectGen(envFactors);
    double prob = boost::apply_visitor(detectGen, param, vis);
    

    and get the following from gcc:

    error: no matching function for call to ‘apply_visitor(const SearchRescue::DetectionGenerator&, const boost::variant<boost::tuples::tuple<double, double, double, double, double, bool, bool, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>, boost::tuples::tuple<std::size_t, std::size_t, double, double, double, bool, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_>&, SearchRescue::Visibility)

    If I attempt to wrap the Visibility enum within a boost::variant I get the same error only instead of Visibility it reads all that junk above and whatever name I chose for the variant. I've read over the docs on boost for binary visitation but I'm at a loss. Due note, all these things are within the same namespace.

    Update:

    It was my attempt that was the problem. Not shown above was that I had the visitor as a const variable. Once I took the const out of the picture, it compiled. Thank you all for trying to help me out. Wish I could give more upvotes.

  • wheaties
    wheaties about 13 years
    That is not correct. You need to take a look at the documentation about binary visitation: boost.org/doc/libs/1_46_1/doc/html/variant/…
  • Boaz Yaniv
    Boaz Yaniv about 13 years
    Read my edit - binary visitation is only for visiting two variants of the same type, or two variants of different types. It's not useful for visiting a variant and a non-variant, and it's not a good idea to make Visibility part of a variant just for that.
  • wheaties
    wheaties about 13 years
    Still get the same error. See what I wrote above. However, thank you for trying to help.
  • Boaz Yaniv
    Boaz Yaniv about 13 years
    Very strange indeed... It can't be you forgot to include the "<boost/variant/apply_visitor.hpp> header by chance, right? A code like I described above is definitely working for me, though not on GCC.
  • Boaz Yaniv
    Boaz Yaniv about 13 years
    BTW, you've said you get the same error as above? You shouldn't get the same one with my code. The last part (SearchRescue::Visibility) definitely can't be there if you don't call boost::apply_visitor with a Visibility parameter.
  • wheaties
    wheaties about 13 years
    @Boaz Yaniv If I name the typdef boost::variant<Visibility> VarVis I get the message such like it appears but with "VarVis" substituted for "Visibility". As to the include, I've got #include <boost/variant.hpp> which should include it all, no?
  • wheaties
    wheaties about 13 years
    btw, +1 for trying to help. Always glad to have another set of eyes on the problem.
  • wheaties
    wheaties about 13 years
    You're right. It was my approach. The visitor was a const variable and that, ultimately, is what caused the issue. I gave you the answer since you presented a version of the code which compiled, forcing me to diff your code from mine. Thanks.