繁体   English   中英

从 C++ 中的动态库访问静态类成员

[英]Access static class members from dynamic library in c++

首先,这是我的第一个问题,我通常会找到答案,如果我犯了一些错误,请见谅。

我正在尝试从动态库访问静态类的成员。 问题是,我不知道为什么,这个静态类成员的内存不一样,这取决于我如何访问它。

我有一个主程序执行多项操作并调用多个动态库(使用 dlopen / dlsym)。 这些动态库应该访问一些属于主程序的变量。 如果这些变量不是静态的,我可以共享一个指针,所以我没有这些问题。 但是,当这些变量是静态的时,我发现了一些问题。

我在 linux 下运行这个程序,使用 g++ 7.5.0 和 glibc 2.27。

这是一个显示我遇到的问题的小例子:主程序main.cpp修改静态变量value并调用动态库libShare.so来创建一个ShareDerived对象。 这个对象访问静态变量value来打印它。

/*** Main.cpp ***/
#include <dlfcn.h>
#include "ShareBase.hpp"

int StaticClass::value = 1;

int main() {
    StaticClass::value = 3;
    void* hdl = dlopen("./lib/libShare.so", RTLD_LAZY | RTLD_GLOBAL);
    ShareBase*(*obj)() = (ShareBase*(*)())dlsym(hdl,"createObject");
    obj()->BaseValue();
    obj()->DerivedValue();
    delete (obj());
    dlclose(hdl);
    return 0;
}

/*** ShareBase.hpp ***/
#pragma once

class StaticClass {
    public:
    StaticClass() = delete;
    ~StaticClass() = delete;
    static int value;
};
class ShareBase {
    public:
    ShareBase();
    virtual ~ShareBase();
    void BaseValue();
    virtual void DerivedValue() = 0;
};

/*** ShareBase.cpp ***/
#include "ShareBase.hpp"
#include <stdio.h>

ShareBase::ShareBase() {}
ShareBase::~ShareBase() {}
void ShareBase::BaseValue() {
    printf("Base: %d --> %p\n", StaticClass::value, (void*)&StaticClass::value);
}

/*** ShareDerived.hpp ***/
#pragma once
#include "ShareBase.hpp"

class ShareDerived:public ShareBase {
    public:
    ShareDerived();
    virtual ~ShareDerived();
    void DerivedValue();
};

extern "C" {
    ShareBase* createObject() {
        auto *m = new ShareDerived();
        return (ShareBase*)m;
    }
}

/*** ShareDerived.cpp ***/
#include "ShareDerived.hpp"
#include <stdio.h>

ShareDerived::ShareDerived() {}
ShareDerived::~ShareDerived() {}
void ShareDerived::DerivedValue() {
    printf("Derived: %d --> %p\n", StaticClass::value, (void*)&StaticClass::value);
    printf("Derived: ");
    BaseValue();
}

这是我用来编译这个例子的 makefile:

CXX         := g++
CXX_FLAGS   := -Wall -Wextra -std=c++17 -ggdb

BIN         := bin
SRC         := src
INC         := include
LIB         := lib

EXE         := test

all: main lib

main: $(BIN)/$(EXE)
    ar rcs $(LIB)/libMain.a $(BIN)/Main.o $(BIN)/ShareBase.o

lib: $(LIB)/libShare.so

$(BIN)/$(EXE): $(BIN)/Main.o $(BIN)/ShareBase.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) $^ -o $@ -ldl

$(LIB)/libShare.so: $(BIN)/ShareDerived.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -shared $^ -o $@ -lMain

$(BIN)/%.o: $(SRC)/%.cpp
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -fPIC -c -o $@ $<

clean:
    -rm $(BIN)/* $(LIB)/*

这是我获得的输出:

Base: 3 --> 0x55e8df87f010 <-- Calling base function from main, static member value is OK.
Derived: 1 --> 0x7f40b5918090 <-- Why this value (and pointer) is not the same as previous one?
Derived: Base: 1 --> 0x7f40b5918090 <-- Accessing to this value calling Base function from Derived class is neither OK.

那么,我的错在哪里? 我可以这样做吗? 为什么行为会根据您访问静态成员的方式而有所不同? 使用相同的指针,调用任何派生类函数,静态变量的值是在调用 main 之前保存的值。 但是,调用基函数时,静态变量的值是您在执行期间修改的值。

如果问题是概念性的; 如何让主程序执行多项操作并调用需要访问某些主程序变量的动态库?

这是我的第一篇文章,所以可能不清楚。 在这种情况下,请告诉我,我将进行编辑以使其更清楚。

编辑:@sam 回答之后,我修改了 makefile,现在它按预期工作

CXX         := g++
CXX_FLAGS   := -Wall -Wextra -std=c++17 -ggdb

BIN         := bin
SRC         := src
INC         := include
LIB         := lib

EXE         := test

all: main lib exec

exec: $(BIN)/$(EXE)

main: $(LIB)/libMain.so

lib: $(LIB)/libShare.so

$(BIN)/$(EXE): $(BIN)/Main.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -Wl,-rpath,$(LIB) $^ -o $@ -ldl -lMain

$(LIB)/libMain.so: $(BIN)/ShareBase.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -shared $^ -o $@

$(LIB)/libShare.so: $(BIN)/ShareDerived.o
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -Wl,-rpath,$(LIB) -shared $^ -o $@ -lMain

$(BIN)/%.o: $(SRC)/%.cpp
    $(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -fPIC -c -o $@ $<

clean:
    -rm $(BIN)/* $(LIB)/*
$(CXX) $(CXX_FLAGS) -I$(INC) -L$(LIB) -shared $^ -o $@ -lMain

您将Main.cpp直接链接到共享库。

您正在运行Main.cpp作为可执行文件的一部分。

结果,静态类成员有重复的实例。 这违反了一个定义规则,并导致未定义的行为。 这是对您观察到的行为的解释。

您很可能这样做是因为没有链接-lMain显示的程序段错误。 发生这种情况的原因完全无关——内联的 C 链接函数永远不会在共享库中实例化,因为编译器在编译共享库时没有理由实例化它。 您可能无法弄清楚原因,但发现链接Main解决了段错误,但导致了这种行为。 这是一个经典的 XY 问题。 您的问题应该是最初为什么会出现段错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM