Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic way to test if a type is a (smart) pointer

In my code, I need to test if a type given to a template is a pointer -- be it smart or not. According to boost, there is no reliable and generic way to do that (see here) -- or is there?

So far, I check for the following:

  • A: Can T be converted to void*?
  • B: Does T have a get() method?
  • C: Does T have a type called element_type?
  • D: Does get() return an element_type*?

If (A || B && C && D), then I conclude that my type must be some kind of pointer.

Here's the template:

template <typename T>
class is_pointer_type
{
    typedef struct { char array[1]; } yes;
    typedef struct { char array[2]; } no;

    template <typename C> static yes test_g(decltype(&C::get));
    template <typename C> static no  test_g(...);

    template <typename C> static yes test_e(typename C::element_type*);
    template <typename C> static no  test_e(...);

    enum {
        has_get          = sizeof(test_g<T>(0)) == sizeof(yes),
        has_element_type = sizeof(test_e<T>(0)) == sizeof(yes)
    };

    template <typename Q, bool OK = false>
    struct get { struct type {}; };

    template <typename Q>
    struct get<Q, true>
    {
        typedef decltype(((Q*)nullptr)->get()) type;
    };

    template <typename Q, bool OK = false>
    struct ptr { struct type {}; };

    template <typename Q>
    struct ptr<Q, true>
    {
        typedef typename Q::element_type* type;
    };

public:
    enum {
        types_ok = std::is_same<
                           typename get<T, has_get>::type,
                           typename ptr<T, has_element_type>::type
                   >::value,
        value    = std::is_convertible<T, void*>::value || types_ok
    };
};

So far, it seems to work out ok. But is there something wrong with this reasoning? Should I be prepared for unpleasant surprises? What about const / volatile?

Update (Motivation):

In the comments you ask for my motivation and they are right, I owe you one. The use case is a Lua - C++ binding library: when exposing a class instance to Lua with template <typename T> push_value(T value), I need to deduce the underlying type U in any combination of T = U const/volatile/*/& and T = some_pointer<U>. I need to know if the underlying class U has been registered already with the binder.

like image 346
marton78 Avatar asked May 10 '12 17:05

marton78


1 Answers

It's easy to check if a type is a pointer, either using boost or defining a custom template with a specialization like

template <typename C> static no test_pointer(C);
template <typename C> static yes test_pointer(C*);

But you can stick with the void* solution if you like it more.

To check smart pointers, I suggest checking for appropriate operators instead. I think a type can be considered to be a smart pointer only if it has both operator* and operator-> defined. So you should check for

template <typename C> static yes test_deref(decltype(&C::operator*));
template <typename C> static no test_deref(...);
template <typename C> static yes test_arrow(decltype(&C::operator->));
template <typename C> static no test_arrow(...);

and require both results to be 'yes'. So, the final formula can be calculated as "normal pointer || (has operator* && has operator->)".

However, it's a solution only for smart pointers. If you also want to pass types other than smart pointers (other wrappers, collections, etc) to Lua than it's a whole different story and I don't dare to propose a solution for that.

like image 119
Zólyomi István Avatar answered Oct 19 '22 23:10

Zólyomi István