Search code examples
c++smart-pointersvoid-pointersreinterpret-cast

Testing if a void* passed into a function is either a shared_ptr or a unique_ptr


I am working on creating a function for a class and the parameter is declared as a void* however within the function I need to test if this void* is either a shared_ptr or unique_ptr is there a way to test for this type of situation?

This is what I am working with so far; my class is a template type and does not store any member variables. It has a default constructor and it can also be constructed by passing in either a shared_ptr<Type> or an unique_ptr<Type> and it is has multiple allocate() functions where they do the same type of work.

#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include <memory>
#include <iostream>

template<class Type>
class Allocator {
public:
    Allocator(){}
    Allocator( Type type, void* pPtr );
    Allocator( std::shared_ptr<Type>& pType );
    Allocator( std::unique_ptr<Type>& pType );
    // ~Allocator(); // Default Okay

    void allocate( std::shared_ptr<Type>& pType );
    void allocate( std::unique_ptr<Type>& pType );
    void allocate( Type type, void* pPtr );

private:
    Allocator( const Allocator& c ); // Not Implemented
    Allocator& operator=( const Allocator& c ); // Not Implemented

}; // Allocator

#include "Allocator.inl"

#endif // ALLOCATOR_H

My *.cpp file only has #include "Allocator.h" since all of the implementations are within my *.inl file.

My two constructors: Allocator( std::shared_ptr<Type>& pType ); & Allocator( std::unique_ptr<Type>& pType ); along with both matching allocate() functions work fine. The constructor Allocator( Type type, void* pPtr ); and its matching function is where I am having trouble.

The constructor itself is straight forward since all it does is invoke the matching function passing to it the variables.

template<class Type>
Allocator<Type>::Allocator( Type type, void* pPtr ) {
    allocate( type, eType, pPtr );
}

It is within the function implementation that I am struggling.

template<class Type>
void Allocator<Type>::allocate( Type type, void* pData ) {
    if ( pData == reinterpret_cast<void*>( std::shared_ptr<Type ) ) {
        std::shared_ptr<Type> pShared;
        pShared.reset( new Type( type ) );
        pData = reinterpret_cast<void*>( pShared );

    } else if ( pData == reinterpret_cast<void*>( std::unique_ptr<Type ) ) {
        std::unique_ptr<Type> pUnique;
        pUnique.reset( new Type( type ) );
        pData = reinterpret_cast<void*>( pUnique );

    } else {
        std::cout << "Error invalid pointer type passed in << std::endl
                  << "must be either a std::shared_ptr<Type> << std::endl
                  << "or a std::unique_ptr<Type> << std::endl;
    }            
}

Other than checking to see if the void* passed in is either a std::shared_ptr<Type> or a std::unique_ptr<Type>the other questions I may have would be, is my use of reinterpret_cast<void*> the correct way to convert a smart pointer to a void pointer and if not how can this be achieved?


Solution

  • A void* pointer does not carry any type information. What you need to do is pass around an additional value along with the void* to specify what the void* points to, and then you can type-cast it accordingly.

    #ifndef ALLOCATOR_H
    #define ALLOCATOR_H
    
    #include <memory>
    #include <iostream>
    
    template<class Type>
    class Allocator {
    public:
        enum AllocateType { eSharedPtr, eUniquePtr };
    
        Allocator() {}
        Allocator( Type type, std::shared_ptr<Type>& pData );
        Allocator( Type type, std::unique_ptr<Type>& pData );
        // ~Allocator(); // Default Okay
    
        void allocate( Type type, std::shared_ptr<Type>& pData );
        void allocate( Type type, std::unique_ptr<Type>& pData );
    
    private:
        Allocator( const Allocator& c ); // Not Implemented
        Allocator& operator=( const Allocator& c ); // Not Implemented
    
        void allocate( Type type, AllocateType eDataType, void* pData );
    
    }; // Allocator
    
    #include "Allocator.inl"
    
    #endif // ALLOCATOR_H
    

    template<class Type>
    Allocator<Type>::Allocator( Type type, std::shared_ptr<Type>& pData ) {
        allocate( type, pData );
    }
    
    template<class Type>
    Allocator<Type>::Allocator( Type type, std::unique_ptr<Type>& pData ) {
        allocate( type, pData );
    }
    
    template<class Type>
    void Allocator<Type>::allocate( Type type, std::shared_ptr<Type>& pData ) {
        allocate( type, eSharedPtr, &pData );
    }
    
    template<class Type>
    void Allocator<Type>::allocate( Type type, std::unique_ptr<Type>& pData ) {
        allocate( type, eUniquePtr, &pData );
    }
    
    template<class Type>
    void Allocator<Type>::allocate( Type type, AllocateType eDataType, void* pData ) {
        switch (eDataType) {
            case eSharedPtr: {
                static_cast<std::shared<Type>*>(pData)->reset( new Type( type ) );
                break;
            }
            case eUniquePtr: {
                static_cast<std::unique_ptr<Type>*>(pData)->reset( new Type( type ) );
                break;
            }
        }
    }
    

    In which case, I wouldn't even bother trying to funnel everything through a single function to begin with:

    #ifndef ALLOCATOR_H
    #define ALLOCATOR_H
    
    #include <memory>
    #include <iostream>
    
    template<class Type>
    class Allocator {
    public:
        Allocator() {}
        Allocator( Type type, std::shared_ptr<Type>& pData );
        Allocator( Type type, std::unique_ptr<Type>& pData );
        // ~Allocator(); // Default Okay
    
        void allocate( Type type, std::shared_ptr<Type>& pData );
        void allocate( Type type, std::unique_ptr<Type>& pData );
    
    private:
        Allocator( const Allocator& c ); // Not Implemented
        Allocator& operator=( const Allocator& c ); // Not Implemented
    
    }; // Allocator
    
    #include "Allocator.inl"
    
    #endif // ALLOCATOR_H
    

    template<class Type>
    Allocator<Type>::Allocator( Type type, std::shared_ptr<Type>& pData ) {
        allocate( type, pData );
    }
    
    template<class Type>
    Allocator<Type>::Allocator( Type type, std::unique_ptr<Type>& pData ) {
        allocate( type, pData );
    }
    
    template<class Type>
    void Allocator<Type>::allocate( Type type, std::shared_ptr<Type>& pData ) {
        pData.reset( new Type( type ) ) ;
    }
    
    template<class Type>
    void Allocator<Type>::allocate( Type type, std::unique_ptr<Type>& pData ) {
        pData.reset( new Type( type ) );
    }