共享库中的类和静态变量 我试图用C++编写一个类似于:的东西。

共享库中的类和静态变量 我试图用C++编写一个类似于:的东西。,c++,shared-libraries,C++,Shared Libraries,应用-->核心(.so)一些背景 共享库在C++中很难,因为标准没有说明它们。这意味着每个平台都有不同的实现方式。如果我们将自己局限于Windows和一些*nix变体(任何ELF),那么差异是微妙的。第一个区别是。强烈建议您阅读这篇文章,以便更好地了解可见性属性是什么以及它们对您的作用,这将帮助您避免链接器错误 无论如何,您将得到如下结果(对于使用许多系统进行编译): 接下来,您需要创建一些共享头(standard.h?),并在其中放入一个漂亮的#ifdef东西: #ifdef MY_LIBRA

应用-->核心(.so)一些背景 <>共享库在C++中很难,因为标准没有说明它们。这意味着每个平台都有不同的实现方式。如果我们将自己局限于Windows和一些*nix变体(任何ELF),那么差异是微妙的。第一个区别是。强烈建议您阅读这篇文章,以便更好地了解可见性属性是什么以及它们对您的作用,这将帮助您避免链接器错误

无论如何,您将得到如下结果(对于使用许多系统进行编译):

接下来,您需要创建一些共享头(
standard.h
?),并在其中放入一个漂亮的
#ifdef
东西:

#ifdef MY_LIBRARY_COMPILE
#   define MY_LIBRARY_PUBLIC DLL_EXPORT
#else
#   define MY_LIBRARY_PUBLIC DLL_IMPORT
#endif
这允许您标记类、函数以及类似的内容:

class MY_LIBRARY_PUBLIC MyClass
{
    // ...
}

MY_LIBRARY_PUBLIC int32_t MyFunction();
这将告诉构建系统在调用函数时在何处查找它们

现在:直截了当! 如果您在库之间共享常量,那么您实际上不应该关心它们是否被复制,因为您的常量应该很小,并且复制允许进行大量优化(这很好)。但是,由于您似乎在使用非常量,因此情况有所不同。C++中有十亿种模式来创建交叉库单体,但我自然喜欢我的方式。

在某些头文件中,假设您想要共享一个整数,因此在
myfuncts.h
中确实有:

#ifndef MY_FUNCTS_H__
#define MY_FUNCTS_H__
// include the standard header, which has the MY_LIBRARY_PUBLIC definition
#include "standard.h"

// Notice that it is a reference
MY_LIBRARY_PUBLIC int& GetSingleInt();

#endif//MY_FUNCTS_H__
#ifdef MY_LIBRARY_COMPILE
#define MY_LIBRARY_EXTERN
#else
#define MY_LIBRARY_EXTERN extern
#endif
然后,在
myfuncts.cpp
文件中,您将有:

#include "myfuncs.h"

int& GetSingleInt()
{
    // keep the actual value as static to this function
    static int s_value(0);
    // but return a reference so that everybody can use it
    return s_value;
}
处理模板 C++有超级强大的模板,这很好。但是,跨库推送模板可能会非常痛苦。当一个编译器看到一个模板时,它是一条“填充任何你想让它工作的内容”的消息,如果你只有一个最终目标,这是非常好的。但是,当您使用多个动态共享对象时,这可能会成为一个问题,因为理论上它们都可以使用不同版本的不同编译器进行编译,所有这些编译器都认为它们的不同模板填充空格方法是正确的(我们是谁?标准中没有定义)。这意味着模板可能是一个巨大的痛苦,但您确实有一些选择

不允许使用不同的编译器。 选择一个编译器(每个操作系统)并坚持使用它。仅支持该编译器,并要求所有库都使用该编译器编译。这实际上是一个非常简洁的解决方案(完全有效)

不要在导出的函数/类中使用模板 仅在内部工作时使用模板函数和类。这确实节省了很多麻烦,但总的来说是相当有限的。就个人而言,我喜欢使用模板

强制导出模板,希望一切顺利 这工作得出奇地好(尤其是当与不允许使用不同编译器的情况下)

将此添加到
标准.h

#ifndef MY_FUNCTS_H__
#define MY_FUNCTS_H__
// include the standard header, which has the MY_LIBRARY_PUBLIC definition
#include "standard.h"

// Notice that it is a reference
MY_LIBRARY_PUBLIC int& GetSingleInt();

#endif//MY_FUNCTS_H__
#ifdef MY_LIBRARY_COMPILE
#define MY_LIBRARY_EXTERN
#else
#define MY_LIBRARY_EXTERN extern
#endif
在某些消费类定义中(在声明类本身之前):

//强制导出模板
MY_LIBRARY_外部模板类MY_LIBRARY_公共std::分配器;
MY_LIBRARY_EXTERN模板类MY_LIBRARY_PUBLIC std::vector;
类MY_LIBRARY_PUBLIC MyObject
{
私人:
std::向量m_向量;
};
这几乎是完全完美的…编译器不会对你大喊大叫,生活也会很美好,除非你的编译器开始改变它填充模板的方式,你重新编译其中一个库而不是另一个库(即使这样,它可能仍然可以工作…有时)


请记住,如果您使用的是部分模板专门化(或类型特征或任何更高级的模板元编程内容),那么所有的生产者和消费者都会看到相同的模板专门化。就像在中一样,如果你有一个针对
int
s的
vector
的专门实现,如果制作人看到了针对
int
的实现,但消费者没有看到,消费者会很高兴地创建错误类型的
vector
,这将导致各种各样的错误。所以要非常小心。

你能做一个简单的代码示例,让我看看你在哪里吗?如:您是否理解
\uuuuu属性((可见性(“默认”))
\uuuu declspec(dllexport)
和使用
extern
?我曾尝试使用
属性((可见性(“默认”)),但在一个小的测试项目中,我没有使用它,一切都很好…对:那是因为ELF自动使事物公开可见(因此是
默认值
)——区别在于当你瞄准Windows时。你太快了!谢谢但对于windows,我听说dllimport/dllexport会对代码设置一些限制,比如模板必须已实例化。有什么方法可以尽量减少使用它们吗?我修改了我的答案,将模板导出包括在内。所以你的意思是,如果我的代码涉及到模板SmartPtr,那将是一个糟糕的设计;因为我永远无法实例化所有可能的类型T?其想法是,您使用
SmartPtr
的每个地方,在使用
MY\u LIBRARY\u EXTERN模板类MY\u LIBRARY\u PUBLIC SmartPtr
或其他任何东西之前,都要强制声明。而且,简单的东西通常更安全,这仅仅是因为当版本更改时,编译器要更改的内容更少。但我会考虑使用Boost或Qt中的智能指针,因为它们对二进制兼容性等问题考虑了很久,所以您不必太担心。
//    force exporting of templates
MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC std::allocator<int>;
MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC std::vector<int, std::allocator<int> >;

class MY_LIBRARY_PUBLIC MyObject
{
private:
    std::vector<int> m_vector;
};