Search code examples
c++void-pointersfactory-method

How to change pointer from base to derived class inside a function


I would like to create some kind of factory function, but instead of returning a pointer to a certain class object, I would like to pass a pointer to the base class to a function as an argument. What makes it even more cumbersome, the function accepts the void pointer.

Here is the code I have tried.

#include <iostream>
#include <string>

class Base {
    public:
    std::string isA()
    {
        return "Base";
    }
};

class Derived : public Base {
    public:
    std::string isA()
    {
        return "Derived";
    }
};


void func(void* obj)
{
    Derived* derived_ptr = (Derived*)obj;
    std::cout << "Address of derived_ptr " << derived_ptr << "\n";
    std::cout << "From func " << derived_ptr->isA() << "\n";

}

int main()
{
  Base* instance;
  func(instance);
  std::cout << "Address of instance " << instance << "\n";
  std::cout << "From main " << instance->isA() << "\n";
}

Output:

Address of derived_ptr 0x4009b0                                                                                                               
From func Derived                                                                                                                             
Address of instance 0x4009b0                                                                                                                  
From main Base   

From the output it is obvious that the addresses are the same but in each case it is different object (Base and Derived. Is there a way to get it working in this form?


Solution

  • If you do not want run time polymorphism via virtual functions, you can use std::variant instead. This enables you to use unrelated objects without a common base class, it did not need to enable RTTI which means it will fit also for small embedded systems and it still type safe as std::variant keeps the information which object is currently stored inside the variant variable.

    You also can store such variants in containers as you like. If you use a std::variant with a pointer type, the potential waste by using this kind of union is only the additional pointer instance.

    But yes, it has another indirection by using the pointer, but in your example you also have a void* and "some mind" of check and conversion like a cast.

    class Base {
        public:
            std::string isA()
            {
                return "Base";
            }
    };
    
    class Derived : public Base {
        public:
            std::string isA()
            {
                return "Derived";
            }
    };
    
    class Another
    {
        public:
            std::string isA() { return "Another"; }
    };
    
    using VAR_T = std::variant< std::unique_ptr<Base>, std::unique_ptr<Derived>, std::unique_ptr<Another> >;
    
    VAR_T Factory( int what )
    {
        switch ( what )
        {   
            case 0: return VAR_T{std::make_unique<Base>()}; 
            case 1: return VAR_T{std::make_unique<Derived>()}; 
            case 2: return VAR_T{std::make_unique<Another>()}; 
        }   
    
        return {}; 
    }
    
    /* Never do such thing in real world code!!!!!!!!!!!!!!!!! */
    
    void func(void* obj)
    {
        VAR_T* var = reinterpret_cast< VAR_T* >( obj );
        std::string s = std::visit( []( auto& ptr ){ return ptr->isA(); }, *var);
        std::cout << "From func " << s << "\n";
    }
    
    
    
    int main()
    {
        std::vector< VAR_T > vars;
    
        for ( int i = 0; i<3; i++)
        {   
            vars.push_back( Factory( i )); 
        }   
    
        for ( auto& var: vars )
        {   
            std::visit( []( auto& ptr ){ std::cout << ptr->isA() << std::endl; }, var );
        }   
    
        // and now via void*, but never use this in production code. DANGEROUS, NOT TYPE SAFE, 
        for ( auto& var: vars )
        {   
            func( &var );
        }  
    
    }
    

    EDIT: Added code with using void*, BUT THIS SHOULD REALLY NEVER BE PART OF ANY PRODUCTION SYSTEM! void* is always a nightmare!