Factory возвращает объекты boost::visitor

Я хочу создать фабрику посетителей, которая возвращает объект любого из этих типов:

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;
    }
};

Я предположил, что могу вернуть указатель типа boost::static_visitor* на объект, созданный фабрикой, но это не компилируется. Я также не могу иметь ссылку на объект, как показано ниже:

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

Ошибка, которую я получаю:

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 

Как мне реализовать мою фабрику?


person Baz    schedule 19.11.2012    source источник


Ответы (1)


Посетители работают с перегрузкой, а значит являются статическим компонентом. Фабрика всегда (?) подразумевает компонент времени выполнения. Во-первых, важно определить, все ли производные посетители используют один и тот же набор перегрузок. Если это так, вы можете использовать эту схему:

#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;
}

Если ваши производные посетители предназначены только для применения к подмножествам возможных типов в варианте, вы не можете использовать чисто виртуальные функции и нуждаетесь в каком-то механизме сообщения об ошибке для обработки значения в базе (например, исключение).

Конечно, несколько функций C++11, использованных в примере кода, легко заменить.

#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;
}
person pmr    schedule 19.11.2012
comment
Спасибо! Я также оставил интерфейс, который можно использовать для решения этой проблемы. Однако я обращаюсь только к подмножествам возможных типов, как вы упомянули. Я планировал использовать шаблоны, чтобы поймать все типы, которые не имеют отношения к конкретной реализации посетителя, но вы не можете смешивать виртуальные и шаблоны. Как я могу решить эту проблему или мне нужно вызывать утверждения для каждого типа, который не имеет отношения к конкретной реализации посетителя. - person Baz; 19.11.2012
comment
Вы имеете в виду, что я должен заменить каждую чистую виртуальную функцию в базовом классе виртуальной функцией, которая создает исключение? - person Baz; 19.11.2012
comment
@Baz Хотя вы не можете сделать вызов шаблонного виртуального operator(), вы можете включить его в область своих производных посетителей с помощью using visitor_base::operator(). - person pmr; 19.11.2012
comment
@pnr Не могли бы вы отредактировать свой ответ, чтобы продемонстрировать это на простом примере? Спасибо! - person Baz; 19.11.2012
comment
@Baz Я добавил второй пример, который показывает универсальный случай. Здесь многое зависит от вашего конкретного варианта использования. Покрывают ли все производные функторы одни и те же перегрузки или есть только частичное перекрытие и т. д. Это может стать довольно хрупким. - person pmr; 19.11.2012