k2snowman69
k2snowman69

Reputation: 1929

Get typename from passed in template

Is it possible to get the typename off a template that is passed into another template? Here is a basic example of what the goal is

#include <memory>
#include <string>
#include <iostream>

class IntId {
private:
    int id;
public:
    IntId(int id) {
        this->id = id;
    }
};

class StringId {
private:
    std::string id;
public:
    StringId(std::string id) {
        this->id = id;
    }
};

template<typename T_Id>
class Object : public std::enable_shared_from_this<Object<T_Id>>
{
private:
    T_Id id;
public:
    typedef T_Id T;
    Object(T_Id id) {
        this->id = id;
    }
    T_Id getId()
    {
        return this->id;
    }
};


template <class T, class Enable = void>
class Observable {
public:
    // Intentionally doesn't have a set so it breaks the build... I want both types to go into the value below
    void setNonSpecialized(T value) {
    }
};

template<typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<IntId>, T>::value>::type> {
    private:
        std::shared_ptr<T> value;
    public:
        Observable() {
            value = nullptr;
        };
        void set(std::shared_ptr<T> newValue) {
            this->value = newValue;
        }
};

class UPCObject : public Object<IntId> {

};

class UserObject : public Object<StringId> {

};


int main()
{
    auto upcObject = std::make_shared<UPCObject>();
    auto upcObservable = std::make_shared<Observable<UPCObject>>();
    upcObservable->set(upcObject); // Expected to succeed as UPCObject inherits from Object<IntId> which matches template

    auto userObject = std::make_shared<UserObject>();
    auto userObservable = std::make_shared<Observable<UserObject>>();
    userObservable->set(userObject); // Want this to succeed as UserObject inherits from Object<StringId> which would match template Object<T::T_Id>

    auto intObservable = std::make_shared<Observable<int>>();
    intObservable->setNonSpecialized(0); // Expected to succeed and use the value on non-specialized Observable

    return 0;
}

In the code above, upcObject succeeds in it's build because it's type matches the templated type. UserObject doesn't because it has a different Id type.

Now if I change the specialization to the following and explicitly describe the type

template <typename T, typename T_Id>
class Observable<T, typename std::enable_if<std::is_base_of<Object<T_Id>, T>::value>::type>

I get the build error 'T_Id': template parameter not used or deducible in partial specialization 'Observable<T,std::enable_if<std::is_base_of<Object<T_Id>,T>::value,void>::type>' because T_Id isn't actually used in Observable at all.

What would be great is if I could do something like the following

template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<T::T_Id>, T>::value>::type>

Where I am able to get the T_Id off the type being passed in. Because in this specialization I'm checking the base of Object, it should have a type defined on it.

Upvotes: 0

Views: 111

Answers (1)

Guillaume Racicot
Guillaume Racicot

Reputation: 41770

In your case, since you define a typedef in the Object class, you can simply do this:

template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<typename T::T>, T>::value>::type>

If you were not using that typedef, you could do that:

// returning a pointer protect us from abstract types.
template<typename T>
T* get_object_type(const Object<T>&);

template<typename T>
using object_type_t = typename std::remove_pointer<
    decltype(get_object_type(std::declval<const T&>()))
>::type;

And then, use the type trait:

template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<object_type_t<T>, T>::value>::type>

Note that sfinae will occur first on object_type_t. If the function get_object_type is not callable using a const T&, that specialization of Observable will be ruled out. If T don't extends Object<T>, but the function get_object_type is still callable, then your condition with is_base_of will rule out the specialization of Observable.

Upvotes: 1

Related Questions