Eugene Kolesnikov
Eugene Kolesnikov

Reputation: 663

C++: Extraction of template types

I am working on a compile-time wrapper library and stumbled upon a problem of extracting a template parameter type from a given type.

The situation is the following. I have an unchangeable class R:

template <class T>
class R {};

I also have an unchangeable main function:

int main()
{
    auto r = new R<int>();

    // Function that knows the type of 'r'
    create_obj<decltype(r)>();

    delete r;
    return 0;
}

The create_obj function is the one that I am working on. For visualization purposes, I will write it the following way:

template< class C >
void create_obj()
{
    // Print the type of the function (Output: R<int>*)
    std::cout << boost::typeindex::type_id<C>().pretty_name() << std::endl;
}

And the problem is that I need to access the int template parameter type of the R class from the create_obj function.

Pseudocode:

template< 
    template < class T >
    class C<T>
>
void create_obj()
{
    // Print the type of the function (Output: int)
    std::cout << boost::typeindex::type_id<T>().pretty_name() << std::endl;
}

Is it possible to do it? I was going through the C++ documentation but I was not able to find any way to "detach" the template parameter type from the class definition.

Upvotes: 1

Views: 91

Answers (2)

max66
max66

Reputation: 66200

I instead calling create_obj() through the type of r

create_obj<decltype(r)>();

you can call it passing directly r, the solution is simple

template <typename T>
void create_obj (R<T> const *)
 { /* do something with T */ }

But if you want call create_obj() passing only the type R<T> (or a type pointer to R<T>) without instantiating an object of type R<T>, you can define a lightweight type wrapper

template <typename>
struct typeWrapper
 { /* empty! */ };

redefine create_obj() receiving a typeWrapper of a R<T> pointer

template <typename T>
void create_obj (typeWrapper<R<T> *> const &)
 { /* do something with T */ }

and call it using decltype(r)

create_obj(typeWrapper<decltype(r)>{});

or directly R<int> *

create_obj(typeWrapper<R<int> *>{});

Another alternative is declare create_obj as a struct/class

template <typename>
struct create_obj;

and define only a specialization based on a R<T> * pointer, with a static func() method in it

template <typename T>
struct create_obj<R<T> *>
 {
   static void func ()
    { /* do something with T */ } 
 };

You can use it through decltype(r)

create_obj<decltype(r)>::func();

or through R<int> *

create_obj<R<int> *>::func();

If you prefer, instead of a static func() method, you can insert in create_obj() an ordinary (non static) method; maybe an operator(). But, this way, you have to instantiate a create_obj object to call the method.

Upvotes: 0

Quimby
Quimby

Reputation: 19113

Yes, it is possible, one way to do it:

template<typename T>
class R{};


template<typename C>
struct extract_impl{};

template<typename T>
struct extract_impl<R<T>>{
    using type = T;
};

template<typename C>
using extracted_type = typename extract_impl<std::remove_pointer_t<std::decay_t<C>>>::type;

template< class C >
void create_obj()
{
    static_assert(std::is_same_v<extracted_type<C>,int>);
}
int main()
{
    create_obj<R<int>>();
}

The convention is that R would itself expose the type via using, but you wrote that it is unchangeable.

Upvotes: 2

Related Questions