如何从C+;按需加载动态库+;函数/Qt方法

如何从C+;按需加载动态库+;函数/Qt方法,qt,linker,tcl,shared-libraries,system-calls,Qt,Linker,Tcl,Shared Libraries,System Calls,我创建了动态库,如下所示 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 -lQt

我创建了动态库,如下所示

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进程加载库 然后我发出命令

tclsh 指挥 %加载libmy.so

是否有任何与负载相同的C++函数/Qt,可以从另一个C++函数按需加载共享库。 我的要求是在函数内部的运行时加载动态库,然后直接使用qt函数

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


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

您希望无延迟加载样板文件。在Windows上,MSVC通过发出存根来实现,存根通过函数指针解析函数。你也可以这样做。首先,让我们观察一下,如果只调用函数指针和函数,它们是可以互换的。调用函数或函数指针的语法相同:

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

int main() {
  foo_impl();
  foo();
}
其思想是将函数指针最初设置为thunk,该thunk将在运行时解析实际函数:

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\u thunk
,解析函数地址,并调用real
foo
实现

为此,可以将库拆分为两个库:

  • 库的实现。它不知道需求加载
  • 按需加载存根
  • 可执行文件将链接到需求加载存根库;这要么是静态的,要么是动态的。需求加载存根将在运行时自动解析符号并调用到实现中

    如果您很聪明,您可以为实现设计报头,这样报头本身就可以用来生成所有存根,而无需输入它们的详细信息两次

    完整示例 所有内容如下,也可从

    顶级项目包括:

    • lib1
      -动态库
    • lib1\u需求
      -lib1的静态需求负载thunk
    • main
      -使用
      lib1\u demand
    demand-load-39291032.pro 我们可以在一个单独的标题中考虑聪明度。此标题允许我们定义库接口,以便自动生成thunk

    由于C的限制,需要使用预处理器和有点多余的语法。如果您想仅为C++实现此,则不需要重复参数列表。

    需求负荷 我们将使用
    DEMAND\u load.h
    中的
    DEMAND\u FUN
    而不是直接声明函数。如果在包含标题时定义了
    DEMAND\u LOAD\u LIB1
    ,则它将向库提供一个DEMAND LOAD接口。如果定义了
    DEMAND\u BUILD
    ,它将定义需求负载thunks。如果两者都没有定义,它将提供一个普通接口

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

    lib1/lib1.h 实施过程没有争议:

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

    main/main.pro main/main.cpp lib1_demand/lib1_demand.cpp
    #定义需求#构建
    #包括“lib1/lib1.h”
    #包括
    void(*resolve_LIB1(const char*name))(){
    auto f=QLibrary::resolve(“../lib1/liblib1”,名称);
    返回f;
    }
    
    > p>完全不同于将库加载到C++代码中(Kuber Ober的答案覆盖得很好),加载的代码是错误的;即使您设法加载它,您的代码也会崩溃!这是因为在文件范围内有一个类型为
    Tcl\u Interp
    的变量;这是对Tcl库的错误使用。相反,该库只提供了一种获取解释器上下文句柄的方法,
    Tcl\u CreateInterp()
    (以及一些其他围绕它的函数),该方法返回一个已正确初始化的
    Tcl\u Interp*
    。(严格地说,它实际上返回了一个句柄,该句柄实际上是
    Tcl\u Interp
    的内部子类,因此您自己无法有效地分配一个句柄。)

    该库的正确用法如下:

    Tcl_FindExecutable(NULL); // Or argv[0] if you have it
    Tcl_Interp *interp = Tcl_CreateInterp();
    // And now, you can use the rest of the API as you see fit
    

    这是为了在代码中放入Tcl解释器。反过来说,您可以创建一个
    int My_Init(Tcl_Interp*)
    函数,正如您所描述的,它用于告诉您解释器在哪里,但您不会询问如何加载代码,因为Tcl已经对此提供了合理的支持。

    您看了吗?据我所知,我需要解决所有对QT方法的调用,这些调用似乎并不干净。你能推荐一些更干净的方法吗?我只有几行代码可以在运行时加载共享库好的,我在阅读你的问题时没有想到这一点。如果在运行时使用dlopen加载库,那么没有dllsym是不行的。另一种方法是在编译时链接到库。这个问题是不完整的,并且没有提到你试图用裸露的C++ API来请求加载C++库。即qtcore。共享库是我的案例m,我必须在运行时按需加载libQtCore.so和libQtGui。因此,在我的案例中,对Qt库代码的函数调用大约为80000,因此我理解,每次调用Qt函数都需要demand_FUNC,这对我来说是不实际可行的。如果有更干净的方法,我的问题是使用C++图书馆
    // 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 += ..
    
    #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;
    }
    
    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
    
    #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);
    }
    
    QT = core
    TEMPLATE = lib
    CONFIG += staticlib
    INCLUDEPATH += ..
    DEPENDPATH += ..
    
    SOURCES = lib1_demand.cpp
    HEADERS = ../demand_load.h
    
    #define DEMAND_BUILD
    #include "lib1/lib1.h"
    #include <QLibrary>
    
    void (* resolve_LIB1(const char * name))() {
        auto f = QLibrary::resolve("../lib1/liblib1", name);
        return f;
    }
    
    Tcl_FindExecutable(NULL); // Or argv[0] if you have it
    Tcl_Interp *interp = Tcl_CreateInterp();
    // And now, you can use the rest of the API as you see fit