Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: How to check if a type is an instantiation of a given class-template of "heterogeneous" NON-TYPE parameters?

I know it's trivial to check if a type is an instantiation of a class-template taking TYPE parameters, as explained here: How can I check if a type is an instantiation of a given class template?

But... is it possible to have a variadic "is_instantiation_of__ntp<...>" (NTP standing for Non-Type Params) that would accept templates with any number of heterogeneous NON-TYPE parameters? For example:

template<char*, bool, long, size_t, char>
struct many_hetero_nontype_params_example {};

char HELLO_WORLD[] = "hello world";
using ManyHeteroNontypeParamsEx = many_hetero_nontype_params_example<HELLO_WORLD, false, -16, 777, 'x'>;

And be able to use it as follows:

is_instantiation_of__ntp<char*, bool, long, size_t, char, many_hetero_nontype_params_example, ManyHeteroNontypeParamsEx>::value

I know this can be easily implemented for NON-TYPE parameter lists that are 1) "homogeneous" (values of same type), or 2) few parameters (such that a non-variadic solution is practical). I even wrote a test case demonstrating these special cases (compiled with gcc 4.7.0) to give a better idea of what I'm talking about:

namespace test__is_instantiation_of__
{
    // is_instantiation_of
    template< template<typename...> class Template, typename T >
    struct is_instantiation_of : std::false_type {};
    template< template<typename...> class Template, typename... Args >
    struct is_instantiation_of< Template, Template<Args...> > : std::true_type {};

    // is_instantiation_of__homogeneous_nontype_params__
    template< typename NTP, template<NTP...> class Template, typename T >
    struct is_instantiation_of__homogeneous_nontype_params__ : std::false_type {};
    template< typename NTP, template<NTP...> class Template, NTP... Args >
    struct is_instantiation_of__homogeneous_nontype_params__< NTP, Template, Template<Args...> > : std::true_type {};

    // is_instantiation_of__fixedcount_nontype_params__
    template< typename NTP1, typename NTP2, template<NTP1, NTP2> class Template, typename T >
    struct is_instantiation_of__fixedcount_nontype_params__ : std::false_type {};
    template< typename NTP1, typename NTP2, template<NTP1, NTP2> class Template, NTP1 v1, NTP2 v2 >
    struct is_instantiation_of__fixedcount_nontype_params__< NTP1, NTP2, Template, Template<v1, v2> > : std::true_type {};

    // type_params_example
    template<typename T1, typename T2, typename T3>
    struct type_params_example {};

    // homogeneous_nontype_params_example
    template<bool B1, bool B2, bool B3, bool B4>
    struct homogeneous_nontype_params_example {};

    // fixedcount_nontype_params_example
    template<long L, char C>
    struct fixedcount_nontype_params_example {};

    using /*.........*/ TypeParamsEx = /*..........*/ type_params_example<std::string, std::tuple<long, void*>, double>;
    using  HomogenousNontypeParamsEx = homogeneous_nontype_params_example<true, false, true, false>;
    using  FixedCountNontypeParamsEx = fixedcount_nontype_params_example<777, 'x'>;

    void run()
    {
        using std::cout;
        using std::endl;

        if ( is_instantiation_of<type_params_example, TypeParamsEx>::value ) {
            cout << "[TypeParamsEx] specializes [type_params_example]" << endl;
        }
        if ( is_instantiation_of<type_params_example, HomogenousNontypeParamsEx>::value ) {
            cout << "[HomogenousNontypeParamsEx] specializes [type_params_example]" << endl;
        }
        if ( is_instantiation_of<type_params_example, FixedCountNontypeParamsEx>::value ) {
            cout << "[FixedCountNontypeParamsEx] specializes [type_params_example]" << endl;
        }

        if ( is_instantiation_of__homogeneous_nontype_params__<bool, homogeneous_nontype_params_example, TypeParamsEx>::value ) {
            cout << "[TypeParamsEx] specializes [homogeneous_nontype_params_example]" << endl;
        }
        if ( is_instantiation_of__homogeneous_nontype_params__<bool, homogeneous_nontype_params_example, HomogenousNontypeParamsEx>::value ) {
            cout << "[HomogenousNontypeParamsEx] specializes [homogeneous_nontype_params_example]" << endl;
        }
        if ( is_instantiation_of__homogeneous_nontype_params__<bool, homogeneous_nontype_params_example, FixedCountNontypeParamsEx>::value ) {
            cout << "[FixedCountNontypeParamsEx] specializes [homogeneous_nontype_params_example]" << endl;
        }

        if ( is_instantiation_of__fixedcount_nontype_params__<long, char, fixedcount_nontype_params_example, TypeParamsEx>::value ) {
            cout << "[TypeParamsEx] specializes [fixedcount_nontype_params_example]" << endl;
        }
        if ( is_instantiation_of__fixedcount_nontype_params__<long, char, fixedcount_nontype_params_example, HomogenousNontypeParamsEx>::value ) {
            cout << "[HomogenousNontypeParamsEx] specializes [fixedcount_nontype_params_example]" << endl;
        }
        if ( is_instantiation_of__fixedcount_nontype_params__<long, char, fixedcount_nontype_params_example, FixedCountNontypeParamsEx>::value ) {
            cout << "[FixedCountNontypeParamsEx] specializes [fixedcount_nontype_params_example]" << endl;
        }
    }
}

As expected, the output you get is:

[TypeParamsEx] specializes [type_params_example]
[HomeogenousNonTypeParamsEx] specializes [homogeneous_nontype_params_example]
[FixedCountNonTypeParamsEx] specializes [fixedcount_nontype_params_example]

