Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access template's arguments with partial type knowledge

I have some code with deep C++ 11 variadic template's magic usage. I am using specialized classes to identify parts of template tree, which I am naming "tags". Each tag has unique number identifier. I need to find a way how to access tag's identifiers

Here is some small example:

#include <string>
#include <iostream>

template <unsigned long ID, typename PARAM>
class tag_
{
    //getter for ID. Better to exclude it. If possible - move to external helper
    constexpr static unsigned long get_id() {return ID;}
    //PARAM is used in some functions of this class
};

//tag's declarations for future usage
template<typename PARAM>
using tag1 = tag_<1UL, PARAM>;
template<typename PARAM>
using tag2 = tag_<2UL, PARAM>;
template<typename PARAM>
using tag3 = tag_<3UL, PARAM>;

//helper class that can iterate TAGS
template <template<typename> class... TAGS>
struct helper
{};
template <template<typename> class TAG>
struct helper<TAG>
{
    static void print_tag(std::ostream& out)
    {
        out << std::string("Tag");
        out << TAG::get_id();  // Here I can't call: error: 'template<class> class TAG' used without template parameters
    }
};
template <template<typename> class TAG, template<typename> class... TAGS>
struct helper<TAG, TAGS...>
{
    static void print_tag(std::ostream& out)
    {
        out << std::string("Tag");
        out << TAG::get_id();  // Here I can't call: error: 'template<class> class TAG' used without template parameters
        out << std::string(", ");
        helper<TAGS...>::print_tag(out);
    }
};

//this class uses tags for some processing
template <template<typename> class... TAG_LIST>
class tagged
{
public:
    void test1(std::ostream& out)
    {
        out << std::string("This function works good");
    }
    void test2(std::ostream& out)
    {
        helper<TAG_LIST...>::print_tag(out);
    }
};

// this class is re-defined for some types of T
template<typename T, typename PARAM>
class usage
{
public:
    void test1(std::ostream& out)
    {
        details.test1(out);
    }
    void test2(std::ostream& out)
    {
        details.test2(out);
    }

    T details;
    PARAM params;
};

//endpoint
struct finish{};

//definition for future usage
template<template<typename> class T1, template<typename> class T2, template<typename> class T3, typename PARAM>
using multitag = usage<tagged<T1, T2, T3>, PARAM>;

int main(int argc, const char* argv[])
{
    // this way I am construction my objects tree
    multitag<tag1, tag2, tag3, tag1<tag2<tag3<finish>>>> tmp;
    tmp.test1(std::cout); // ok
    std::cout << std::endl;
    tmp.test2(std::cout);  //compile error. I want to get "Tag1, Tag2, Tag3" printed
    std::cout << std::endl;
}
like image 828
Evgeniy Avatar asked Jan 25 '26 08:01

Evgeniy


2 Answers

multitag<tag1, tag2, tag3, tag1<tag2<tag3<finish>>>> tmp;

This is your definition for tmp, you just pass in tag1, which is a template type alias

template<typename PARAM>
using tag1 = tag_<1UL, PARAM>;

however its template parameter PARAM isn't provided. So when you want to use it,

out << TAG::get_id()

You should give it a parameter, for example

out << TAG<dummy>::get_id();

dummy can be anything, for example,

class dummy{};

http://coliru.stacked-crooked.com/a/bb1d924dc3d5b774

like image 190
Shangtong Zhang Avatar answered Jan 28 '26 00:01

Shangtong Zhang


The closest what you can get is to declare a specialization for each tagX:

template <template <typename> class TAG> struct tag_id;

template <> struct tag_id<tag1> : std::integral_constant<unsigned long, 1UL> {};
template <> struct tag_id<tag2> : std::integral_constant<unsigned long, 2UL> {};
template <> struct tag_id<tag3> : std::integral_constant<unsigned long, 3UL> {};
out << tag_id<TAG>{};

DEMO

like image 43
Piotr Skotnicki Avatar answered Jan 27 '26 23:01

Piotr Skotnicki