首页 > 解决方案 > 是否可以使用 C++ 元编程收集一个特定模板的所有显式实例化类型?

问题描述

我想实现一些编译时评估,包括将一个特定模板的所有显式实例化类型作为输入,就像这样:

template<typename T>
struct Box{T content;};

struct ExplicitlyInstantiatedBoxTypesHolder {
    using types = SOME_MAGICAL_META_PRGRAMMING_CODE; // todo
};

template<typename...>
void printTypes();

template<>
void printTypes(){}

template<typename First, typename... Rest>
void printTypes(){
    cout << typeid(First).name() << ", ";
    printTypeNames<Rest...>();
}

int main(){
    Box<int> box1;
    Box<long> box2;

    printTypes<ExplicitlyInstantiatedBoxTypesHolder::types...>();
    // expected output:
    //      Box<int>, Box<long>
}

标签: c++templatesmetaprogramming

解决方案


如果您只想保留(并打印)类型名称

您可以将创建的类型注册到静态“Regsitrar”中。

让我们为所有 Box 类型建立一个基础。该基地还将管理注册和打印:

class BoxBase {
private:
    static std::set<std::string>& types() {
        static std::set<std::string> types;
        return types;
    }
protected:
    template<typename T>
    static int registerType() {
        std::string name = boost::typeindex::type_id<T>().pretty_name();
        if(types().insert(name).second) {
            // for the need of the line below see:
            // https://stackoverflow.com/questions/59365123
            static std::ios_base::Init force_init;
            std::cout << "registering a box of type: " << name << std::endl;
        }
        return 0;
    }
public:
    static void print_types() {
        for(const auto& type: types()) {
            std::cout << type << ' ';
        }
        std::cout << std::endl;
    }
};

现在类 Box 必须确保它为每个创建的类型注册自己:

template<typename T> class Box: public BoxBase {
    const static int foo_;
public:
    Box() {
        std::cout << "creating a box of type: ";
        std::cout << boost::typeindex::type_id<T>().pretty_name() << std::endl;
    }
    ~Box() {
        // we use foo_ here to avoid optimizing its initialization
        std::cout << foo_ << std::endl;
    }
    void doSomething() const {}
};    

template<typename T>
const int Box<T>::foo_ = BoxBase::registerType<T>();

现在将记录所有 Box 类型,无论它们创建的位置如何!

int main() {
    std::cout << "-------------------------------" << std::endl;
    std::cout << "box types: ";
    BoxBase::print_types();
    std::cout << "-------------------------------" << std::endl;

    Box<long> long_box;
    Box<int> int_box;

    long_box.doSomething();
    int_box.doSomething();

    Box<long> long_box2;

    Box<double> double_box;    
}

void bar() {
    Box<char> box;  
}

输出:

registering a box of type: long
registering a box of type: int
registering a box of type: double
registering a box of type: char
-------------------------------
box types: char double int long 
-------------------------------
creating a box of type: long
creating a box of type: int
creating a box of type: long
creating a box of type: double

代码: http ://coliru.stacked-crooked.com/a/c69162c8da91e51e


如果你真的想持有类型

您可以允许模板的用户提前声明可能使用的类型,然后只允许这些类型,并且您可以打印这些类型的列表。这也不完全是您要寻找的,但也许可以帮助解决相关问题。

这个想法是有一个工厂模板来管理允许的类型。

盒子工厂

template<typename... Ts> 
struct BoxesFactory {
    template<typename T>
    static Box<T> create_box() {
        static_assert(TypeExists<T, Ts...>::value);
        return Box<T>{};
    }
    static constexpr void print_types() {
        TypesPrinter<Ts...>();
    }
};

主要的

int main() {
    BoxesFactory<int, long> factory;
    std::cout << "Supported boxes: " << std::endl;
    factory.print_types();

    auto long_box = factory.create_box<long>();
    auto int_box = factory.create_box<int>();
    // auto double_box = factory.create_box<double>(); // <= compilation error

    long_box.do_box_things();
    int_box.do_box_things();
}

如果您想避免在工厂外部创建 Box,则可以在 BoxFactory 的私有部分内声明类 Box 本身,或者您可以在 Box 的 ctor 中要求只有 BoxFactory 可以传递的私有令牌参数。

类型存在

template<typename Validate, typename T, typename... Ts>
struct TypeExists {
    constexpr static bool value = TypeExists<Validate, T>::value
                                  || TypeExists<Validate, Ts...>::value;
};

template<typename Validate, typename T>
struct TypeExists<Validate, T> {
    constexpr static bool value = std::is_same<Validate, T>::value;
};

类型打印机

template<typename T, typename... Ts>
struct TypesPrinter {
    constexpr TypesPrinter() {
        TypesPrinter<T>();
        TypesPrinter<Ts...>();
    }
};

template<typename T>
struct TypesPrinter<T> {
    constexpr TypesPrinter() {
        // std::cout << typeid(T).name() << ' ';
        std::cout << boost::typeindex::type_id<T>().pretty_name() << ' ';
    }
};

代码: http ://coliru.stacked-crooked.com/a/42f67cc6ce95e5c5


旁注:还有其他技术可以将模板的实例化限制为某些类型。但是那些不处理实际使用的类型的“簿记”。


推荐阅读