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

c++, shared-ptr, templates

Solution

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.

Problem

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.

Original source