pseudomarvin
pseudomarvin

Reputation: 1627

c++ Runtime template parameter identification

I am writing a game and I use a ResourceHolder template class which provides functions to load and access a resource(texture, music, font...). It works great for Texture or Font classes because they have the same interface for loading(their functions are called "loadFromFile") but the Music class works a bit differently and it has an "openFromFile" function instead (I use a library called SFML).

template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, 
                                                const std::string & fileName)
{
    std::unique_ptr<Resource> resource(new Resource());

    resource->loadFromFile(fileName);
}

I tried an if-else approach:

if (typeid(id).name() == "Resources::MusicType")
    resource->openFromFile(fileName);
else
    resource->loadFromFile(fileName);

But compiler throws an error when compiling the Texture version of the template since it doesn't have openFromFile method. I also considered passing the function as a third template parameter but I'd like to know whether there is a better solution. Thanks.

Upvotes: 1

Views: 212

Answers (2)

Jarod42
Jarod42

Reputation: 217135

with following type_traits:

#include <cstdint>

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
    template <typename U>                                                   \
    class traitsName                                                        \
    {                                                                       \
    private:                                                                \
        template<typename T, T> struct helper;                              \
        template<typename T>                                                \
        static std::uint8_t check(helper<signature, &funcName>*);           \
        template<typename T> static std::uint16_t check(...);               \
    public:                                                                 \
        static                                                              \
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
    }

DEFINE_HAS_SIGNATURE(has_loadFromFile, T::loadFromFile, void (T::*)(const std::string&));

You may use tag dispatching:

template <typename Resource, typename Identifier>
class ResourceHolder
{
    struct load_tag{};
    struct open_tag{};
public:
    void load(Identifier id,
              const std::string& fileName)
    {
        load(id, fileName,
             typename std::conditional<has_loadFromFile<Resource>::value,
                 load_tag, open_tag>::type{});
    }

private:
    void load(Identifier id,
              const std::string& fileName,
              load_tag)
    {
        std::unique_ptr<Resource> resource(new Resource());

        resource->loadFromFile(fileName);
    }
    void load(Identifier id,
              const std::string& fileName,
              open_tag)
    {
        std::unique_ptr<Resource> resource(new Resource());

        resource->openFromFile(fileName);
    }

};

or SFINAE:

template <typename Resource, typename Identifier>
class ResourceHolder
{
public:
    template <typename T = Resource>
    typename std::enable_if<has_loadFromFile<T>::value>::type
    load(Identifier id,
         const std::string& fileName)
    {
        std::unique_ptr<Resource> resource(new Resource());

        resource->loadFromFile(fileName);
    }
    template <typename T = Resource>
    typename std::enable_if<!has_loadFromFile<T>::value>::type
    load(Identifier id,
         const std::string& fileName)
    {
        std::unique_ptr<Resource> resource(new Resource());

        resource->openFromFile(fileName);
    }

};

Upvotes: 1

zmbq
zmbq

Reputation: 39013

Template Specialization is your friend.

Basically you can create a specialized function for specific types:

template <typename Resource>
void ResourceHolder<Resource, MusicType>::load(...)

I'm not sure this is the right way, as I'm not sure what's the difference between Resource and Identifier, and how you use them.

Upvotes: 2

Related Questions