繁体   English   中英

CRTP和唯一的持久标识符

[英]CRTP and unique persistent identifiers

请考虑以下代码:

#include <iostream>
#include <cstdlib>
#include <ctime>

struct BaseClass {
    static int identifier() {
        static int identifier_counter = 0;
        return identifier_counter++;
    }
};

template <class D>
struct Class: public BaseClass {
    static int identifier() {
        static int class_identifier = BaseClass::identifier();
        return class_identifier;
    }
};

struct A: public Class<A> { };
struct B: public Class<B> { };

int main() {
    std::srand(std::time(0));
    int r = std::rand()%2;

    if(r) {
        std::cout << "A: " << A::identifier() << std::endl;
        std::cout << "B: " << B::identifier() << std::endl;
    } else {
        std::cout << "B: " << B::identifier() << std::endl;
        std::cout << "A: " << A::identifier() << std::endl;
    }
}

这是一个减少但仍然合理的问题表示。

任何派生类在运行时都将具有特定的不同标识符 ,并且相同类型的两个实例将共享相同的标识符。 肯定是解决这个问题的好方法。

不幸的是,这些标识符取决于调用identifier成员的顺序(我们可以通过多次运行示例来轻松查看)。 换句话说,给定两个类AB ,如果碰巧运行两次软件,它们的identifier成员以不同的顺序被调用,则它们具有不同的标识符。

我的问题是,由于某些原因,我需要将这些标识符存储在某个地方并让它们在单次执行中存活下来,这样我可以在应用程序再次运行后对原始类型进行推理并决定从存储中读取这些值。

另一种方法是使用type_info hash_code ,但它会遇到其他问题。 另一种解决方案是在应用程序的引导期间强制调用identifier成员,但是这也有几个缺点。

我想知道到目前为止是否有一个易于实现但仍然优雅的解决方案,开发人员完全透明地识别多个执行的类型,因为上面的一个是针对单个运行的应用程序。

使用C ++无法解决每个类具有唯一持久标识符的问题。 抱歉。 您将依赖于调用初始化函数的顺序,或者,如果您从静态对象的初始化程序中调用它们,则按静态初始化程序的顺序(通常取决于链接行中对象文件的顺序)。

当然,不能保证散列是唯一的。

您必须使用外部脚本。 特别是,可能会使用以下内容:

// when class is first created
class Foo {
  static int class_id = ?CLASS_ID?;
};

// after class is process by the script 
class Foo {
  static int class_id = 123; // Autogenerated by 'stamp_id.pl'
};

您可能有一个perl脚本作为编译的一部分运行(第一件事),它打开项目目录中的所有.h文件,读取所有这些文件, Autogenerated by 'stamp_id.pl'计算所有自动生成的实例,而不是标记全部?CLASS_ID? 使用递增的计数器(从已生成的ID的数量开始)。 为了增加一些安全性,你可能想要一个比简单更好的模式?......?但是我认为,你有了这个想法。

即使它们与问题略有不同,我在这里提出了一个可能也适合这个问题的解决方案。
它不是基于CRTP习语,而是具有非侵入式解决方案的优势。

它遵循一个最小的工作示例:

#include<cstddef>
#include<functional>
#include<iostream>

template<typename T>
struct wrapper {
    using type = T;
    constexpr wrapper(std::size_t N): N{N} {}
    const std::size_t N;
};

template<typename... T>
struct identifier: wrapper<T>... {
    template<std::size_t... I>
    constexpr identifier(std::index_sequence<I...>): wrapper<T>{I}... {}

    template<typename U>
    constexpr std::size_t get() const { return wrapper<U>::N; }
};

template<typename... T>
constexpr identifier<T...> ID = identifier<T...>{std::make_index_sequence<sizeof...(T)>{}};

// ---

struct A {};
struct B {};

constexpr auto id = ID<A, B>;

int main() {
    switch(id.get<B>()) {
    case id.get<A>():
        std::cout << "A" << std::endl;
        break;
    case id.get<B>():
        std::cout << "B" << std::endl;
        break;
    }
}

主要问题是如果从类型列表中删除元素,则id可能会发生变化。
无论如何,定义一个空的占位符来解决这个问题是微不足道的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM