How to detect if a type is shared_ptr at compile time
Asked Answered
G

3

10

I want to get a templatized way of finding if a type is a shared_ptr and based on that I want to have a new specialization of a function.

Example main function is,

template <class T> inline
void CEREAL_LOAD_FUNCTION_NAME( RelaxedJSONInputArchive & ar,    NameValuePair<T> & t )
{
    std::cout << " CEREAL_LOAD_FUNCTION_NAME NameValuePair 1 " << std::endl;
     ar.setNextName( t.name );
     ar( t.value );
}

If t.value is shared_ptr then I want to have a different function specialization. I have tried below,

template <class T> inline
typename std::enable_if<is_pointer<T>::value, void>::type
CEREAL_LOAD_FUNCTION_NAME( RelaxedJSONInputArchive & ar, NameValuePair<T> & t )
 {
    std::cout << " CEREAL_LOAD_FUNCTION_NAME NameValuePair 2 " << std::endl;
   ar.setNextName( t.name );
   ar( t.value );
  }

But it does not seem to work. These are part of c++11 cereal library. Which I am trying to customize.

Gymnastic answered 25/1, 2017 at 13:41 Comment(3)
Does NameValuePair have a member type telling you what t.value is?Forewoman
NameValuePair is defined here. uscilab.github.io/cereal/assets/doxygen/… .it has a Type value member.Gymnastic
a "direct" overload should work, see coliru.stacked-crooked.com/a/db3eae53e609bcfcBarrio
B
18

the following may help:

template<typename T> struct is_shared_ptr : std::false_type {};
template<typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

then you can do the following to get the correct function:

template <class T> 
typename std::enable_if<is_shared_ptr<decltype(std::declval<T>().value)>::value, void>::type
func( T t )
{
    std::cout << "shared ptr" << std::endl;
}

template <class T> 
typename std::enable_if<!is_shared_ptr<decltype(std::declval<T>().value)>::value, void>::type
func( T t )
{
    std::cout << "non shared" << std::endl;
}

live demo

Bradberry answered 25/1, 2017 at 13:49 Comment(2)
Will it work in my case. Actually I have to get a new specialization of the function based on the type of a member called value. If T::value is shared_ptr then I need new specialization.Gymnastic
This looks like the correct answer for the question.Derogative
C
13

This is a basic case of template specialization. The following is a type trait that determines if a type T is a shared_ptr or not. It can be used the same way std::is_pointer, which you already use.

#include <memory>
#include <type_traits>

template<class T>
struct is_shared_ptr : std::false_type {};

template<class T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

Demonstration :

static_assert(is_shared_ptr<std::shared_ptr<int>>::value == true, "");
static_assert(is_shared_ptr<int>::value == false, "");
Counterweigh answered 25/1, 2017 at 13:51 Comment(6)
I like the simplicity of this answer.Derogative
This definitely works and I also love the simplicity, but I don't understand why it works. Can anyone explain or point me to cpp reference or similar explaining this?Precincts
@IronAttorney The answer contains a link to en.cppreference.com/w/cpp/language/partial_specialization which explains what partial specialization is. It basically says "there is a class template call is_shared_ptr with 1 type argument" and then says "when the template argument is a type of std::shared_ptr<T> use this version of the template instead".Meri
Yeah sorry, I didn't give much detail to what was confusing me. So that is the right area, but to me it reads like you'll end up with a specialisation of std::shared_ptr<std::shared_ptr<int>> as T is already a std::shared_ptr<int>. I'd have thought you'd need something more like template<template<class T> class OUTER_T, class T> struct is_shared_ptr<std::shared_ptr<T>> or something similar (sorry, I've forgotten exacly how you have to write such a thing now, but hopefully that's close enough to get my meaning)Precincts
That confusion aside, you have helped me properly get what is happening here. The partial specialisation has a base class of std::true_type. That's cleared the fog alot there, thanks!Precincts
Nevermind sorry, the specialisation part all just clicked...Precincts
S
4

If the type provided is itself a std::shared_ptr of some unknown type T, then the following use of SFINAE should help! Since all smart pointers provide a member type "element_type" we can specialize for std::shared_ptr<T>, std::weak_ptr<T>, and std::unique_ptr<T> as follows:

template<typename T, typename Enable = void>
struct is_smart_pointer
{
    enum { value = false };
};

template<typename T>
struct is_smart_pointer<T, typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, std::shared_ptr<typename T::element_type>>::value>::type>
{
    enum { value = true };
};

template<typename T>
struct is_smart_pointer<T, typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, std::unique_ptr<typename T::element_type>>::value>::type>
{
    enum { value = true };
};

template<typename T>
struct is_smart_pointer<T, typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, std::weak_ptr<typename T::element_type>>::value>::type>
{
    enum { value = true };
};
Sept answered 20/5, 2019 at 17:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.