如何从C ++函数/Qt方法按需加载动态库 [英] How to load a dynamic library on demand from a C++ function/Qt method

查看:176
本文介绍了如何从C ++函数/Qt方法按需加载动态库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有如下创建的动态库

I have dynamic library created as follows

cat myfile.cc

cat myfile.cc

struct Tcl_Interp;
extern "C" int My_Init(Tcl_Interp *) { return 0; }

1)编译cc文件

g++ -fPIC -c myfile.cc

2)创建共享库

g++ -static-libstdc++ -static-libgcc -shared -o libmy.so myfile.o -L/tools/linux64/qt-4.6.0/lib -lQtCore -lQtGui

3)从TCL进程加载库 然后我给出命令

3) load the library from a TCL proc then I give command

tclsh 和给定的命令 %加载libmy.so

tclsh and given command % load libmy.so

是否有任何等效的C ++函数/Qt可以根据需要从另一个C ++函数加载共享库.

is there any C++ function/ Qt equivalent to load that can load the shared library on demand from another C++ function.

我的要求是在运行时在函数内部加载动态库,然后直接使用qt函数

My requirement is to load the dynamic library on run time inside the function and then use the qt functions directly

1)加载qt共享库(对于lib1.so) 2)直接调用函数而无需任何解决方法

1) load the qt shared libraries (for lib1.so) 2) call directly the functions without any call for resolve

例如,我们有dopen,但是对于每个函数调用,我们必须调用dsym.我的要求是只调用共享库,然后直接调用这些函数.

For example we have dopen, but for that for each function call we have to call dsym. My requirement is only call for shared library then directly call those functions.

推荐答案

您要减少样板的延迟加载.在Windows上,MSVC通过发出通过函数指针解析功能的存根来实现延迟加载.你也可以做到的.首先,让我们观察一下,如果您所要做的只是调用它们,则函数指针和函数是可互换的.调用函数或函数指针的语法是相同的:

You want boilerplate-less delay loading. On Windows, MSVC implements delay loading by emitting a stub that resolves the function through a function pointer. You can do the same. First, let's observe that function pointers and functions are interchangeable if all you do is call them. The syntax for invoking a function or a function pointer is the same:

void foo_impl() {}
void (*foo)() = foo_impl;

int main() {
  foo_impl();
  foo();
}

这个想法是将函数指针最初设置为一个thunk,它将在运行时解析实际函数:

The idea is to set the function pointer initially to a thunk that will resolve the real function at runtime:

extern void (*foo)();
void foo_thunk() {
  foo = QLibrary::resolve("libmylib", "foo");
  if (!foo) abort();
  return foo();
}
void (*foo)() = foo_thunk;

int main() {
  foo(); // calls foo_thunk to resolve foo and calls foo from libmylib
  foo(); // calls foo from libmylib
}

第一次调用foo时,它将真正调用foo_thunk,解析函数地址并调用实际的foo实现.

When you first call foo, it will really call foo_thunk, resolve the function address, and call real foo implementation.

为此,您可以将库分成两个库:

To do this, you can split the library into two libraries:

  1. 库的实现.它不了解需求负载.
  2. 需求负载存根.

可执行文件将链接到需求负载存根库;是静态的还是动态的.需求负载存根将在运行时自动解析符号并调用实现.

The executable will link to the demand-load stub library; that is either static or dynamic. The demand-load stub will automatically resolve the symbols at runtime and call into the implementation.

如果您很聪明,则可以设计实现的标头,以便标头本身可以用于生成所有存根,而无需两次输入其详细信息.

If you're clever, you can design the header for the implementation such that the header itself can be used to generate all the stubs without having to enter their details twice.

所有内容都可以从 https://上找到. github.com/KubaO/stackoverflown/tree/master/questions/demand-load-39291032

顶级项目包括:

  • lib1-动态库
  • lib1_demand-lib1
  • 的静态需求负荷重击
  • main-使用lib1_demand
  • 的应用程序
  • lib1 - the dynamic library
  • lib1_demand - the static demand-load thunk for lib1
  • main - the application that uses lib1_demand
TEMPLATE = subdirs
SUBDIRS = lib1 lib1_demand main
main.depends = lib1_demand
lib1_demand.depends = lib1

我们可以将聪明程度分解到一个单独的标题中.此标头允许我们定义库接口,以便可以自动生成thunk.

We can factor out the cleverness into a separate header. This header allows us to define the library interface so that the thunks can be automatically generated.

由于C的限制,需要大量使用预处理器和某种程度上多余的语法.如果您只想针对C ++实现此功能,则无需重复参数列表.

The heavy use of preprocessor and a somewhat redundant syntax is needed due to limitations of C. If you wanted to implement this for C++ only, there'd be no need to repeat the argument list.

// Configuration macros:
// DEMAND_NAME - must be set to a unique identifier of the library
// DEMAND_LOAD - if defined, the functions are declared as function pointers, **or**
// DEMAND_BUILD - if defined, the thunks and function pointers are defined

#if defined(DEMAND_FUN)
#error Multiple inclusion of demand_load.h without undefining DEMAND_FUN first.
#endif

