类型的备用ID生成器

时间:2018-07-13 21:10:40

标签: c++ shared-libraries c++17

a project of mine中,我有一个ID generator用于类似于以下类型的类型:

class Family {
    static std::size_t identifier;

    template<typename...>
    static std::size_t family() {
        static const std::size_t value = identifier++;
        return value;
    }

public:
    template<typename... Type>
    inline static std::size_t type() {
        return family<std::decay_t<Type>...>();
    }
};

std::size_t Family::identifier{};

用法:

const auto id = Family::type<FooBar>();

对于我来说,它可以正常工作,但是有一些限制。最令人讨厌的一个问题(该问题的目的)是,如果链接共享库的所有可执行文件都尝试创建标识符,则该可执行文件将无法使用它。结果通常是,第n个标识符跨边界分配给不同的类型,因为每个共享库都维护各自独立的Family::identifier

一些共享库专家指出了一种更可靠的解决方案,但并没有提出一个不会破坏性能的解决方案(几乎所有解决方案都引入了容器,查找功能和内存分配) )。

有没有其他方法可以解决上述限制而又不损失当前设计的性能?

我搜索了SO,发现了一些有趣的答案。其中许多已经有好几年了。只要现有类的接口保持完整,我想探索的是最新标准版本的解决方案。
This one是最有趣的。它使用静态成员的地址来实现相同目的,但是不符合顺序生成标识符的想法

注意:不幸的是,不能选择使用RTTI。

注意:ID必须按照上面介绍的解决方案从0开始按顺序生成。

3 个答案:

答案 0 :(得分:4)

出现问题是因为您的头文件中包含以下行:

std::size_t Family::identifier{};

因此,它结束于每个翻译单元。取而代之的是,您需要为此将存储移动到.cpp源文件,该文件仅被编译一次,也许会编译到其自身的共享库中。这样,程序中将只有一个identifier实例,它将按您的预期工作。

您还可以将identifier从类static移到头文件中的全局extern中(并且如上所述,在单个.cpp文件中定义)。

如果您使用的是C ++ 17或更高版本,也可以尝试:

inline std::size_t Family::identifier{};

尽管该语言不能保证(甚至无法提及)跨共享库边界使用此新功能时会发生什么,但它确实可以在我的计算机上使用。

答案 1 :(得分:0)

如果不要求id为连续整数,则可以使用模板静态成员的地址作为id。这种方法的好处是它不需要任何运行时初始化(使用static initialization):

// in a header
class Family {
    template<class...> struct Id { static char const id; };

    template<typename... T>
    static std::size_t family() {
        return reinterpret_cast<std::size_t>(&Id<T...>::id);
    }

public:
    template<typename... Type>
    static std::size_t type() {
        return family<std::decay_t<Type>...>();
    }
};

// in a header
template<class... T>
char const Family::Id<T...>::id = {};

// elsewhere    
int main() {
    auto int_id = Family::type<int>();
    auto int_int_id = Family::type<int, int>();
}

您还可以将id设为编译时间常数,并将其用作模板参数:

// in a header
struct Family {
    template<class...> struct Id { static char const id; };
};

// in a header
template<class... T>
char const Family::Id<T...>::id = {};

// elsewhere    
template<char const*>
struct X {};

int main() {
    X<&Family::Id<int>::id> x;
}

答案 2 :(得分:0)

如果您不关心顺序ID,请使用函数的地址作为标识符。

template<typename... T>
uintptr_t getID() {
    return reinterpret_cast<uintptr_t>(&getID<T...>);
}

然后

auto intID = getID<int>();
auto floatID = getID<float>();
...