gpt4 book ai didi

c++ - 共享库中的类和静态变量

转载 作者:IT老高 更新时间:2023-10-28 12:50:45 24 4
gpt4 key购买 nike

我正在尝试用C++编写具有以下架构的内容:

应用->核心(.so)<-插件(.so)

适用于linux,mac和Windows。 Core隐式链接到App,插件通过dlopen/LoadLibrary显式链接到App。我有的问题:
Core中的

  • 静态变量在运行时被复制-插件和App具有不同的副本。
  • 至少在Mac上,当插件返回指向App的指针时,在App中动态强制转换该指针总是导致NULL。

    谁能给我一些针对不同平台的解释和说明?我知道在这里问他们似乎有些懒惰,但是我真的找不到这个问题的系统答案。

    我在entry_point.cpp中为插件做了什么:
    #include "raw_space.hpp"

    #include <gamustard/gamustard.hpp>

    using namespace Gamustard;
    using namespace std;

    namespace
    {
    struct GAMUSTARD_PUBLIC_API RawSpacePlugin : public Plugin
    {
    RawSpacePlugin(void):identifier_("com.gamustard.engine.space.RawSpacePlugin")
    {
    }

    virtual string const& getIdentifier(void) const
    {
    return identifier_;
    }

    virtual SmartPtr<Object> createObject(std::string const& name) const
    {
    if(name == "RawSpace")
    {
    Object* obj = NEW_EX RawSpaceImp::RawSpace;
    Space* space = dynamic_cast<Space*>(obj);
    Log::instance().log(Log::LOG_DEBUG, "createObject: %x -> %x.", obj, space);
    return SmartPtr<Object>(obj);
    }
    return SmartPtr<Object>();
    }

    private:
    string identifier_;
    };

    SmartPtr<Plugin> __plugin__;
    }

    extern "C"
    {
    int GAMUSTARD_PUBLIC_API gamustardDLLStart(void) throw()
    {
    Log::instance().log(Log::LOG_DEBUG, "gamustardDLLStart");
    __plugin__.reset(NEW_EX RawSpacePlugin);
    PluginManager::instance().install(weaken(__plugin__));
    return 0;
    }

    int GAMUSTARD_PUBLIC_API gamustardDLLStop(void) throw()
    {
    PluginManager::instance().uninstall(weaken(__plugin__));
    __plugin__.reset();
    Log::instance().log(Log::LOG_DEBUG, "gamustardDLLStop");
    return 0;
    }
    }
  • 最佳答案

    一些背景
    C++中的共享库非常困难,因为该标准没有对它们进行任何说明。这意味着每个平台都有不同的处理方式。如果我们将自己限制在Windows和某些* nix变体(ELF的任何形式)上,则区别是微妙的。第一个区别是Shared Object Visibility。强烈建议您阅读该文章,这样您可以很好地了解什么是可见性属性以及它们对您的作用,这将帮助您避免链接器错误。
    无论如何,您最终都会得到如下所示的内容(适用于许多系统的编译):

    #if defined(_MSC_VER)
    # define DLL_EXPORT __declspec(dllexport)
    # define DLL_IMPORT __declspec(dllimport)
    #elif defined(__GNUC__)
    # define DLL_EXPORT __attribute__((visibility("default")))
    # define DLL_IMPORT
    # if __GNUC__ > 4
    # define DLL_LOCAL __attribute__((visibility("hidden")))
    # else
    # define DLL_LOCAL
    # endif
    #else
    # error("Don't know how to export shared object libraries")
    #endif
    接下来,您需要制作一些共享的 header ( standard.h?),并在其中添加一个漂亮的 #ifdef小东西:
    #ifdef MY_LIBRARY_COMPILE
    # define MY_LIBRARY_PUBLIC DLL_EXPORT
    #else
    # define MY_LIBRARY_PUBLIC DLL_IMPORT
    #endif
    这使您可以标记类,函数以及类似的内容:
    class MY_LIBRARY_PUBLIC MyClass
    {
    // ...
    }

    MY_LIBRARY_PUBLIC int32_t MyFunction();
    这将告诉构建系统在调用函数时在哪里寻找它们。
    现在:到实际点!
    如果要在库之间共享常量,那么实际上就不必关心它们是否重复,因为常量应该很小,并且重复可以进行很多优化(这很好)。但是,由于您似乎在与非常量一起工作,因此情况有所不同。有十亿个模式可以在C++中创建跨库单例,但是我自然很喜欢自己的方式。
    在某些头文件中,假设您要共享一个整数,因此您需要在 myfuncts.h中:
    #ifndef MY_FUNCTS_H__
    #define MY_FUNCTS_H__
    // include the standard header, which has the MY_LIBRARY_PUBLIC definition
    #include "standard.h"

    // Notice that it is a reference
    MY_LIBRARY_PUBLIC int& GetSingleInt();

    #endif//MY_FUNCTS_H__
    然后,在 myfuncts.cpp文件中,您将具有:
    #include "myfuncs.h"

    int& GetSingleInt()
    {
    // keep the actual value as static to this function
    static int s_value(0);
    // but return a reference so that everybody can use it
    return s_value;
    }
    处理模板
    C++具有超强大的模板,这很棒。但是,跨库推送模板确实很痛苦。当编译器看到一个模板时,它就是“填写您想要完成此工作的所有内容”的消息,如果您只有一个最终目标,则这很好。但是,当您使用多个动态共享库时,这可能会成为一个问题,因为理论上它们都可以使用不同版本的不同编译器进行编译,所有这些编译器都认为它们的不同模板空白方法是正确的。 (以及我们要争论的人-标准中未定义)。这意味着模板可能会非常痛苦,但是您确实有一些选择。
    不允许使用其他编译器。
    选择一个编译器(每个操作系统)并坚持使用。仅支持该编译器,并要求所有库都使用同一编译器进行编译。这实际上是一个非常简洁的解决方案(完全有效)。
    不要在导出的函数/类中使用模板
    仅在内部工作时才使用模板函数和类。这确实节省了很多麻烦,但是总体上来说,它的局限性很大。就个人而言,我喜欢使用模板。
    强制导出模板并希望获得最好的
    这出奇地好(特别是在与不允许不同的编译器配对时)。
    将此添加到 standard.h:
    #ifdef MY_LIBRARY_COMPILE
    #define MY_LIBRARY_EXTERN
    #else
    #define MY_LIBRARY_EXTERN extern
    #endif
    并且在一些消耗性的类定义中(在声明类本身之前):
    //    force exporting of templates
    MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC std::allocator<int>;
    MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC std::vector<int, std::allocator<int> >;

    class MY_LIBRARY_PUBLIC MyObject
    {
    private:
    std::vector<int> m_vector;
    };
    这几乎是完全完美的……除非您的编译器开始改变它填充模板的方式,并且您重新编译其中一个库,而不是重新编译另一个库,否则编译器不会对您大吼大叫,生活会很好。可能仍然可以...有时)。
    请记住,如果您使用的是部分模板特化(或类型特征或任何更高级的模板元编程内容)之类的东西,那么所有生产者及其所有使用者都将看到相同的模板特化。就像在其中,如果您为 vector<T>或其他内容专门实现了 int的实现,如果生产者看到了 int的实现,但消费者没有看到,则消费者会很乐意创建错误的 vector<T>类型,这将导致各种真正的错误。错误。所以要非常小心。

    关于c++ - 共享库中的类和静态变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2505385/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com