Question

I wish to create a visitor factory, which returns an object of either of these types:

class Visitor1: public boost::static_visitor<int>
{
public:
    int operator()(int& ) const 
    {
        return 1;
    }

    int operator()(bool& ) const 
    {
        return 1;
    }
};

class Visitor2: public boost::static_visitor<int>
{
public:
    int operator()(int& ) const 
    {
        return 2;
    }

    int operator()(bool& ) const 
    {
        return 2;
    }
};

I presumed that I could return a pointer of type boost::static_visitor* for the object created by the factory, but this doesn't compile. Nor can I have a reference to the object, as follows:

Visitor1 v;
Type t;
boost::static_visitor<int>& vR = v;
boost::apply_visitor(vR, t);

The error I get is:

see reference to function template instantiation 'int boost::apply_visitor<boost::static_visitor<R>,Type>(Visitor &,Visitable &)' being compiled
1>          with
1>          [
1>              R=int,
1>              Visitor=boost::static_visitor<int>,
1>              Visitable=Type
1>          ]
1>c:\boost_1_49_0\boost\variant\variant.hpp(858): error C2064: term does not evaluate to a function taking 1 arguments 

How should I implement my factory?

Was it helpful?

Solution

Visitors work with overloading, which means they are a static component. A Factory always (?) implies a runtime component. First it is important to determine if all derived visitors share the same set of overloads. If so, you can use this scheme:

#include <memory>

#include <boost/variant.hpp>
#include <boost/variant/static_visitor.hpp>

struct visitor_base : public boost::static_visitor<int>
{
  virtual int operator()(int) = 0;
  virtual int operator()(double) = 0;
  virtual int operator()(const char*) = 0;
  virtual ~visitor_base() {}
};

struct impl_1 : visitor_base {
  virtual int operator()(int) { return 1; }
  virtual int operator()(double) { return 1; }
  virtual int operator()(const char*) { return 1; }
};

struct impl_2 : visitor_base {
  virtual int operator()(int) { return 2; }
  virtual int operator()(double) { return 2; }
  virtual int operator()(const char*) { return 2; }
};

std::unique_ptr<visitor_base> visitor_factory(int i) {
  if(i == 1) {
    return std::unique_ptr<visitor_base>(new impl_1);
  } else {
    return std::unique_ptr<visitor_base>(new impl_2);
  }
}

int main()
{
  auto vis = visitor_factory(1);
  boost::variant<int, double, const char*> v = 3;
  std::cout << boost::apply_visitor(*vis, v) << std::endl;
  auto vis2 = visitor_factory(2);
  std::cout << boost::apply_visitor(*vis2, v) << std::endl;

  return 0;
}

If your derived visitors are only intended to apply to subsets of the possible types in the variant you cannot use pure virtual functions and need some mechanism of reporting failure to handle a value in the base (e.g. an exception).

Of course the few C++11 features used in the example code are easy to replace.

#include <memory>
#include <string>

#include <boost/variant.hpp>
#include <boost/variant/static_visitor.hpp>

struct visitor_base : public boost::static_visitor<int>
{
  // the catch all
  template <typename T>
  int operator()(const T& t) {
    return 0;
  }

  virtual int operator()(int) {}
  virtual int operator()(double) {}
  virtual int operator()(const char*) {}

  virtual ~visitor_base() {}
};

struct impl_1 : visitor_base {
  virtual int operator()(int) { return 1; }
  virtual int operator()(double) { return 1; }
  virtual int operator()(const char*) { return 1; }
};

struct impl_2 : visitor_base {
  virtual int operator()(int) { return 2; }
  virtual int operator()(double) { return 2; }
  virtual int operator()(const char*) { return 2; }
};

std::unique_ptr<visitor_base> visitor_factory(int i) {
  if(i == 1) {
    return std::unique_ptr<visitor_base>(new impl_1);
  } else {
    return std::unique_ptr<visitor_base>(new impl_2);
  }
}

int main()
{
  auto vis = visitor_factory(1);
  boost::variant<int, double, const char*> v = 3;
  std::cout << boost::apply_visitor(*vis, v) << std::endl;
  auto vis2 = visitor_factory(2);
  std::cout << boost::apply_visitor(*vis2, v) << std::endl;

  // a variant with more than impl_1 and impl_2 can catch
  boost::variant<int, double, const char*, std::string> vv = std::string{"asdf"};
  std::cout << boost::apply_visitor(*vis2, vv) << std::endl;
  // no use one that we handle
  vv = 3;
  std::cout << boost::apply_visitor(*vis2, vv) << std::endl;
  return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top