1

I became a bit confused.

namespace Io
{
    class IDevice;
}

//...

namespace Sensor
{
    class IDevice;
}

//...

class ComplexDeviceHandler : public Io::IDevice, public Sensor::IDevice;

//...

std::vector<std::shared_ptr<Io::IDevice>> devices; //populated by objects of type ComplexDeviceHandler

//..

for (const auto& device : devices)
{
    std::shared_ptr<Sensor::IDevice> sensor = device; //obviously, error here
}

Both Io::IDevice and Sensor::IDevice are interfaces (sort of). What cast should I use to convert std::shared_ptr<Io::IDevice> to std::shared_ptr<Sensor::IDevice>. In this case, std::shared_ptr<Io::IDevice> stores an address of object of type ComplexDeviceHandler, which is a child of both types.

CorellianAle
  • 645
  • 8
  • 16
  • 1
    You can [cast](http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast) appropriately – Cory Kramer Feb 15 '18 at 15:30
  • Any reason to not have `std::vector> devices;`? – NathanOliver Feb 15 '18 at 15:31
  • Assuming IDevice as the virtual base class and IO is the namespace here. – seccpur Feb 15 '18 at 15:34
  • @NathanOliver There are other classes as ComplexDeviceHandler. Vector of pointer to the base class allows me to store objects of different type in one collection. – CorellianAle Feb 15 '18 at 15:34
  • @CorellianAle Do those other classes also inherit from `Sensor::IDevice`? – NathanOliver Feb 15 '18 at 15:35
  • @NathanOliver Most of them inherit from both, but some only from `Io::IDevice` – CorellianAle Feb 15 '18 at 15:36
  • Looks like [this](https://stackoverflow.com/questions/7426422/multiple-inheritance-casting-from-base-class-to-different-derived-class) answers the question. You need to know the derived type and cast to it before you can cast to the other parent. Not sure if it should be dupe closed though. – NathanOliver Feb 15 '18 at 15:42
  • @CorellianAle I know this isn't a solution, but given what you're trying to do, perhaps you should consider redesigning. You can certainly slice `ComplexDeviceHandler` into `Io::IDevice` and `Sensor::IDevice`. However, if there is no relationship between `Io::IDevice` and `Sensor::IDevice`, you shouldn't be casting from one to the other. So, one way to resolve this is to collect the interface that is common to both `Io::IDevice` and `Sensor::IDevice` in another class, say `Generic::IDevice`, then have `Io::IDevice` and `Sensor::IDevice` inherit `Generic::IDevice`. –  Feb 15 '18 at 15:44
  • @user934063 Yes, you are right. I would, eventually, but right now I just need to make it work. – CorellianAle Feb 15 '18 at 15:51
  • @CorellianAle Well, because you have two interfaces, `Sensor::IDevice` and `Io::IDevice`, it tells me that the two interfaces are not identical and you (kind of indirectly) confirmed that there is no relationship between them (in terms of inheritance). So short of some voodoo hack (say a c-style cast?), I am not sure you can do this -- and even then, I am not sure it would work (depends on the exact layout of the two interfaces). –  Feb 15 '18 at 15:56
  • Yea, I created this question because C-style cast is way too vodoo'ish and c++-unfriendly. – CorellianAle Feb 15 '18 at 16:43

2 Answers2

4

You need to first try to cast it to ComplexDeviceHandler*, check if it worked, and then it will be convertible to Sensor::IDevice*:

for (const auto& device: devices)
{
    if (auto sidptr = std::dynamic_pointer_cast<ComplexDeviceHandler>(device)) {
        std::shared_ptr<Sensor::IDevice> sensor = sidptr;
    }
}
Holt
  • 36,600
  • 7
  • 92
  • 139
  • I doubt this will work: "std::shared_ptr sensor = sidptr" – Slava Feb 15 '18 at 15:48
  • @Slava It does - `ComplexDeviceHandler` derives from `Sensor::IDevice`, so `ComplexDeviceHandler*` is convertible to `Sensor::IDevice*` so there is constructor from `std::shared_ptr` to `std::shared_ptr`. – Holt Feb 15 '18 at 15:50
  • @Holt Yes, that would work, but I don't know how `std::dynamic_pointer_cast(device)` would not return `nullptr`. –  Feb 15 '18 at 15:52
  • Yes you are right, and it seems even my first variant was right (you can check pointer after assignment). – Slava Feb 15 '18 at 15:55
  • Sure, "check if it worked" is safe, but this doesn't solve the original problem. The original problem is "how do I make it work," not "check whether it works." –  Feb 15 '18 at 15:58
  • 1
    @user934063 I don't get your comment... This works perfectly fine if `device` points to a `ComplexDeviceHandler` and is safe if it does not. – Holt Feb 15 '18 at 15:59
  • @Holt You're right. I missed the line `std::shared_ptr` stores an address of object of type `ComplexDeviceHandler` in the OP. –  Feb 15 '18 at 16:04
0

This is a coding example based on Holt's answer, which worked for me. (Very cool, Holt! Educational.) Hopefully mimics the OP use case scenario. I used C++17 in particular.

#include <cstddef>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

using std::cout;
using std::dynamic_pointer_cast;
using std::endl;
using std::make_shared;
using std::move;
using std::shared_ptr;
using std::string;
using std::vector;

class IFoo
{
  virtual void DoFooStuff() = 0;
protected:
  ~IFoo() = default;
public:
  void FooStuff();
};

void IFoo::FooStuff()
{
  FooStuff();
}

class IBar
{
  virtual void DoBarStuff() const = 0;
protected:
  ~IBar() = default;
public:
  void BarStuff() const;
};

void IBar::BarStuff() const
{
  DoBarStuff();
}

class Baz : public IFoo
{
  virtual void DoFooStuff() override;
public:
};

void Baz::DoFooStuff()
{
  cout << "Baz's DoFooStuff\n";
}

class Quuxplex : public IFoo, public IBar
{
  virtual void DoFooStuff() override;
  virtual void DoBarStuff() const override;
  string s;
public:
  Quuxplex(string);
  void SecretSauce() const;
};

Quuxplex::Quuxplex(string sval) : s{move(sval)}
{ }

void Quuxplex::DoFooStuff()
{
  cout << "Quuxplex's DoFooStuff with " << s << "\n";
}

void Quuxplex::DoBarStuff() const
{
  cout << "Quuxplex's DoBarStuff with " << s << "\n";
}

void Quuxplex::SecretSauce() const
{
  cout << "Quuxplex's SecretSauce with " << s << "\n";
}

int main()
{
  vector<shared_ptr<IFoo>> things;
  things.push_back(make_shared<Quuxplex>("pickle"));
  things.push_back(make_shared<Baz>());
  things.push_back(make_shared<Quuxplex>("relish"));
  things.push_back(make_shared<Baz>());
  things.push_back(make_shared<Quuxplex>("mustard"));
  vector<shared_ptr<Quuxplex>> quuxplexes;

  int found = 0;
  for (auto const& thing : things)
  {
    if (auto q = dynamic_pointer_cast<Quuxplex>(thing))
    {
      ++found;
      q->SecretSauce();
      quuxplexes.push_back(q);
      cout << "Quuxplex objects found: " << found << " count: " <<  q.use_count() << endl;
    }
  }

  return EXIT_SUCCESS;
}
Eljay
  • 4,648
  • 3
  • 16
  • 27