문제

I have a polymorphic interface

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

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

The elements in both ranges are of the same type (e.g. int), but the types returned by my_range() and by some_range() are different, e.g. one can be a filtered counting range and the other a transformed filtered counting range. For the interface I need a single Range type.

I've tried using boost::any_range but the performance is significantly worse. I would like to avoid having to copy the range elements into a vector and returning the vector instead.

Are there any alternatives to any_range and copying?

도움이 되었습니까?

해결책

Kind of, but not really.

You want to access data sequentially when you don't know how it's stored. You have three options:

  • Copy the data into a container with known format (the "return vector" option).
  • Use compile-time polymorphism to choose the correct access method (the way std algorithms do it, not possible due to you using an interface).
  • Use runtime polymorphism to choose the correct access method.

So the second is not possible due to the constraint that you want to use an interface. The first and the third both come with overhead.

The obvious way of doing the third thing is any_range. But it's not the only way, depending on what you want to do. The problem with any_range is that in a simple for-each loop, there are three virtual calls for every element: the increment, the comparison, and the dereference.

As long as all you want to do is simple for-each iteration, you could reduce the overhead to one virtual call by implementing the loop on the interface level:

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

Of course that only works as long as the ways you use the range are very limited.

다른 팁

If there are only known 2 known types (or fixed number of types), then alternative could be Boost.Variant. Here is sample usage:

#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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top