Obtain original struct/class name during C++ template instantiation
Asked Answered
M

2

6
template<typename T> struct S {};
template<typename T> struct R {};

int main() {
  typedef S<double> s1;
  typedef S<int> s2;
  typedef R<int> s3;
  static_assert(xxx<s1, s2>::value,
                "No, assertion must not be raised");
  static_assert(xxx<s2, s3>::value,
                "Yes, assertion must be raised");
}

So, I want xxx<s1, s2>::value to return true while xxx<s2, s3>::value to return false during compile-time.

Is the existence of xxx impossible in C++? Or, is the existence of xxx theoretically possible in C++ but possibly no one has done it yet?

Mastin answered 28/5, 2017 at 19:47 Comment(2)
So, xxx<T, U>::value is true iff T and U are specializations of the same template?Bertha
@Quentin: yes :)Mastin
P
5

Use two specialisations​ that use template template parameters to perform this "matching":

template<
  typename T,
  typename V>
struct xxx;

template<
 template <class> class A,
 template <class> class B,
 typename X,
 typename Y>
struct xxx<A<X>, B<Y>> {
  static constexpr const int value = false;
};


template<
 template <class> class U,
 typename X,
 typename Y>
struct xxx<U<X>, U<Y>> {
  static constexpr const int value = true;
};

With your code on ideone

Note: For it to be a real type trait you should not set value manually, but derive from std::integral_constant (std::true_type or std::false_type). Above is just a quick mockup I did on my phone.

Primo answered 28/5, 2017 at 20:4 Comment(8)
What is constexpr const?Personate
Are you sure you need two specializations?Beberg
@HWalters No, but with two specialisations it gives an error if it was used with types that are not instantiations of some templates. I'd prefer it that way, because now that trait does only one thing (same templates?) and not two (templates? same templates?).Primo
@HWalters Like here: ideone.com/USMD1Q "error: implicit instantiation of undefined template [..]"Primo
When I use xxx in the following way by first including ratio header: int main() { typedef std::ratio<7, 8> r1; typedef std::ratio<12, 9> r2; static_assert(xxx<r1, r2>::value, "No, assertion shan't be raised"); }, compiling it with g++ -std=c++14 -o mwe mwe.cpp gives: error: incomplete type 'xxx<std::ratio<7l, 8l>, std::ratio<12l, 9l> >' used in nested name specifier. Any idea?Mastin
Ops, sorry, my bad, I need to tweak your solution to have two parameters. Let me try it first.Mastin
I will open up a new thread to address the non-type template parameter. As for the initial problem, your solution works with g++ -std=c++14. Thanks.Mastin
I manage to utilize your strategy in the following way, so I won't open a new thread: template<typename T, typename V> struct xxx : std::false_type {}; template<typename U> struct xxx<U, U> : std::true_type {}; template<template <typename, typename> class U, typename X1, typename X2, typename Y1, typename Y2> struct xxx<U<X1, X2>, U<Y1, Y2>> : std::true_type {}; template<template <intmax_t, intmax_t> class U, intmax_t X1, intmax_t X2, intmax_t Y1, intmax_t Y2> struct xxx<U<X1, X2>, U<Y1, Y2>> : std::true_type {};Mastin
C
1

Something like same_base_template:

#include <type_traits>
template<class A, class B>
struct same_base_template : std::false_type{};

template<template<class...> class S, class... U, class... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};

Edit:

And a third specialization since you are using non-type template arguments (std::ratio):

template<class T, template<T...> class S, T... U, T... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};

Demo

This uses true_typeand false_type from type_traits so we don't need to write a constexpr bool value ourselves. I used a variadic template here because it was slightly more generic and took only a few more keystrokes to do. For your specific use case, you don't need them)

Caton answered 28/5, 2017 at 20:11 Comment(5)
I'm all ears for a better name than same_base_template.Caton
When I use same_base_template in the following way by first including header ratio: int main() { typedef std::ratio<7, 8> r1; typedef std::ratio<12, 9> r2; static_assert(same_base_template<r1, r2>::value, "No, assertion shan't be raised"); }, compiling it with g++ -std=c++14 -o mwe mwe.cpp raises the static assertion error: static assertion failed: No, assertion shan't be raised, which should not be the case. Any idea?Mastin
@TadeusPrastowo: This is because std::ratio is accepting non-type template arguments (the integer values for the ratio). This is slightly different than what I expected, so I added another specialization. I've updated my post. See the new demo.Caton
Now g++ -std=c++14 says error: template parameters not deducible in partial specialization: struct same_base_template<S<U...>, S<V...>> : std::true_type{};. The error does not go away when I use -std=c++1z as in the demo. Any idea?Mastin
@TadeusPrastowo: Hmm, it seems C++14 cannot handle this in clang or gcc. Using C++17 via the wandbox link, though, neither of them complain. Working on a C++14 fix.Caton

© 2022 - 2024 — McMap. All rights reserved.