Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design a base iterator for a base class

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.

like image 575
tea2code Avatar asked Mar 20 '23 06:03

tea2code


1 Answers

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)}); }
};
like image 171
Angew is no longer proud of SO Avatar answered Mar 22 '23 21:03

Angew is no longer proud of SO