Help! Detecting that two template aliases are equal.

In working on Boost.Generic I need, in multiple parts of the library, to detect if two template aliases are equivalent. For instance, this is required when comparing the compatibility of an automatically generated concept map of a less-refined concept with an explicitly created concept map for the less-refined concept when the concept in question has an associated class template. This seems like it shouldn't be too difficult, but apparently my understanding of template aliases is incomplete: //////////////////// // Create a template template< class > struct template_ {}; // Make a simple alias of the template template< class A > using template_alias = template_< A >; // Create a metafunction to determine if two unary type templates are the same template< template< class > class L, template< class > class R > struct templates_are_the_same { static bool constexpr value = false; }; template< template< class > class T > struct templates_are_the_same< T, T > { static bool constexpr value = true; }; // In Clang and GCC, the following static_assert will be triggered static_assert( templates_are_the_same< template_, template_alias >::value , "Templates are not the same." ); //////////////////// This behavior is observed in both Clang and GCC, though I haven't verified that it is correct behavior in the standard. Assuming that it is in fact standard, can anyone conceive of a way around this limitation? I've considered passing dummy arguments to the templates based on the template parameter list and checking that the resultant types are equal, but I'd like to avoid that if at all possible, since it seems like it would be difficult or impossible to support certain templates properly (I.E. consider the template parameter list of a template where one parameter type is dependent on another, like std::integral_constant). This type of detection is very important in making Boost.Generic work as closely to N2914 as possible so any workaround, however hairy, would be a huge help. -- -Matt Calabrese

