Home > Enterprise >  Detect the existence of a template instantiation for a given type
Detect the existence of a template instantiation for a given type

Time:03-25

I'm using templates to explicitly declare and allow read access to specific data.

#include <type_traits>

template <typename T>
struct Access
{
    template <typename U>
    void Read()
    {
        static_assert(std::is_same_v<T, U>);
    }
};

Normally T would be a set of types, but I've simplified it here.

I'd like to ensure that any access that gets declared actually gets used. If a user declares Access<int> I want to check to see that there is a corresponding Read<int> somewhere.

Give that context, what I'm currently trying to do is detect whether Access<int>::Read<int> ever gets instantiated. Is this possible?

I tried using extern template to prevent implicit instantiations. The main problem here is that you have to explicitly write out every possible type at namespace scope. I don't see a way to do this systemically.

extern template void Access<int>::Read<int>();

int main()
{
    auto IsIntReadUsed = &Access<int>::Read<int>; // Linker error, yay!
    return 0;
}

Being able to detect this at compile time, link time, or run time is acceptable. The solution does not need to be portable across compilers. A solution that works on any single compiler is sufficient. Any C version is acceptable.

Here is a sandbox for experimenting https://godbolt.org/z/d5cco989v

// -----------------------------------------------------------
// Infrastructure

#include <type_traits>

template <typename T>
struct Access
{
    template <typename U>
    void Read()
    {
        static_assert(std::is_same_v<T, U>);
    }
};

int main()
{
    return 0;
}

// -----------------------------------------------------------
// User Code

using UserAccess = Access<int>;

void UserUpdate(UserAccess access)
{
    // Oh no, access.Read<int> is never used!

    (void) access;
}

// -----------------------------------------------------------
// Validation

template <typename TDeclared>
void ValidateAccess(Access<TDeclared>)
{
    // Write something here that can tell if Access<TDeclared>::Read<TDeclared> has been
    // used when this is called with UserAccess (i.e. ValidateAccess(UserAccess())).
    // This could also be implemented inside the Access class.
}

CodePudding user response:

Here's a link-time solution. Works on GCC, Clang, and MSVC.

One template (impl::Checker<T>) declares a friend function and calls it.

Another template (impl::Marker) defines that function. If it's not defined, the first class gets an undefined reference.

run on gcc.godbolt.org

#include <cstddef>
#include <type_traits>


namespace impl
{
    template <typename T>
    struct Checker
    {
        #if defined(__GNUC__) && !defined(__clang__)
        #pragma GCC diagnostic push 
        #pragma GCC diagnostic ignored "-Wnon-template-friend"
        #endif
        friend void adl_MarkerFunc(Checker<T>);
        #if defined(__GNUC__) && !defined(__clang__)
        #pragma GCC diagnostic pop
        #endif

        static std::nullptr_t Check()
        {
            adl_MarkerFunc(Checker<T>{});
            return nullptr;
        }

        inline static const std::nullptr_t check_var = Check();
        static constexpr std::integral_constant<decltype(&check_var), &check_var> use_check_var{};
    };


    template <typename T>
    struct Marker
    {
        friend void adl_MarkerFunc(Checker<T>) {}
    };
}


template <typename T, impl::Checker<T> = impl::Checker<T>{}>
struct Access
{
    template <typename U>
    void Read()
    {
        static_assert(std::is_same_v<T, U>);
        (void)impl::Marker<U>{};
    }
};


int main()
{
    Access<int> x;
    x.Read<int>();

    [[maybe_unused]] Access<float> y; // undefined reference to `impl::adl_MarkerFunc(impl::Checker<float>)'

    using T [[maybe_unused]] = Access<double>; // undefined reference to `impl::adl_MarkerFunc(impl::Checker<double>)'
}

Had to introduce a dummy template parameter to Access, since I couldn't think of any other way of detecting it being used in a using.

  • Related