Domanda

I'm currently designing an interface (Base in the following example) which offers a couple of virtual methods including begin() and end(). These two methods simple return the corresponding iterator like in any other collection like class. Derived classes should implement these methods and return their specific implementation of an iterator.

The following (simplified) example shows a derived class which uses a boost::transform_iterator to convert the private internal list of integers. This implementation is only an example in reality the iterated "thing" can be something else and so does the iterator.

The example works but i have one problem. The object type in main() doesn't hide the fact that the used iterator is of type TransformIterator. The base class will be used in some kind of plugin architecture where every plugin is a shared library. The plugins shouldn't know which type of iterator is used and should solely depend on the abstract interface. Is there a way to do this?

#include <boost/iterator/transform_iterator.hpp>

#include <iostream>
#include <string>
#include <vector>

template<class Iterator>
class Base
{
public:
    virtual Iterator begin() = 0;
    virtual Iterator end() = 0;
};

struct toString
{
    std::string operator()(int number) const
    {
        return std::to_string(number);
    }
};

typedef boost::transform_iterator<toString, std::vector<int>::iterator> TransformIterator;

class Derived : public Base<TransformIterator>
{
public:
    Derived() : ints{132, 3, 6451, 12, 5} {}

    TransformIterator begin()
    {
        return boost::make_transform_iterator(ints.begin(), toString());
    }

    TransformIterator end()
    {
        return boost::make_transform_iterator(ints.end(), toString());
    }

private:
    std::vector<int> ints;
};

int main()
{
    Base<TransformIterator>* obj = new Derived();
    for(const auto& value : *obj)
    {
        std::cout << value.size() << std::endl;
    }
    return 0;
}

A little bit more background: This specific example is based on an interface which reads configuration files. Currently i only plan to give a implementation for YAML files but other formats like XML or old school INI are also possible. Thus a common interface.

È stato utile?

Soluzione 2

Not with classic C++ iterators, no. They aren't intended for polymorphic use.

What you can do is define an abstract base class for your iterators, which is then implemented by a (templated) wrapper around each concrete iterator type. The abstract base class simply defines all required operators as pure virtual. The downside is every operation on the iterator will need a virtual function call... Depending on your use this may or may not become an issue (usually not unless you use them to frequently iterate over very large collections).

Example:

template <typename T>
class InputIterator<T>
{
  public:
    virtual T operator*() const = 0;

    // Other operators go here
};

template <typename TIterator>
class ConcreteInputIterator final
:  public InputIterator<typename std::iterator_traits<TIterator>::value_type>
{
  public:
    ConcreteInputIterator(TIterator iterator) : m_iterator(iterator) {}

  public:
    virtual T operator*() const override
    {
      return *m_iterator;
    };

  private:
    TIterator m_iterator;
};

Altri suggerimenti

I recently did something very similar in one of my projects at work. The way I did it was introduce an abstract iterator interface and a class for the iterator accessing that interface. Here's a simplified version:

template <class ValueType>
struct AbstractIterator
{
  virtual ValueType& dereference() const = 0;
  virtual void increment() = 0;
  // and so on...
};


template <class ValueType>
struct ClientIterator : public std::iterator<whatever>
{
  std::unique_ptr<AbstractIterator<ValueType>> it;

  ClientIterator(std::unique_ptr<AbstractIterator<ValueType>> it) : it(std::move(it)) {}

  ValueType& operator* const () { return it->dereference(); }
  ClientIterator& operator++ () { it>increment(); return *this; }

  // and so on...
};

struct Base
{
  typedef ClientIterator<std::string> Iterator;
  virtual Iterator begin() = 0;
  virtual Iterator end() = 0;
}


struct Derived : Base
{
  struct ServerIterator : AbstractIterator<std::string>
  {
    boost::transform_iterator<toString, std::vector<int>::iterator> it;

    virtual std::string& dereference() const override { return *it; }
    virtual void increment() override { ++it; }
  }

  virtual Iterator begin() override { return Iterator({new ServerIterator(something)}); }
  virtual Iterator end() override { return Iterator({new ServerIterator(anotherThing)}); }
};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top