AMDG On 11/02/2012 04:26 PM, Matt Calabrese wrote:
<snip>
// In Clang and GCC, the following static_assert will be triggered static_assert( templates_are_the_same< template_, template_alias >::value , "Templates are not the same." ); ////////////////////
This behavior is observed in both Clang and GCC, though I haven't verified that it is correct behavior in the standard.
I haven't checked this, but it's the behavior that I would expect.
Assuming that it is in fact standard, can anyone conceive of a way around this limitation? I've considered passing dummy arguments to the templates based on the template parameter list and checking that the resultant types are equal, but I'd like to avoid that if at all possible, since it seems like it would be difficult or impossible to support certain templates properly (I.E. consider the template parameter list of a template where one parameter type is dependent on another, like std::integral_constant).
You already have to specify this in the template argument list. I don't see how creating a dummy parameter makes it any worse.
This type of detection is very important in making Boost.Generic work as closely to N2914 as possible so any workaround, however hairy, would be a huge help.
You have to define exactly what you mean by template aliases being equal. If you mean that for all template parameters, the aliases resolve to the same type, the problem is undecidable. (This follows directly from Rice's Theorem). In Christ, Steven Watanabe

On Fri, Nov 2, 2012 at 8:36 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
I haven't checked this, but it's the behavior that I would expect.
That seems surprising to me. It's a simple alias here, so I would expect it to be exactly that -- an alias. If I make a typedef of a type, I expect it to alias that type such that is_same tells me that are the same (which it, of course, does). An alias should, whenever possible, be exactly that, an alias.
Assuming that it is in fact standard, can anyone conceive of a way around this limitation? I've considered passing dummy arguments to the templates based on the template parameter list and checking that the resultant types are equal, but I'd like to avoid that if at all possible, since it seems like it would be difficult or impossible to support certain templates properly (I.E. consider the template parameter list of a template where one parameter type is dependent on another, like std::integral_constant).
You already have to specify this in the template argument list. I don't see how creating a dummy parameter makes it any worse.
Inside of the macro I know the textual form of the template parameter list, broken down into its individual parts, but not the arguments that are valid for that template. In other words, imagine a template such as this: template< class T, typename T::type X > struct foo; All I know is that parameter type 1 is "class" named "T" and that parameter type 2 is "typename T::type" named "X". I can't possibly create a proper dummy parameter automatically from within the macro since I can't deduce that the dummy type I pass to "T" is required to have a nested type called "type" that is a valid non-type template parameter type. If I get archetypes working, I should be able to build proper defaults based on the constraints of the types, but I'm not there yet and I don't think it should be necessary for this particular problem.
You have to define exactly what you mean by template aliases being equal. If you mean that for all template parameters, the aliases resolve to the same type, the problem is undecidable. (This follows directly from Rice's Theorem).
Sorry, I should have been more specific. I'm not dealing with the general case of any arbitrary template alias, I'm dealing with the special case where the template parameters of the underlying template are deducible from the template parameters of the alias. In other words, cases similar to where template argument deduction works in C++11 when dealing with a template alias: //////////////////// // A simple template template< class T > struct foo {}; // An alias where the underlying template's // parameters are deducible from the alias's // template parameters. template< class T > using foo_alias = foo< T >; // A function template taking a foo< T >, deduction works template< class T > void bar( foo< T > ); // The equivalent function written in terms of the alias template< class T > void bar_alias( foo_alias< T > ); int main() { // All of these work in C++11, as expected bar( foo< int >() ); bar( foo_alias< int >() ); bar_alias( foo< int >() ); bar_alias( foo_alias< int >() ); } //////////////////// Again, this type of deduction works when the template parameters of the underlying template are deducible from the template parameters of the alias. Similar to this behavior, it seems to me that in the case where the template parameter list of an alias is the same as its underlying template (or even to that of another alias); where the underlying template's arguments are deducible from the alias arguments; and where correspondence of the parameters to those in the underlying template are equivalent, then the alias could (IMO should) be a "true" alias (it is called an alias after all). Note that N2914 at least somewhat refers to this type of equivalence, though not in an elaborately-defined manner, when dealing with the compatibility checking of implicitly generated concept maps. -- -Matt Calabrese

I haven't checked this, but it's the behavior that I would expect.
That seems surprising to me. It's a simple alias here, so I would expect it to be exactly that -- an alias. If I make a typedef of a type, I expect it to alias that type such that is_same tells me that are the same (which it, of course, does). An alias should, whenever possible, be exactly that, an alias.
Suppose you have an alias of the form template <typename T> using alias = typename some_metafunction<T>::type; It could very well be that some_metafunction<T>::type is foo<T> for all types T (where foo is some class template), but it's undecidable for the compiler to determine that in the general case. So, in the general case, it is not reasonable to expect a compiler to deem 'alias' and 'foo', as template *template* parameters, to be equal (of course for any T, the template *type* parameters alias<T> and foo<T> *will* be equal). Given that this is the case, it would be strange if, as a special case, the compiler did deem 'alias' and 'foo', as template template parameters, to be equal if 'alias' happened to be defined as follows: template <typename T> using alias = foo<T>; I think the behaviour you're experiencing, while undesirable for your particular use case, is consistent. Regards, Nate

On 03/11/12 03:06, Matt Calabrese wrote:
On Fri, Nov 2, 2012 at 8:36 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
I haven't checked this, but it's the behavior that I would expect.
That seems surprising to me. It's a simple alias here, so I would expect it to be exactly that -- an alias. If I make a typedef of a type, I expect it to alias that type such that is_same tells me that are the same (which it, of course, does). An alias should, whenever possible, be exactly that, an alias.
A template alias is more like a mapping between types. Finding whether mappings are equivalent is a difficult task.

On Fri, Nov 2, 2012 at 7:06 PM, Matt Calabrese <rivorus@gmail.com> wrote: [...]
Sorry, I should have been more specific. I'm not dealing with the general case of any arbitrary template alias, I'm dealing with the special case where the template parameters of the underlying template are deducible from the template parameters of the alias. In other words, cases similar to where template argument deduction works in C++11 when dealing with a template alias:
//////////////////// // A simple template template< class T > struct foo {};
// An alias where the underlying template's // parameters are deducible from the alias's // template parameters. template< class T > using foo_alias = foo< T >;
// A function template taking a foo< T >, deduction works template< class T > void bar( foo< T > );
// The equivalent function written in terms of the alias template< class T > void bar_alias( foo_alias< T > );
[...] I don't really know what you're trying to do, so this might be a completely irrelevant suggestion, but can you do something that would take advantage of, say, ambiguous overloads or ill-defined overloads if you renamed bar_alias to bar, and maybe add some dummy parameter (e.g., "...")? - Jeff

On Sun, Nov 4, 2012 at 1:43 AM, Jeffrey Lee Hellrung, Jr. < jeffrey.hellrung@gmail.com> wrote:
I don't really know what you're trying to do, so this might be a completely irrelevant suggestion, but can you do something that would take advantage of, say, ambiguous overloads or ill-defined overloads if you renamed bar_alias to bar, and maybe add some dummy parameter (e.g., "...")?
No, that's a very good suggestion, and I do something similar during concept map look-up to determine and report ambiguities, but unfortunately I don't think it can be made to work here (the point that the concept map itself is written and implicit maps are generated). There are two problems: first, the ambiguity can only be detected when such a function is attempted to be called, and in order to call it, I need to be able to pass template arguments to the template in question, but for reasons mentioned earlier, this isn't technically feasible. It works during concept map look-up because at that point someone has specified a concept along with its arguments. Once archetypes are in, I might be able to hack something together, but I'm not there yet. The other problem is, I might be able to detect that the call is ambiguous, but unless I'm mistaken, I won't be able to differentiate between the overloads being effectively the same or just being ambiguous in another manner (I.E. imagine two overloads, one involving concept_map concept_< int, T > and another involving concept_map concept_< T, int >, both implicitly generated by the same more-refined concept_map). I need to make this differentiation because a general ambiguity should be ignored at the time that the concept map is written, whereas them being exactly the same is acceptable in the case that they are automatically generated -- I just need to do compatibility checking on the corresponding concept maps involved. I guess one thing that I can do is delay all checking of this kind until concept map look-up and allow ALL forms of ambiguity, doing compatibility checking on them just like I would for concept maps that should be the "same". This would at least allow valid code to compile but would just also allow some invalid code to compile as well. The only other down-side I see is that it would likely slow down compile-times since it effectively means that I may be frequently generating redundant implicit concept maps hooks behind the scenes and considering them all during look-up rather than inhibiting the creation of redundant implicit concept maps at the point they'd be created. Anyway, I don't know how clear any of that was or if I'm just too into this project to be able to communicate properly, but I think I'm just going to go this route for now unless someone has a better solution. -- -Matt Calabrese

On 03/11/12 00:26, Matt Calabrese wrote:
// In Clang and GCC, the following static_assert will be triggered static_assert( templates_are_the_same< template_, template_alias >::value , "Templates are not the same." ); ////////////////////
This behavior is observed in both Clang and GCC, though I haven't verified that it is correct behavior in the standard.
I believe so.
Assuming that it is in fact standard, can anyone conceive of a way around this limitation? I've considered passing dummy arguments to the templates based on the template parameter list and checking that the resultant types are equal, but I'd like to avoid that if at all possible, since it seems like it would be difficult or impossible to support certain templates properly (I.E. consider the template parameter list of a template where one parameter type is dependent on another, like std::integral_constant). This type of detection is very important in making Boost.Generic work as closely to N2914 as possible so any workaround, however hairy, would be a huge help.
Since you're working with concepts, couldn't you use archetypes?

On Fri, Nov 2, 2012 at 8:44 PM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
Since you're working with concepts, couldn't you use archetypes?
Right now I'm dealing with something a little more important to the fundamentals of concepts -- the implementation of [concept.refine.maps] p3 ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2914.pdf#page=416), which is actually extremely complicated to do properly in an emulated manner. What's frustrating is that I've managed to do it almost entirely, with only a tiny hiccup being that I require checking the equivalence of template aliases. I need to do this both for compatibility checking and to eliminate duplicates when looking up more-refined concept maps via the hierarchy of less-refined concept specifiers, which need to be implement internally via template aliases. I have postponed archetypes to a later time and it would be unfortunate if I had to wait until I finished implementing them before explicit concept maps of less-refined concepts could be made to work. -- -Matt Calabrese
participants (5)
-
Jeffrey Lee Hellrung, Jr.
-
Mathias Gaunard
-
Matt Calabrese
-
Nathan Ridge
-
Steven Watanabe