Добавление функций-членов в Boost.Variant - PullRequest
3 голосов
/ 02 марта 2010

В моей библиотеке C ++ у меня есть тип boost::variant<A,B> и множество алгоритмов, получающих этот тип в качестве входных данных. Вместо функций-членов у меня есть глобальные функции этого типа, такие как void f( boost::variant<A,B>& var ). Я знаю, что этого также можно добиться с помощью шаблонов, но это не подходит для моего дизайна.

Я очень хорошо с этим стилем программирования:

boost::variant<A, B> v;
f( v );

но некоторые пользователи этой библиотеки к этому не привыкли, и поскольку концепция Boost.Variant скрыта определением типа, им кажется, что нужно вызывать v.f().

Чтобы достичь этого, я могу представить две возможности: 1) переопределение boost::variant и 2) повторную реализацию boost::variant и добавление собственных функций-членов. Я не уверен, хороши ли эти идеи или нет. Можете ли вы помочь мне с этим, пожалуйста? Есть ли другие возможности?

Ответы [ 2 ]

5 голосов
/ 02 марта 2010

Другая возможность: использовать агрегацию. Тогда вы не будете напрямую предоставлять файл boost.variant пользователям библиотеки, предоставляя вам возможность большей свободы для будущих улучшений, и можете значительно упростить некоторые задачи отладки.

Общие рекомендации : Агрегация менее тесно связана, чем наследование, поэтому лучше по умолчанию, за исключением того, что вы знаете вариант использования, в котором вы явно хотите передать экземпляр вашего объекта уже существующим функциям только с вариантами. И даже если базовый класс должен был быть разработан с учетом наследования.

Пример агрегации для вашей проблемы : Насколько я понимаю, свободные функции уже существуют, и берут вариант. Просто определите класс с единственным членом данных варианта и предоставьте открытые функции-члены, которые ничего не делают, но вызывают уже существующие свободные функции с вариантом члена, как

class variant_wrapper {
  boost::variant<A,B> m_variant;
public:
  variant_wrapper(...) : m_variant(...) {} // whatever c_tor you need.
  void f() {  f(m_variant); }
};

Используя этот подход, вы абстрагируете тот факт, что вы используете boost.variant для своей реализации (что вы уже делаете с помощью typedef для пользователей библиотеки), давая вам возможность позже изменить это (для оптимизации или расширения функций или что угодно), вы можете решить сделать значения неизменяемыми, использовать более простой подход для отладки доступа к вашим алгоритмам и т. д. и т. д.

Недостаток агрегации состоит в том, что вы не можете просто передать оболочку static_visitor, но поскольку ваши пользователи не должны знать, что существует вариант, и вы знаете, что нужно просто передать переменную-член, я не вижу большой проблемы здесь.

Финальная напыщенная речь: C ++ не является Java. Вам нужно исправить пользователей библиотеки ...

Что бы вы хотели иметь, это методы расширения C #; таких вещей не существует в C ++. Тем не менее, я бы не переопределение / реализация-копия boost.variant (бремя обслуживания), и я бы не наследовать от него. По возможности используйте агрегацию.

2 голосов
/ 02 марта 2010

Я бы получил от boost :: варианта. Это должно быть хорошо, если вы не добавляете элементы данных в класс и не добавляете виртуальные функции. (Вы можете сделать некоторые из них, но я думаю, что дела обстоят немного сложнее). В любом случае, мне кажется, это работает нормально.

#include "boost/variant.hpp"
#include <iostream>

template<typename T1, typename T2>
struct my_print : public boost::static_visitor<>
{
  void operator()( T1 t1 ) const
  {
    std::cout<<"FIRST TYPE "<<t1<<std::endl;
  }
  void operator()( T2 t2 ) const
  {
    std::cout<<"SECOND TYPE "<<t2<<std::endl;
  }
};

template<typename T1, typename T2>
class MyVariant : public boost::variant<T1,T2>
{
  public:
    void print()
    {
      boost::apply_visitor(my_print<T1,T2>(), *this );
    }

    template<typename T>
    MyVariant<T1,T2>& operator=(const T& t)
    {
      boost::variant<T1,T2>::operator=(t);
      return *this;
    }

    MyVariant(const T1& t) : boost::variant<T1,T2>(t)
    {
    }

    MyVariant(const T2& t) : boost::variant<T1,T2>(t)
    {
    }

    template<typename T>
    explicit MyVariant(const T& t) : boost::variant<T1,T2>(t)
    {
    }
};

int main()
{
  MyVariant<int,std::string> s=1;
  s.print();
  s=std::string("hello");
  s.print();

  MyVariant<int,std::string> v2 = s;
  v2.print();

  s=boost::variant<int,std::string>(3);
  s.print();
}
...