【问题标题】:c++ using shared libraries if installedc++ 使用共享库(如果已安装)
【发布时间】:2021-05-20 16:02:15
【问题描述】:

如果已安装共享库,我如何有条件地加载和使用它,但如果它不存在,我仍然可以在没有该功能的情况下运行?更具体地说,我可以在不将该库用作插件的情况下做到这一点吗?如果可能的话,我更喜欢在构建时而不是运行时失败。

如果我使用库标志 -lfoo 构建它,它就会构建。但随后它无法运行 libfoo.so.2 未安装在目标系统中。但如果我不添加库标志,则链接失败。

这里有一些代码片段以获得更好的图片。

myAdapter.cpp

#include "newlib/foo.h" //this is from the shared library
...

bool myAdapter::isAvailable()
{
  handle_ = dlopen("libfoo.so.2", RTLD_LAZY);
  if (!handle_)
  {
      return false;
  }

  return true;
}

...

bool myAdapter::init()
{  
  if (!isAvailable())
  {
    return false;
  }

  isInitilized = false;
  isConnected = false;
  if (!fooInit()) // shared library function
  {
    fooCleanup(); //shared library function
    return false;
  }

  // these are my private functions but they call shared library functions.
  if (!createUserParams_() || !setCallbacks_() || !createContext_() || !connect_())
  {
    return false;
  }
  
  return true;
}
...

myApp.cpp

#include "myAdapter.h"
...
int main()
{
...
foo = new myAdapter();
if (!foo.init())
{
cout << "Foo function is not available;
isFooAvailable = false;
}
}

...

}

【问题讨论】:

  • prefer failing during build time rather than runtime if possible. if I build it with the library flag -lfoo, it builds. But then it fails to run it libfoo.so.2 is not installed in the target system. 如果不知道目标系统上是否安装了库,构建时怎么会失败?
  • 我已经在 init() 中添加了 isAvailable() 函数。所以,它在运行时不会走得更远。我只是想在构建期间尽可能多地检查。
  • 无法再编辑我的答案,所以我在这里添加。我想检查我是否正确使用了库 API。此外,共享库有一堆内部结构和枚举,例如错误代码等。我也想利用这些。
  • 这能回答你的问题吗? How to make a shared library delay loaded on Linux。该线程提到了一个帮助脚本项目来为库函数生成存根,这些函数将在首次访问时加载库 - github.com/yugr/Implib.so

标签: c++


【解决方案1】:

首先,删除任何与 libfoo.a、libfoo.sa 或构建中的任何 -lfoo 参数的链接。一切都是动态加载的。

其次,在dlopen 成功后,您需要通过dlsym 获取您需要使用的所有函数的地址 - 并调用这些函数。

第三,不是直接调用你的函数,而是通过你通过 dlsym 加载的指针调用它们。

最后,您的 isAvailable 有副作用(加载库)并且每次都会重新尝试。您只需要加载库一次。

这是您的代码的改进版本。我对这些库函数的参数做了一些假设。更改它以满足您的需求:

class myAdapter
{
    void* handle_;

    typedef bool (*CREATE_USER_PARAMS)(UserParams*);
    typedef int (*CONNECT)(int);

    CREATE_USER_PARAMS createUserParams_;
    CONNECT connect_; 

    myAdapter() : 
        handle_(),
        createUserParams_(),
        connect_()
    {
       handle_ = dlopen("libfoo.so.2", RTLD_NOW);
       if (handle_)
       {
           createUserParams = (CREATE_USER_PARAMS)dlsym(handle_, "createUserParams");
           connect_ = (CONNECT)dlsym(handle_, "connect");
           if (!createUserParams_ || !connect_)
           {
              // error
              dlclose(handle_);
              handle_ = nullptr;
              connect = nullptr_;
              createUserParams_ = nullptr;
           }
       }
    }  

    ~myAdapter()
    {
       if (handle_) 
       {
           dlclose(handle_);
           handle = nullptr;
       }
    }

    bool createUserParams(UserParams* params)
    {
        if (createUserParams_ != nullptr)
        {
            return createUserParams_(params);
        }
        else
        {
           return false;
        }
    }

    int connect(int sock)
    {
        if (connect_ != nullptr)
        {
            return (connect_(sock));
        }
        else
        {
           return -1;
        }
    }
};

【讨论】:

  • 感谢您的详细解答,不胜感激。但这正如我的问题中提到的,使用共享库作为插件。如果任何 API 签名发生更改,这将在运行时失败,例如,如果他们将 init 更改为返回 int 而不是 bool 等。如果可能,我想在构建时捕获这些错误。也许有一些链接标志等。同样,我不知道这是否可能,这就是我问的原因。再次感谢。
猜你喜欢
  • 2019-02-03
  • 1970-01-01
  • 1970-01-01
  • 2016-05-26
  • 1970-01-01
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多