abe*_*bel 28 c++ shared-libraries
我正在尝试使用以下架构在c ++中编写内容:
App - > Core(.so)< - 插件(.so)
对于linux,mac和windows.Core隐式链接到App,而插件显式链接到dlopen/LoadLibrary到App.我遇到的问题:
有人可以给我一些不同平台的解释和说明吗?我知道在这里问他们所有人似乎都很懒,但我真的找不到这个问题的系统答案.
我在entry_point.cpp中为插件做了什么:
#include "raw_space.hpp"
#include <gamustard/gamustard.hpp>
using namespace Gamustard;
using namespace std;
namespace
{
struct GAMUSTARD_PUBLIC_API RawSpacePlugin : public Plugin
{
RawSpacePlugin(void):identifier_("com.gamustard.engine.space.RawSpacePlugin")
{
}
virtual string const& getIdentifier(void) const
{
return identifier_;
}
virtual SmartPtr<Object> createObject(std::string const& name) const
{
if(name == "RawSpace")
{
Object* obj = NEW_EX RawSpaceImp::RawSpace;
Space* space = dynamic_cast<Space*>(obj);
Log::instance().log(Log::LOG_DEBUG, "createObject: %x -> %x.", obj, space);
return SmartPtr<Object>(obj);
}
return SmartPtr<Object>();
}
private:
string identifier_;
};
SmartPtr<Plugin> __plugin__;
}
extern "C"
{
int GAMUSTARD_PUBLIC_API gamustardDLLStart(void) throw()
{
Log::instance().log(Log::LOG_DEBUG, "gamustardDLLStart");
__plugin__.reset(NEW_EX RawSpacePlugin);
PluginManager::instance().install(weaken(__plugin__));
return 0;
}
int GAMUSTARD_PUBLIC_API gamustardDLLStop(void) throw()
{
PluginManager::instance().uninstall(weaken(__plugin__));
__plugin__.reset();
Log::instance().log(Log::LOG_DEBUG, "gamustardDLLStop");
return 0;
}
}
Run Code Online (Sandbox Code Playgroud)
Tra*_*kel 41
C++中的共享库非常困难,因为标准对它们一无所知.这意味着每个平台都有不同的方式.如果我们将自己限制在Windows和某些*nix变体(任何ELF),那么差异是微妙的.第一个区别是共享对象可见性.强烈建议您阅读该文章,以便更好地了解可见性属性及其为您做的事情,这将有助于避免链接器错误.
无论如何,你最终会看到这样的东西(用于编译许多系统):
#if defined(_MSC_VER)
# define DLL_EXPORT __declspec(dllexport)
# define DLL_IMPORT __declspec(dllimport)
#elif defined(__GNUC__)
# define DLL_EXPORT __attribute__((visibility("default")))
# define DLL_IMPORT
# if __GNUC__ > 4
# define DLL_LOCAL __attribute__((visibility("hidden")))
# else
# define DLL_LOCAL
# endif
#else
# error("Don't know how to export shared object libraries")
#endif
Run Code Online (Sandbox Code Playgroud)
接下来,你需要制作一些共享标题(standard.h?)并在其中放入一个不错的小#ifdef东西:
#ifdef MY_LIBRARY_COMPILE
# define MY_LIBRARY_PUBLIC DLL_EXPORT
#else
# define MY_LIBRARY_PUBLIC DLL_IMPORT
#endif
Run Code Online (Sandbox Code Playgroud)
这可以让你标记类,函数和类似的东西:
class MY_LIBRARY_PUBLIC MyClass
{
// ...
}
MY_LIBRARY_PUBLIC int32_t MyFunction();
Run Code Online (Sandbox Code Playgroud)
这将告诉构建系统在调用它们时在哪里查找函数.
如果您在库之间共享常量,那么您实际上不应该关心它们是否重复,因为您的常量应该很小并且重复允许进行大量优化(这很好).但是,由于您似乎使用非常量,因此情况略有不同.在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__
Run Code Online (Sandbox Code Playgroud)
然后,在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;
}
Run Code Online (Sandbox Code Playgroud)
C++拥有超级强大的模板,非常棒.但是,跨库推送模板真的很痛苦.当编译器看到一个模板时,它就是"填写你想做的任何工作"的信息,如果你只有一个最终目标,那就完全没问题了.但是,当您使用多个动态共享对象时,它可能会成为一个问题,因为它们理论上可以使用不同版本的不同编译器进行编译,所有这些都认为它们的不同模板填充空白方法是正确的(我们要争论谁 - 它没有在标准中定义).这意味着模板可能会非常痛苦,但您确实有一些选择.
选择一个编译器(每个操作系统)并坚持下去.仅支持该编译器并要求使用相同的编译器编译所有库.这实际上是一个非常巧妙的解决方案(完全有效).
在内部工作时,只使用模板函数和类.这确实可以省去很多麻烦,但总的来说是非常严格的.就个人而言,我喜欢使用模板.
这种方法效果非常好(特别是在不允许使用不同编译器的情况下).
将此添加到standard.h:
#ifdef MY_LIBRARY_COMPILE
#define MY_LIBRARY_EXTERN
#else
#define MY_LIBRARY_EXTERN extern
#endif
Run Code Online (Sandbox Code Playgroud)
并且在一些消费类定义中(在您声明类本身之前):
// 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;
};
Run Code Online (Sandbox Code Playgroud)
这几乎完全是完美的...编译器不会对你大喊大叫,生活会很好,除非你的编译器开始改变它填充模板的方式,你重新编译其中一个库而不是其他库(即便如此,它可能仍然有效...有时候).
请记住,如果您使用的是部分模板特化(或类型特征或任何更高级的模板元编程功能),则所有生产者及其所有消费者都会看到相同的模板特化.如,如果你有一个专门的实施vector<T>为intS或什么的,如果生产者看到一个int,但消费者不,消费者会很乐意创建错误的类型vector<T>,这将导致各种真的搞砸了错误的.所以要非常小心.
| 归档时间: |
|
| 查看次数: |
16929 次 |
| 最近记录: |