C ++ return vector of ptr interface

I am trying to create an interface that returns an interface vector using it on the other side.

I know it is not clear, so here are the examples:

class IComponent
{
  public:
    virtual ~IComponent() {}

    virtual int getValue() const = 0;
};

class ICollection
{
  public:
    virtual ~ICollection() {}

    virtual IComponent* getComp() = 0;
};

class Component : public IComponent
{
  public:
    virtual int getValue() const { return 42; }
};

class Collection : public ICollection
{
  public:
    Collection()
    {
      m_comp = new Component();
    }
    virtual Component* getComp()
    {
      return m_comp;
    }

  private:
    Component* m_comp;
};

      

This compilation works because it is Component*

implicitly converted to IComponent*

. This allows me to use fully implemented Collection

and Component

.

#include <vector>

class IComponent
{
  public:
    virtual ~IComponent() {}

    virtual int getValue() const = 0;
};

class ICollection
{
  public:
    virtual ~ICollection() {}

    virtual std::vector<IComponent*> &getComp() = 0;
};

class Component : public IComponent
{
  public:
    virtual int getValue() const { return 42; }
};

class Collection : public ICollection
{
  public:
    Collection()
    {
      m_comp.push_back(new Component());
      m_comp.push_back(new Component());
    }
    virtual std::vector<Component*> &getComp()
    {
      return m_comp;
    }

  private:
    std::vector<Component*> m_comp;
};

      

But this time, I cannot work with the fully implemented collection and component.

Of course, it's easy to see why, because there Component *

is IComponent *

, but strictly speaking std::vector<Component *>

not std::vector<IComponent *>

. This issue also applies to all types of templates, such as iterators, smart_ptr, etc.

Instead, Collection::getComp()

I need to return std::vector<IComponent *>

. I can possibly turn it on m_comp

by returning it (I'm not even sure if it works, wouldn't it be an rvalue?). I can also store 2 vector

, one of Component *

and one of IComponent *

, and keep them in sync, but that's ugly code I guess.

Is there some workaround, or was I somehow unaware of this?

ADDITIONAL INFORMATION:

I would really like to be able to use Component

in Collection

. If I change m_comp

to std::vector<IComponent *>

and the return type getComp()

works, but I am forced to use pointers to IComponent

and not Component

to Collection

.

So yes, I can sync two vectors, or create a copy in getComp()

, but I want to know if there is a better way to do this.

EVENT MORE DETAILS:

Here is an example of how I would like to use it: (I just added the method getDouble()

in Component

and called it in Collection

)

#include <iostream>
#include <vector>

class IComponent
{
  public:
    virtual ~IComponent() {}

    virtual int getValue() const = 0;
};

class ICollection
{
  public:
    virtual ~ICollection() {}

    virtual std::vector<IComponent*> &getComp() = 0;
};

class Component : public IComponent
{
  public:
    virtual int getValue() const { return 42; }
    double getDouble() const { return 3.14; }
};

class Collection : public ICollection
{
  public:
    Collection()
    {
      m_comp.push_back(new Component());
      m_comp.push_back(new Component());
    }
    // Impossible because it does not override the good method
    virtual std::vector<Component*> &getComp()
    {
      std::cout << m_comp[0]->getDouble() << std::endl;
      return m_comp;
    }

  private:
    std::vector<Component*> m_comp;
};

      

If I change the return type, the Collection becomes:

class Collection : public ICollection
{
  public:
    Collection()
    {
      m_comp.push_back(new Component());
      m_comp.push_back(new Component());
    }
    virtual std::vector<IComponent*> &getComp()
    {
      // This time this line is impossible without cast
      // because m_comp[0] is IComponent*
      std::cout << m_comp[0]->getDouble() << std::endl;
      return m_comp;
    }

  private:
    std::vector<IComponent*> m_comp;
};

      

One solution would be like this:

class Collection : public ICollection
{
  public:
    Collection()
    {
      // Useless and redundant code here
      m_comp.push_back(new Component());
      m_icomp.push_back(m_comp.back());
      m_comp.push_back(new Component());
      m_icomp.push_back(m_comp.back());
    }
    // Impossible because it does not override the good method
    virtual std::vector<IComponent*> &getComp()
    {
      std::cout << m_comp[0]->getDouble() << std::endl;
      return m_icomp;
    }

  private:
    // Duplicated storage for "nothing"
    std::vector<Component*> m_comp;
    std::vector<IComponent*> m_icomp;
};

      

In the answers I saw there was something in the implementation of the method pop()

and push()

, but in my project I use Map

instead of Collection

, and there is a vector of blocks. This allows you to access the block by executing map[y][x]

.

The problem is the repository is fixed. One solution would be to add a method instead getBlock(size_t x, size_t y)

. But I want to see what I could do in the first case :)

+3


source to share


3 answers


Not sure if this solution might work in your case, but ... what about passing a derived class Component

as a template parameter in ICollection

?

Not exactly curious about the repeating pattern, but something similar.

I mean, something like

template <typename CompT>
class ICollection
{
  public:
    virtual ~ICollection() {}

    virtual std::vector<CompT*> &getComp() = 0;
};

      



So Collection

become

class Collection : public ICollection<Component>
{
  public:
    Collection()
    {
      m_comp.push_back(new Component());
      m_comp.push_back(new Component());
    }
    virtual std::vector<Component*> &getComp()
    {
      return m_comp;
    }

  private:
    std::vector<Component*> m_comp;
};

      

or even if you convert Collection

to template class

template <typename CompT>
class Collection : public ICollection<CompT>
{
  public:
    Collection()
    {
      m_comp.push_back(new CompT());
      m_comp.push_back(new CompT());
    }
    virtual std::vector<CompT*> &getComp()
    {
      return m_comp;
    }

  private:
    std::vector<CompT*> m_comp;
};

      

+2


source


If you know that the elements of the return are vector

pointing to objects Component

, you can:

Collection coll;
std::vector<IComponent*>& v = coll.getComp();
Component& comp = *dynamic_cast<Component*>(v[0]);

      

If you only want virtual methods from the interface IComponent

, you don't even need to throw; you can just use dynamic dispatch to call them. But casting to a reference of the child type will give you whatever methods it adds.



I also suggest making your abstraction a little less leaky. You can access items through operator[]

, push()/pop()

or something makes sense. Then the internal storage can be any container of any pointer type, and the public interface should not change.

A less secure alternative is to go back to path C. You can take m_comp.data()

as IComponent**

, give it to, Component**

and return it.

+2


source


In any case, you need to change the return type Collection::getComp

to std::vector<IComponent*>&

, because otherwise it cannot override ICollection::getComp

. Since a std::vector<IComponent*>

can also contain pointers to Component

, just change your member to it and you should be fine.

If pointers to Component

, rather than its interface, are required somewhere, they can be disabled using dynamic_cast .

+1


source







All Articles