1

Please have a look at the following code, you can compile it as a single .cpp file but you should imagine base library code in a separate package:

#include <iostream>
#include <memory>
#include <vector>

// Start of classes defined in a base library shared by many projects

class BaseObject
{
    public:

        BaseObject() : value(0) {}

        BaseObject(int val) : value(val) {}

    protected:

        int value;
};

class BaseObjectHandler
{
    public:

        void setVector(std::unique_ptr<std::vector<BaseObject>>&& v)
        {
            vec = std::move(v);
        }

    protected:

        std::unique_ptr<std::vector<BaseObject>> vec;
};

// End of classes defined in a base library shared by many projects

// Start of specific implementation for project A

class SpecificObject : public BaseObject
{
    public:

        SpecificObject() : BaseObject(0) {}
};

class SpecificObjectHandler : public BaseObjectHandler
{
    public:

        void doSomethingOnVector()
        {
            // Do something on vector
        }
};

int main()
{
    SpecificObjectHandler handler;

    handler.setVector(std::make_unique<std::vector<SpecificObject>>());

    handler.doSomethingOnVector();

    exit(0);
}

// End of specific implementation for project A

BaseObjectHandler can share some common operations to its derived handlers, but knows nothing about SpecificObject, because it resides in a separate package base library.

In turn SpecificObjectHandler resides in project A package, knows about SpecificObject of course, and needs the above mentioned common operations that do not act on SpecificObject instances but rely only on BaseObject.

The code obviously does not compile because of error conversion from std::vector<SpecificObject> to std::vector<BaseObject>.

Is there a simple and elegant C++ 14 way to solve this issue? Or do you suggest a completely different pattern for this problem?

I found some answers to similar problems but I need more specific clarification.

Thanks.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
Pampo
  • 19
  • 3
  • `std::unique_ptr>` is kind of the opposite of what you want. To make it work you need a vector of *pointers to the base object*, or `std::vector>`. – Some programmer dude Jan 18 '21 at 11:36
  • And instead of a `setVector` function I suggest you create an `addObject` (or similar) function to add one object to the existing vector. – Some programmer dude Jan 18 '21 at 11:38
  • No, I would like to keep a vector of objects, not of unique pointers. I can directly pass a vector instead of a unique_ptr, but the problem remains. – Pampo Jan 18 '21 at 11:39
  • Using vector serves just as an example, I would have the same issue with any templated class to be passed to handler, I'd like to have a more general answer to this problem. – Pampo Jan 18 '21 at 11:41
  • 1
    First of all you almost *never* need a pointer to a container, and in this case it really makes no sense. Secondly you need to read up on [*object slicing*](https://stackoverflow.com/questions/274626/what-is-object-slicing), while `SpecificObject` is-a `BaseObject`, the opposite is not true. Lastly, if all you have is a `BaseObject` instance you can't use any kind of polymorphism, which needs pointers or references to work. – Some programmer dude Jan 18 '21 at 11:42
  • 1
    I think it would be better if you asked us about the *actual* problem you're trying to solve with code like this. Why do you need it? Why do you think something like this is the only possible solution to that problem? Perhaps there are other possible solutions? – Some programmer dude Jan 18 '21 at 11:45
  • @Pampo *No, I would like to keep a vector of objects* Not if you want to be able to use them polymorphicly. That's not possible. – super Jan 18 '21 at 12:04
  • you also cannot convert a `SpecificObject` to a `BaseObject`. Read about object slicing. You need to store pointers when you want a polymorphic vector – 463035818_is_not_an_ai Jan 18 '21 at 12:14
  • As I wrote below it's hard for me to share my actual code. In my case I don't get into object slicing problem because BaseObjectHandler only performs operations unrelated to derived objects. Why do you say "you almost never need a pointer to a container"? – Pampo Jan 18 '21 at 12:54
  • you almost never need a pointer to a container, because standard containers are not meant to be used polymorphically, also containers already do manage the memory of their elements, hence wanting to use the heap is also no reason to deal with pointers to containers – 463035818_is_not_an_ai Jan 18 '21 at 13:12

1 Answers1

1

Assuming you can edit the base library code, you could define BaseObjectHandler as a template class

// base_class_library.hpp

template<typename ObjectType>
class BaseObjectHandler
{
    public:

        void setVector(std::unique_ptr<std::vector<ObjectType>>&& v)
        {
            vec = std::move(v);
        }

    protected:
        std::unique_ptr<std::vector<ObjectType>> vec;
};

and inherit class SpecificObjectHandler : public BaseObjectHandler<SpecificObject>. Now SpecificObjectHandler has pointer to vector of SpecificObjects and your code would compile.

Of course it may lead to conflicts with other uses of the base library, but whenever you need the old (your) version of BaseObjectHandler you can declare it as BaseObjectHandler<BaseObject>. As a price, you would loose a possibility to upcast SpecificObjectHandler into generic BaseObjectHandler, but maybe that is not important for you.

Raddeo
  • 161
  • 1
  • 6
  • This is a good answer, thank you. But this is not my actual code, it would be rather complicated for me to share it here. From a logic perspective, I wouldn't like to add a sort of dependency from Object (Base or not) into BaseObjectHandler, because in my real case it does more than only handling such objects. Any other suggestions? – Pampo Jan 18 '21 at 12:51
  • You could change `vec` inside `BaseObjectHandler` into a vector of pointers to `BaseObject` (or any other pointer to be honest) and recast them inside `SpecificObjectHandler` but you would have to store the objects somewhere else. Another approach, if `SpecificObject` extends `BaseObject` only by new functions but not by new variables, you can just take pointer to `BaseObject` in `SpecificObjectHandler` and recast it to `SpecificObject*`, bu tthis would work only in very special case. Nothing else comes to my mind. – Raddeo Jan 18 '21 at 13:13