0

I've now the following code snipplet in a class:

DrvClassA *drv_a_obj;
DrvClassB *drv_b_obj;
DrvClassC *drv_c_obj;

if ( use_drv_a ) {
   drv_a_obj = new DrvClassA(args);
}
if ( use_drv_b ) {
   drv_b_obj = new DrvClassB(args);
}
if ( use_drv_c ) {
   drv_c_obj = new DrvClassC(args);
}

and want to convert it somehow, to use only one variable for the created driver instance:

   this->Drv = new DrvClassA(args);
}
if ( use_drv_b ) {
   this->Drv = new DrvClassB(args);
}
if ( use_drv_c ) {
   this->Drv = new DrvClassC(args);
}

I thought about creating an union like this, but I'm getting errors upon compile.

error: cannot convert 'DrvClassA*' to 'MainClass::DRIVERS*' in assignment

union DRIVER {
      DrvClassA *drv_a_obj;
      DrvClassB *drv_b_obj;
      DrvClassC *drv_c_obj;
}

DRIVER *Drv;

Is this achievable somehow? What am I missing?

timrau
  • 22,578
  • 4
  • 51
  • 64
V1pr
  • 103
  • 2
  • 9

3 Answers3

2

What you might be looking for is polymorphism:

Live sample

class Drv{
    public:
    virtual ~Drv(){};
};

class DrvClassA : public Drv{};
class DrvClassB : public Drv{};
class DrvClassC : public Drv{};

int main(){  
    Drv* a = new DrvClassA(); // Drv a = DrvClassA();
    Drv* b = new DrvClassB(); // Drv b = DrvClassB();
    Drv* c = new DrvClassC(); // Drv c = DrvClassC();
}

Better yet use smart pointers to avoid memory leaks:

Live sample

#include <memory>

using std::unique_ptr;

class Drv{
    public:
    virtual ~Drv(){};
};

class DrvClassA : public Drv{};
class DrvClassB : public Drv{};
class DrvClassC : public Drv{};

int main(){ 
    unique_ptr<Drv> a(new DrvClassA);
    unique_ptr<Drv> b(new DrvClassB);
    unique_ptr<Drv> c(new DrvClassC);
}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
2

Yes, you can have a union of pointers.

What am I missing?

You've failed to specify which member of the union to assign. Correct syntax:

this->Drv->drv_a_obj = new DrvClassA(args);

P.S. You may later be interested in which of the union members was assigned. You'll find that this is not possible. The solution for that is to us a tagged union instead, such as std::variant.

P.P.S: A possibly better design would be to use inheritance. this->Drv could be a pointer to base object instead of a union.

P.P.P.S. Don't use owning bare pointers. You'll end up with memory leaks and undefined behaviour. Use RAII containers and smart pointers instead.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • if I use `this->Drv->drv_a_obj`, than I won't be able to call functions of the created class like this `this->Drv->setOption(arg)`, will I? The only option will be left for me is to use inheritance, I think. – V1pr Feb 06 '20 at 17:46
  • @V1pr Indeed not. You'd need to use `this->Drv->drv_a_obj->setOption(arg)` – eerorika Feb 06 '20 at 17:47
2

union is mostly only needed to implement variant.

So you might use:

using DRIVER = std::variant<std::unique_ptr<DrvClassA>,
                            std::unique_ptr<DrvClassB>,
                            std::unique_ptr<DrvClassC>>;

// or
//using DRIVER = std::variant<DrvClassA*, DrvClassB*, DrvClassC*>;

with usage

DRIVER Drv;
// ...
Drv = std::make_unique<DrvClassA>(args);
// ...
std::visit(overloaded{[](const std::unique_ptr<DrvClassA>& ptr){ ptr->funcA(); },
                      [](const auto& ptr){ ptr->common_interface(); }}, // DrvClassB or DrvClassC
           Drv);

But Polymorphism might be more appropriate:

struct DRIVER
{
    virtual ~DRIVER() = default;
    virtual void some_common_function() = 0;
};
struct DrvClassA : DRIVER
{
    void some_common_function() override { /*..*/ }
};
struct DrvClassB : DRIVER
{
    void some_common_function() override { /*..*/ }
};
struct DrvClassC : DRIVER
{
    void some_common_function() override { /*..*/ }
};

and then

std::unique_ptr<DRIVER> Drv;
// ...
Drv = std::make_unique<DrvClassA>(args);

Drv->some_common_function();
Jarod42
  • 203,559
  • 14
  • 181
  • 302