Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect if a type is a specialization from the primary template or a user-provided specialization

Tags:

c++

c++20

Let's say I have this:

template<typename T>
class my_template {...};

Now, users are expected to be able to specialize my_template for their own types. And they will pass those types to some API functions of mine, which will use properties of my_template<T> to do stuff.

So at some point in my code, I have a my_template<T>. I want a meta-function of some kind which takes my_template<T> and results in a compile-time value of true if my_template<T> is a user-provided specialization (partial or explicit) and false if it isn't.

The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.

This question is not sophistry. The C++20 specification has a meta-function ITER_CONCEPT(I), which has different internal behavior based on whether std::iterator_traits<I> is from the primary template or a user-provided specialization. Of course, being the standard library, they can create an identifier prefixed with __ as a member of the primary template and thereby declare any attempted forgery from user-space to be undefined behavior.

Is this a thing only a compiler/standard library implementation can do in a foolproof, or is it possible to do this in C++20?

like image 870
Nicol Bolas Avatar asked Feb 26 '21 22:02

Nicol Bolas


1 Answers

The most obvious solution is to shove a private member alias, or some other concept-detectable private declaration, into the primary my_template definition, and rely on that not being present in user-provided specializations. However, a user could forge an explicit specialization by providing an appropriate definition. So this isn't foolproof.

That's basically it, yep. For instance, libstdc++'s iterator traits has its primary class template inherit from a hidden base class template, and then checks for inheritance from that base.

Yes, a user could forge an explicit specialization by providing an appropriate definition - but, like, don't. That isn't something you would do by accident, that's explicitly and pointlessly malicious, and the typical saying is that the library and the language defend against Murphy, not Machiavelli.

With Modules, you can make it even harder for the user to be explicitly malicious by exporting the primary template but not actually exporting the base class that you're using to check if the class template was specialized:

export module std;

namespace std {
    template <typename T> struct __iterator_traits { };

    template <typename T>
    export struct iterator_traits : __iterator_traits { };
}

Now the user can't even name std::__iterator_traits in order to break the library. Perhaps reflection will still give them a way to do so (assuming those things are still accessible), but now they'd really be jumping through a lot of hoops.


Speaking of Reflection, part of the currently-specified API (P1240) includes the functions is_specialization, is_partial_specialization, and is_explicit_specialization. If those mean what I think they mean, then when we eventually get reflection, then the library wouldn't have do use these magic base / magic member hacks and could just directly check to see if the provided specialization was a partial or explicit specialization or not. I figure the library should probably add is_primary_specialization for completeness.

like image 66
Barry Avatar answered Sep 21 '22 06:09

Barry