简体   繁体   English

CRTP和唯一的持久标识符

[英]CRTP and unique persistent identifiers

Consider the following code: 请考虑以下代码:

#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;
    }
}

It's a reduced, but still plausible representation of the problem. 这是一个减少但仍然合理的问题表示。

Any derived class will have a specific, different identifier on runtime and two instances of the same type will share the same identifier. 任何派生类在运行时都将具有特定的不同标识符 ,并且相同类型的两个实例将共享相同的标识符。 Surely a good solution for such a problem. 肯定是解决这个问题的好方法。

Unfortunately, those identifiers depend on the order on which the identifier members are invoked (we can see it easily by running multiple times the example). 不幸的是,这些标识符取决于调用identifier成员的顺序(我们可以通过多次运行示例来轻松查看)。 In other words, given two classes A and B , if it happens that running twice the software their identifier members are invoked in different order, they have different identifiers. 换句话说,给定两个类AB ,如果碰巧运行两次软件,它们的identifier成员以不同的顺序被调用,则它们具有不同的标识符。

My problem is that, for some reasons, I need to store those identifiers somewhere and let them survive the single execution, so that I can reason on the original types once the application runs once more and decide to read those values from the storage. 我的问题是,由于某些原因,我需要将这些标识符存储在某个地方并让它们在单次执行中存活下来,这样我可以在应用程序再次运行后对原始类型进行推理并决定从存储中读取这些值。

An alternative would be to use hash_code from type_info , but it suffers from other problems. 另一种方法是使用type_info hash_code ,但它会遇到其他问题。 Another solution would be to force the calls to the identifier members during the bootstrap of the application, but this one also has several drawbacks. 另一种解决方案是在应用程序的引导期间强制调用identifier成员,但是这也有几个缺点。

I'd like to know if there is so far an easy to implement though still elegant solution that is completely transparent to the developer to identify types over several executions, as the one above is for the single run of the application. 我想知道到目前为止是否有一个易于实现但仍然优雅的解决方案,开发人员完全透明地识别多个执行的类型,因为上面的一个是针对单个运行的应用程序。

The problem of having unique persistent identifier for every class is unsolvable with C++. 使用C ++无法解决每个类具有唯一持久标识符的问题。 Sorry. 抱歉。 You will either depend on the order of calling your initializaer functions, or, if you call them from initializers of static objects, on the order of static initializer (which will usually depend on the order of your object files in your link line). 您将依赖于调用初始化函数的顺序,或者,如果您从静态对象的初始化程序中调用它们,则按静态初始化程序的顺序(通常取决于链接行中对象文件的顺序)。

And of course, there is no guarantee that hash will be unique. 当然,不能保证散列是唯一的。

You will have to use external script for this. 您必须使用外部脚本。 In particular, something like this might be used: 特别是,可能会使用以下内容:

// 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'
};

You might have a perl script running as part of the compilation (the very first thing) which opens all .h files in the project directory, reads all of them, counts all instances of Autogenerated by 'stamp_id.pl' and than stamps all ?CLASS_ID? 您可能有一个perl脚本作为编译的一部分运行(第一件事),它打开项目目录中的所有.h文件,读取所有这些文件, Autogenerated by 'stamp_id.pl'计算所有自动生成的实例,而不是标记全部?CLASS_ID? with incremented counter (starting from the number of already generated ids). 使用递增的计数器(从已生成的ID的数量开始)。 To add some safety, you might want a better pattern than simple ?...?, but I think, you got the idea. 为了增加一些安全性,你可能想要一个比简单更好的模式?......?但是我认为,你有了这个想法。

Even if they are slightly different as questions, here I proposed a solution that maybe can fit well also with this question. 即使它们与问题略有不同,我在这里提出了一个可能也适合这个问题的解决方案。
It isn't based on the CRTP idiom and it has the advantage of being a non-intrusive solution. 它不是基于CRTP习语,而是具有非侵入式解决方案的优势。

It follows a minimal, working example: 它遵循一个最小的工作示例:

#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;
    }
}

The main problem is that the ids can change if an element is removed from the types list. 主要问题是如果从类型列表中删除元素,则id可能会发生变化。
Anyway, it's trivial to define an empty placeholder to work around the issue. 无论如何,定义一个空的占位符来解决这个问题是微不足道的。

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

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