How to implement a generic pointer type in C ++

In C, you can assign a pointer to a pointer to void, and then return it back to its original type so that this data pointer is restored. The language standard ensures that such a conversion does not lose information. This often means (not necessarily, but true on most platforms) that the size of the void pointer is the same as the size of the data pointers. Thus, these facts can be expected to use void pointers as generic pointers to heterogeneous types, while void pointers have the same size and presentation. For example, one has an array of void pointers, with its elements pointing to dynamically distributed objects of different types. Building such an array makes some things convenient. My question is: how to implement something similar, a generic pointer type in C ++, which corresponds to the following: (supposethat g_pointer is the name of the class)

  • Built from any type of pointer, you can write code like

    g_pointer g_ptr =  g_pointer(new T())
    
  • Restore original pointer

    T* ptr = g_ptr.recover(), or
    auto* ptr = g_tr.recover() 
    
  • Update: according to some comments, the above cannot be done in C ++, then something like

    recover<Type>(g_ptr)
    

    should be enough, throwing an exception. Type is incompatible.

  • g_pointer can be contained in std :: vector or in a simple array, which basically means

    sizeof(g_pointer) // a predetermined constant number,
    

    (Update: this is always true if such a class can be correctly implemented , thanks for pointing out.)

I just found boost :: any, to look into its introduction, it seems, it seems that this may be what I want, although it may not be so. Therefore, anyone familiar with boost :: any is welcome to comment.

Update: (reply to some comments)

  • g_pointer , . .
  • , , , , - .

: @Caleth, std:: any is great.

+4
2

++ . g_ptr.recover() , , .


g_ptr.recover<T>(), g_pointer, void* const std::type_info&, , ,

class g_pointer {
public:
    template <class T>
    constexpr g_pointer(T *data) noexcept : _data(data), _object_type(typeid(T)) {}

    template <class T>
    T* recover() const {
         if (typeid(T) == _object_type) return static_cast<T*>(_data);
         else throw std::bad_cast{};
    }
private:
    void *_data;
    const std::type_info &_object_type;
};

: g_pointer , , , , .


- : const T* void*, const T*. -, _data const void* const_cast . , recover g_pointer, const. , typeid -, , const.

class g_pointer {
public:
    template <class T>
    constexpr g_pointer(T *data) noexcept : _data(data), 
                                            _object_type(typeid(T)),
                                            _is_const(std::is_const_v<T>) 
                                         // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change here
    {
    }

    template <class T>
    T* recover() const {
         if (
             typeid(T) != _object_type ||
             (_is_const && !std::is_const_v<T>) // try to obtain T* while const T* is held
         ) {
             throw std::bad_cast{};
         }
         else return static_cast<T*>(const_cast<void*>(_data));
                                  // ^^^^^^^^^^^^^^^^^ change here
    }
private:
    const void *_data;
 // ^^^^^ change here
    const std::type_info &_object_type;
    bool _is_const; // <-- record whether the pointer points to const T
};
+5

C-, void*, ++. , , , , .

, , void* , std::vector, .

, , :

generic_ptr.hpp

#include <exception>
#include <typeinfo>
#include <map>

namespace so {
    std::map<std::size_t, std::size_t> type_sizes;

    template < typename T >
    std::size_t type_id()
    {
        static char tid;
        std::size_t sz = reinterpret_cast<std::size_t>(&tid);
        so::type_sizes[sz] = sizeof(T);
        return sz;
    }

    template < typename T >
    inline std::size_t type_id(const T& t)
    {
        return so::type_id<T>();
    }

    template < typename T >
    inline std::size_t type_id(const T *const t)
    {
        return so::type_id<T>();
    }

    template < typename T, typename C >
    inline bool type_of()
    {
        return so::type_id<T>() == so::type_id<C>();
    }

    template < typename T, typename C >
    inline bool type_of(const C& c)
    {
        return so::type_of<T, C>();
    }

    template < typename T, typename C >
    inline bool type_of(const C *const c)
    {
        return so::type_of<T, C>();
    }

    template < typename T, typename C >
    inline bool type_of(const T& t, const C& c)
    {
        return so::type_of<T, C>();
    }

    template < typename T, typename C >
    inline bool type_of(const T *const t, const C *const c)
    {
        return so::type_of<T, C>();
    }

    class generic_ptr
    {
        public:
            generic_ptr() : m_ptr(0), m_id(0) { }

            template < typename T >
            generic_ptr(T *const obj) : 
                m_ptr(obj), m_id(so::type_id<T>())
            {
            }

            generic_ptr(const generic_ptr &o) : 
                m_ptr(o.m_ptr), m_id(o.m_id)
            {
            }

            ~generic_ptr()
            {
                this->invalidate();
            }

            static generic_ptr null()
            {
                return generic_ptr();
            }

            void invalidate()
            {
                this->m_ptr = 0;
                this->m_id = 0;
            }

            template < typename T >
            bool is_type() const
            {
                return this->m_id == so::type_id<T>();
            }

            template < typename T >
            void gc()
            {
                delete ((T*)this->m_ptr);
                this->invalidate();
            }

            bool valid() const
            {
                return (this->m_ptr != 0);
            }

            operator bool() const
            {
                return (this->m_ptr != 0);
            }

            bool operator!() const
            {
                return (!operator bool());
            }

            generic_ptr& operator=(const generic_ptr &o)
            {
                this->m_ptr = o.m_ptr;
                this->m_id = o.m_id;
                return *this;
            }

            template < typename T >
            const generic_ptr& operator=(T *const obj)
            {
                this->m_ptr = obj;
                this->m_id = so::type_id<T>();
                return *this;
            }

            template < typename T >
            operator T *const() const
            {
                if (this->m_id != so::type_id<T>()) {
                    throw std::bad_cast();
                }
                return static_cast<T *const>(
                    const_cast<void *const>(this->m_ptr)
                );
            }

            template < typename T >
            operator const T *const() const
            {
                if ((this->m_id != so::type_id<T>()) && (this->m_id != so::type_id<const T>())) {
                    throw std::bad_cast();
                }
                return static_cast<const T *const>(this->m_ptr);
            }

            operator void *const() const
            {
                return const_cast<void*>(this->m_ptr);
            }

            operator const void *const() const
            {
                return this->m_ptr;
            }

            bool operator==(const generic_ptr& o) const
            {
                return (this->m_ptr == o.m_ptr && this->m_id == o.m_id);
            }

            bool operator!=(const generic_ptr& o) const
            {
                return !(*this == o);
            }

            std::size_t hash() const
            {
                return this->m_id;
            }

        private:
            const void* m_ptr;
            std::size_t m_id;
    };
}

, :

main.cpp

#include <iostream>
#include <vector>
#include "generic_ptr.hpp"

class MyClass {
    public:
        MyClass() : m_val1(10), m_val2(20), m_val3(10), m_val4(2) {}
        MyClass(int a, int b, int c, int d) : m_val1(a), m_val2(b), m_val3(c), m_val4(d) {}

        friend std::ostream& operator<<(std::ostream& os, const MyClass& mc)
        {
            os << mc.m_val1 << " + " <<
                mc.m_val2 << " + " <<
                mc.m_val3 << " + " <<
                mc.m_val4 << " = " <<
                (mc.m_val1 + mc.m_val2 + mc.m_val3 + mc.m_val4);
            return os;
        }
    private:
        int m_val1;
        int m_val2;
        int m_val3;
        int m_val4;
};

template < typename T >
void print(so::generic_ptr& g_ptr)
{
    std::cout << "sizeof = " << so::type_sizes[g_ptr.hash()]
            << ", val = " << *((T*)g_ptr) << std::endl;
}

template < typename T >
void cleanup(so::generic_ptr& g_ptr)
{
    delete ((T*)g_ptr);
}

int main(int argc, char* argv[])
{
    std::vector<so::generic_ptr> items;
    items.push_back(new int(10));
    items.push_back(new double(3.14159));
    items.push_back(new MyClass());
    items.push_back(new char(65));
    items.push_back(new MyClass(42,-42,65536,9999));
    items.push_back(new int(999));

    for (auto i : items) {
        if (i.is_type<int>()) { print<int>(i); }
        else if (i.is_type<char>()) { print<char>(i); }
        else if (i.is_type<double>()) { print<double>(i); }
        else if (i.is_type<MyClass>()) { print<MyClass>(i); }
    }

    int* i = (int*)items[0];
    std::cout << "i = " << *i << std::endl;
    *i = 500;
    std::cout << "i = " << *i << std::endl;

    try {
        double* d = (double*)items[0];
        std::cout << "d = " << *d << std::endl;
    } catch (std::bad_cast& ex) {
        std::cout << ex.what() << std::endl;
    }

    for (auto i : items) {
        if (i.is_type<int>()) {
            print<int>(i);
            cleanup<int>(i);
        } else if (i.is_type<char>()) {
            print<char>(i);
            cleanup<char>(i);
        } else if (i.is_type<double>()) {
            print<double>(i);
            cleanup<double>(i);
        } else if (i.is_type<MyClass>()) {
            print<MyClass>(i);
            cleanup<MyClass>(i);
        }
    }

    return 0;
}

, , ; , recover , , print: *((T*)g_ptr), raw-, , for..each:

int* i = (int*)items[0];
*i = 500;
print<int>(items[0]);

, :

try {
    double* d = (double*)items[0];
    // items[0] is an int, so this will throw a std::bad_cast
    std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
    std::cout << ex.what() << std::endl;
}

, , , , , , ++.

, .

+1
source

Source: https://habr.com/ru/post/1693053/


All Articles