我有一些关于如何在共享库之间导出功能的问题,尤其是如果共享库正在使用 managed_pointers 并且我们需要在运行时加载它们。 我有两个应用程序 SampleApplication(executable) 和 Foo Module (foo.dylib)
SampleApplication 和 Foo 模块都依赖于公共模块,在公共模块中我有一个接口让我们说
class FooInterface{
..
void DoSomething()
};
Foo 模块实现 FooInterface 或示例
class Foo_IMPL : public FooInterface {
shared_ptr<FooInterface> create(...);
void DoSomething() override;
};
还有其他模块,如 Foo,这些模块中的每一个都有自己的工厂方法(创建方法),现在的要求是我需要在应用程序需要时动态加载/卸载这些模块。所以我将 Foo 模块设为共享库,并在公共库中编写了一个 dynamicModuleHandler,如下所示
class DynamicHandlerCommon {
protected:
template <typename C>
friend class DynamicHandler;
DynamicHandlerCommon(const std::string& libPath, const std::string& absoluteSerializerPath);
bool load(const std::string& libPath);
void unload();
bool m_isLoaded = false;
std::string m_libPath;
std::string m_absoluteSerializerPath;
bool m_completed = false;
void* m_libraryHandle;
};
template <typename C>
class DynamicHandler
: public DynamicCommon
, public BarObserverInterface
, public std::enable_shared_from_this<DynamicHandler<C>>{
public:
typedef void* (*pFunc)();
static std::unique_ptr<DynamicHandler> create(
std::shared_ptr<ManagerInterface> connection,
const std::string& libPath,
const std::string& absoluteSerializerPath);
DynamicCapabilityAgentHandler(std::shared_ptr<BarObserverInterface> bar,
const std::string& libPath,
const std::string& absoluteSerializerPath);
void onBarStatusChanged(
const BarStatusObserverInterface::Status status,
const BarStatusObserverInterface::ChangedReason reason) override;
void serialize();
void deserialize();
void getFunction();
};
template <typename C>
DynamicHandler<C>::DynamicHandler(std::shared_ptr<BarManagerInterface> barManager,
const std::string& libPath,
const std::string& absoluteSerializerPath)
: DynamicHandlerCommon(libPath, absoluteSerializerPath){
getFunction();
}
template <typename C>
std::unique_ptr<DynamicHandler<C>> DynamicHandler<CapabilityAgentType>::create(
std::shared_ptr<BarManagerInterface> connection,
const std::string& libPath,
const std::string& absoluteSerializerPath) {
std::unique_ptr<DynamicHandler<C>> dynamicHandler( new DynamicHandler<C>>(libPath, absoluteSerializerPath));
barManager->addBarStatusObserver(DynamicHandler<C>::shared_from_this());
return dynamicHandler;
}
template <typename C>
void DynamicHandler<C>::serialize(){
{
std::ofstream os(m_absoluteSerializerPath.c_str());
cereal::JSONOutputArchive archive(os);
//m_component->serialize(archive);
//save(archive)
}
unload();
}
template <typename C>
void DynamicHandler<C>::getFunction(){
C pFunc = reinterpret_cast<C>(dlsym(m_libraryHandle, "createInstance"));
if (!pFunc) {
std::cout << dlerror() << std::endl;
return;
}
C* pInstance = static_cast<C>(pFunc());
}
template <typename C>
void DynamicHandler<C>::deserialize(){
}
template <typename C>
void DynamicHandler<C>::onBarStatusChanged(
const BarStatusObserverInterface::Status status,
const BarStatusObserverInterface::ChangedReason reason){
if(m_completed)
{
return;
}
if(BarStatusObserverInterface::Status::CONNECTED == status){
m_completed = true;
serialize();
}
}
我还在我的 Foo 模块中创建了一个新方法 createInstance,看起来像这样。
Util.h in Foo module
extern "C" {
__attribute__((
__visibility__("default"))) void*
createInstance();
}
Util.cpp in Foo Module
void*
createInstance() {
return Foo_IMPL::create().get();
}
为了提供更多上下文,我正在使用 extern“C”,这样方法名称就不会被管理,但这会导致问题,我不确定我的 SampleApplication 中的 getFunction 中的实现应该是什么,即
template <typename C>
void DynamicHandler<C>::getFunction(){
C pFunc = reinterpret_cast<C>(dlsym(m_libraryHandle, "createInstance"));
if (!pFunc) {
std::cout << dlerror() << std::endl;
return;
}
C* pInstance = static_cast<C>(pFunc());
...
}
基本上,如果我将导出的函数更改为像
extern "C" {
__attribute__((
__visibility__("default"))) std::shared_ptr<FooInterface>
createInstance();
}
然后我得到一个错误说明
error: 'createInstance' has C-linkage specified, but returns incomplete type 'std::shared_ptr<FooInterfaceInterface>' which could be incompatible with C [-Werror,-Wreturn-type-c-linkage]
因此我将导出的函数更改为如下所示
extern "C" {
__attribute__((
__visibility__("default"))) void*
createInstance();
}
void * createInstance() {
return Foo_IMPL::create().get();
}
但现在我不确定我应该如何把它扔回去,因为我得到了错误 在行中分配抽象类类型“FooInterface”的对象 C* pInstance = static_cast(pFunc());其中 C 是 FooInterface
因此,如果我在这里遗漏了什么,以及如果其中一个/某些方法返回智能指针,使用 dlopen/dlsym 加载/卸载共享库的最佳方式是什么,我想获得反馈/意见。如果这不是理想的方式,我可以针对我的情况采用其他替代方法。我最初的想法是创建一个 SampleApplication 可以缓存的接口,然后有一个实现它的包装类,基于是否调用任何方法,它将在运行时加载 foo.lib,以某种方式从中获取实例,然后基本上调用关于它的方法,请对这种方法以及我的用例是否可行发表评论。我的项目仅与此处相关的 c++11 兼容。
--------更新------------ 所以我也试过我的导出功能是这样的,它有点工作
void createInstance(FooInterface* fooInterface) {
fooInterface = Foo_IMPL::create().get();
if(fooInterface){ // here fooInterface is not null
}
}
但是在 SampleApplication 中,如果我的代码是这样的
template <typename C>
void DynamicHandler<C>::getFunction(){
C pFunc = reinterpret_cast<C(dlsym(m_libraryHandle,"createInstance"));
if (!pFunc) {
std::cout << dlerror() << std::endl;
return;
}
C* pInstance = nullptr;
pFunc(pInstance);
if(pInstance){ // here pInstance is null
}
...
}
所以我的猜测是 smart_pointer 在 pFunc(pInstance) 之后被销毁;所以仍然不确定这里最好的选择是什么