The problem is none of those templates work for the many_hetero_nontype_params_example (above). I.e: a single, variadic "is_instantiation_of__ntp" that would accept templates with any number of heterogeneous non-type parameters.

I think if primary-templates weren't required to have parameter-packs at the end of the template parameter list, then this would be easy to implement. Or if it were possible to use a wrapper-struct/nested-struct approach. Here are my (failed) attempts:

namespace test__is_instantiation_of__nontypes__
{
    template<char*, bool, long, size_t, char>
    struct many_hetero_nontype_params_example {};

    char HELLO_WORLD[] = "hello world";
    using ManyHeteroNontypeParamsEx = many_hetero_nontype_params_example<HELLO_WORLD, false, -16, 777, 'x'>;

    /*
     * is_instantiation_of__nontypes_v1__ (version 1)
     * if uncommented, syntax error as expected ...
     *   error: parameter pack 'NTPs' must be at the end of the template parameter list
     *   error: parameter pack argument 'NTPs ...' must be at the end of the template argument list
     */
    //template< typename... NTPs, template<NTPs...> class Template, typename T >
    //struct is_instantiation_of__nontypes_v1__ : std::true_type {};
    //template< typename... NTPs, template<NTPs...> class Template, NTPs... NonTypeArgs >
    //struct is_instantiation_of__nontypes_v1__< NTPs..., Template, Template<NonTypeArgs...> > : std::true_type {};

    /*
     * is_instantiation_of__nontypes_v2__ (version 2)
     * no syntax error (but see instantiation errors below)
     */
    template<typename... NTPs>
    struct is_instantiation_of__nontypes_v2__
    {
        template< template<NTPs...> class Template, typename T >
        struct impl : std::false_type {};

        template< template<NTPs...> class Template, NTPs... NonTypeArgs >
        struct impl< Template, Template<NonTypeArgs...> > : std::true_type {};
    };

    void run()
    {
        /*
         * uncommented since "v1" template has syntax error, but this is how it would be used ...
         */
        //if ( is_instantiation_of__nontypes_v1__<char*, bool, long, size_t, char, many_hetero_nontype_params_example, ManyHeteroNontypeParamsEx>::value ) {
        //  std::cout << "yes" << std::endl;
        //}

        /*
         * "v2" template has no syntax error, but the following attempt to use it results in these errors ...
         *
         *   error: type/value mismatch at argument 1 in template parameter list for 'template<class ... NTPs> template<template<template<NTPs ...<anonymous> > class Template, class T> template<class ... NTPs> template<NTPs ...<anonymous> > class Template, class T> struct is_instantiation_of__nontypes_v2__<NTPs>::impl'
         *   error: expected a template of type 'template<class ... NTPs> template<NTPs ...<anonymous> > class Template', got 'template<char* <anonymous>, bool <anonymous>, long int <anonymous>, long unsigned int <anonymous>, char <anonymous> > struct many_hetero_nontype_params_example'
         */
        //if ( is_instantiation_of__nontypes_v2__<char*, bool, long, size_t, char>::impl<many_hetero_nontype_params_example, ManyHeteroNontypeParamsEx>::value ) {
        //  std::cout << "yes" << std::endl;
        //}
    }
}

...

Is a variadic solution for this even possible?

Thanks in advance.

like image 984
etherice Avatar asked Mar 02 '13 14:03

etherice


People also ask

What is the instantiation of the class template?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation.

How do you instantiate an object in a template class?

To instantiate a template class explicitly, follow the template keyword by a declaration (not definition) for the class, with the class identifier followed by the template arguments. template class Array<char>; template class String<19>; When you explicitly instantiate a class, all of its members are also instantiated.

Which is correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.

How can you define an implementation of a template when a specific type is passed as template parameter?

If we want to define a different implementation for a template when a specific type is passed as template parameter, we can declare a specialization of that template.


1 Answers

You have probably hit a compiler bug. I tried to reduce this to a simpler example:

#include <iostream>

template<bool, char> struct A { };

template<typename... Ts>
struct test
{  
    template<typename T>
    struct impl : std::false_type {};

    template<Ts... Args>
    struct impl<A<Args...>> : std::true_type {};
};

int main()
{
    using IA = A<false, 'x'>;
    std::cout << ((test<bool, char>::impl<IA>::value) ? "Y" : "N");
}

GCC 4.7.2 compiles this, but the compiled program prints the wrong output (N). On the other hand, Clang 3.2 gets this right, and the compiled program prints the correct output (Y).

Here is a slightly modified version of the above program, where the test class template much resembling your is_instantiation_of__nontypes_v2__ class template:

#include <iostream>

template<bool, char> struct A {};

template<typename... Ts>
struct test
{  
    template<template<Ts...> class TT, typename T>
    struct impl : std::false_type {};

    template<template<Ts...> class TT, Ts... Args>
    struct impl<TT, TT<Args...>> : std::true_type {};
};

int main()
{
    using IA = A<false, 'x'>;
    std::cout << ((test<bool, char>::impl<A, IA>::value) ? "Y" : "N");
}

While Clang compiles this and the compiled program prints the correct output (Y), GCC emits the following compilation error:

expected a template of type 'template<class ... Ts> template<Ts ...<anonymous> > class TT', got 'template<bool <anonymous>, char <anonymous> > struct A'.

It looks like GCC does not recognize that the first template template parameter should have a template argument list given by the expansion of Ts. Therefore, it seems to me this is a bug of GCC.

like image 171
Andy Prowl Avatar answered Oct 18 '22 12:10

Andy Prowl