c++template-specialization

Detecting if a function template specialization exists and returning a function pointer to a valid implementation


I have a project where I use function templates to allow users to swap in function specializations at compile time, so there is a default version provided in a library of common code and an optional user-provided specialization for a specific subproject.

I would like the common code to be able to tell whether a user actually provided a specialization, but most of the techniques I've found for this rely on deleting the primary template along these lines:

#include <type_traits>

using FunctionPtr = void (*)();

// Primary template
template<class T>
void demo() = delete;

// The common code default implementation
template<>
void demo<int>() { };

// Detect if a specialization exists
template<class T>
concept has_demo = requires { demo<T>(); };

static_assert(has_demo<int>);
static_assert(!has_demo<char>); // False - no user-provided specialization.

I eventually need to store the actual resolved function pointer in a Vulkan dispatch table, so I'm trying to write a resolution function which returns the specialization if it exists and not otherwise:

constexpr FunctionPtr getPointer(void)
{
    if constexpr(has_demo<char>)
    {
        return demo<char>;
    }

    return demo<int>;
}

... which is subsequently complaining that demo<char> is hitting the deleted template (which is true, it doesn't exist because the user didn't provide one).

I know I can support this by not deleting the default version and providing a parallel trait that can be tested for the has_..<>() check, but that requires users to add the "true" trait for every function specialization that they provide, which is a worse user experience in terms of users having to write more code. Is there a way to avoid that?


Solution

  • if constexpr doesn't work that way outside of a template.

    You can put it in a template to fix that:

    constexpr FunctionPtr getPointer(void)
    {
        return []<typename T>{
            if constexpr(has_demo<T>)
            {
                return demo<T>;
            }
    
            return demo<int>;
        }.operator()<char>();
    }
    

    However, this is an ODR nightmare. If you write getPointer before the specialisation of demo<char> is declared, this program will be ill-formed NDR (and probably return demo<int> in practice). This is the same issue with having a parallel has_...<>() function.