2013-06-25 4 views
1

У меня полиморфного интерфейсуИспользование Boost.Range в интерфейсах C++

struct Interface { 
    Interface(SomeType& other) 
    : range([=](){ return other.my_range(); }), /*...*/ {} 
    Interface(SomeOtherType& other) 
    : range([=](){ return other.some_range(); }), /*...*/ {} 

    const std::function<Range(void)> range; 
    /// ... 
}; 

элементов в обоих диапазонах имеют одинаковый тип (например, int), но типы возвращаемых my_range() и some_range() различны, например, может быть filtered counting range, а другой transformed filtered counting range. Для интерфейса мне нужен один тип Range.

Я пробовал использовать boost::any_range, но производительность значительно хуже. Я хотел бы избежать необходимости копировать элементы диапазона в vector и вместо этого возвращать вектор.

Есть ли альтернативы any_range и копированию?

ответ

1

Вид, но не совсем.

Вы хотите получить доступ к данным последовательно, когда вы не знаете, как они хранятся. У вас есть три варианта:

  • Скопируйте данные в контейнер с известным форматом (опция «возврат вектора»).
  • Используйте полиморфизм времени компиляции, чтобы выбрать правильный метод доступа (способ, которым это делают стандартные алгоритмы std, что невозможно благодаря использованию интерфейса).
  • Используйте полиморфизм времени выполнения, чтобы выбрать правильный метод доступа.

Таким образом, второе невозможно из-за ограничения, которое вы хотите использовать с интерфейсом. Первая и третья приходят с накладными расходами.

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

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

struct Interface { 
    Interface(SomeType& other) 
    : traverse([=](std::function<void(int)> body) { 
     for (int i : other.my_range()) body(i); 
    }) {} 

    const std::function<void (std::function<void(int)>)> traverse; 
}; 

Конечно, это только работает поскольку способы использования диапазона очень ограничены.

+0

Спасибо за ответ, что алгоритмы обертывания в интерфейсе - это возможность, которую я не рассматривал. Кон является то, что после того, как алгоритм «for_each» приходит sort, binary_search, а затем ... взрыв функций-членов в интерфейсе. Возможно, этого не произойдет в моем приложении, поэтому то, что вы предлагаете, может быть лучшим решением (по крайней мере, в сочетании с any_range). Я буду спать на нем, спасибо за эту мысль! – gnzlbg

0

Если известны только 2 известных типа (или фиксированное количество типов), то альтернативой может быть Boost.Variant. Вот пример использования:

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

struct SomeType 
{ 
    typedef int range_t; 
    range_t my_range() const { return 1; } 
}; 

struct SomeOtherType 
{ 
    typedef double range_t; 
    range_t some_range() const { return 3.14; } 
}; 

typedef std::function<SomeType::range_t (void)> SomeTypeRange; 
typedef std::function<SomeOtherType::range_t (void)> SomeOtherTypeRange; 
typedef boost::variant<SomeTypeRange, SomeOtherTypeRange> Range; 

struct Interface 
{ 
    Interface(const SomeType& other) 
    : range(SomeTypeRange([=](){ return other.my_range(); })) {} 

    Interface(const SomeOtherType& other) 
    : range(SomeOtherTypeRange([=](){ return other.some_range(); })) {} 

    Range range; 
}; 

struct print_me_visitor : public boost::static_visitor<void> 
{ 
public: 
    void operator()(const SomeTypeRange& i_st) const 
    { 
     std::cout << "SomeTypeRange: " << i_st() << std::endl; 
    } 

    void operator()(const SomeOtherTypeRange& i_sot) const 
    { 
     std::cout << "SomeOtherTypeRange: " << i_sot() << std::endl; 
    } 
}; 

int main() 
{ 
    SomeType st; 
    SomeOtherType sot; 
    Interface i1(st); 
    Interface i2(sot); 

    boost::apply_visitor(print_me_visitor(), i1.range); 
    boost::apply_visitor(print_me_visitor(), i2.range); 

    return 0; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^