#if !defined(DEMAND_NAME)
#error DEMAND_NAME must be defined
#endif

#if defined(DEMAND_LOAD)
// Interface via a function pointer
#define DEMAND_FUN(ret,name,args,arg_call) \
    extern ret (*name)args;

#elif defined(DEMAND_BUILD)
// Implementation of the demand loader stub
#ifndef DEMAND_CAT
#define DEMAND_CAT_(x,y) x##y
#define DEMAND_CAT(x,y) DEMAND_CAT_(x,y)
#endif
void (* DEMAND_CAT(resolve_,DEMAND_NAME)(const char *))();
#if defined(__cplusplus)
#define DEMAND_FUN(ret,name,args,arg_call) \
    extern ret (*name)args; \
    ret name##_thunk args { \
        name = reinterpret_cast<decltype(name)>(DEMAND_CAT(resolve_,DEMAND_NAME)(#name)); \
        return name arg_call; \
    }\
    ret (*name)args = name##_thunk;
#else
#define DEMAND_FUN(ret,name,args,arg_call) \
    extern ret (*name)args; \
    ret name##_impl args { \
        name = (void*)DEMAND_CAT(resolve_,DEMAND_NAME)(#name); \
        name arg_call; \
    }\
    ret (*name)args = name##_impl;
#endif // __cplusplus

#else
// Interface via a function
#define DEMAND_FUN(ret,name,args,arg_call) \
    ret name args;
#endif

然后,动态库本身:

TEMPLATE = lib
SOURCES = lib1.c
HEADERS = lib1.h
INCLUDEPATH += ..
DEPENDPATH += ..

我们将直接使用demand_load.h中的DEMAND_FUN,而不是直接声明函数.如果在包含标头时定义了DEMAND_LOAD_LIB1,它将为库提供按需加载接口.如果定义了DEMAND_BUILD,它将定义需求负载重击.如果两者都没有定义,它将提供一个普通的界面.

Instead of declaring the functions directly, we'll use DEMAND_FUN from demand_load.h. If DEMAND_LOAD_LIB1 is defined when the header is included, it will offer a demand-load interface to the library. If DEMAND_BUILD is defined, it'll define the demand-load thunks. If neither is defined, it will offer a normal interface.

我们注意取消定义特定于实现的宏,以使全局名称空间不会受到污染.然后,我们可以在项目中包含多个库,每个库都可以在按需加载和非按需加载之间进行单独选择.

We take care to undefine the implementation-specific macros so that the global namespace is not polluted. We can then include multiple libraries the project, each one individually selectable between demand- and non-demand loading.

#ifndef LIB_H
#define LIB_H

#ifdef __cplusplus
extern "C" {
#endif

#define DEMAND_NAME LIB1
#ifdef DEMAND_LOAD_LIB1
#define DEMAND_LOAD
#endif
#include "demand_load.h"
#undef DEMAND_LOAD

DEMAND_FUN(int, My_Add, (int i, int j), (i,j))
DEMAND_FUN(int, My_Subtract, (int i, int j), (i,j))

#undef DEMAND_FUN
#undef DEMAND_NAME

#ifdef __cplusplus
}
#endif

#endif

实现过程无可争议:

#include "lib1.h"

int My_Add(int i, int j) {
    return i+j;
}

int My_Subtract(int i, int j) {
    return i-j;
}

对于这样一个库的用户,需求量减少到定义一个宏并使用thunk库lib1_demand而不是动态库lib1.

For the user of such a library, demand loading is reduced to defining one macro and using the thunk library lib1_demand instead of the dynamic library lib1.

if (true) {
   # Use demand-loaded lib1
   DEFINES += DEMAND_LOAD_LIB1
   LIBS += -L../lib1_demand -llib1_demand
} else {
   # Use direct-loaded lib1
   LIBS += -L../lib1 -llib1
}
QT = core
CONFIG += console c++11
CONFIG -= app_bundle
TARGET = demand-load-39291032
TEMPLATE = app
INCLUDEPATH += ..
DEPENDPATH += ..
SOURCES = main.cpp

main/main.cpp

#include "lib1/lib1.h"
#include <QtCore>

int main() {
    auto a = My_Add(1, 2);
    Q_ASSERT(a == 3);
    auto b = My_Add(3, 4);
    Q_ASSERT(b == 7);
    auto c = My_Subtract(5, 7);
    Q_ASSERT(c == -2);
}

最后,执行重击.在这里,我们可以选择使用dlopen + dlsymQLibrary.为简单起见,我选择了后者:

Finally, the implementation of the thunk. Here we have a choice between using dlopen+dlsym or QLibrary. For simplicity, I opted for the latter:

QT = core
TEMPLATE = lib
CONFIG += staticlib
INCLUDEPATH += ..
DEPENDPATH += ..

SOURCES = lib1_demand.cpp
HEADERS = ../demand_load.h

lib1_demand/lib1_demand.cpp

#define DEMAND_BUILD
#include "lib1/lib1.h"
#include <QLibrary>

void (* resolve_LIB1(const char * name))() {
    auto f = QLibrary::resolve("../lib1/liblib1", name);
    return f;
}

这篇关于如何从C ++函数/Qt方法按需加载动态库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