有没有办法准备一个结构以供将来添加? [英] Is there any way to prepare a struct for future additions?

查看:207
本文介绍了有没有办法准备一个结构以供将来添加?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下结构,将用于保存插件信息。我非常肯定这将改变(最可能添加)随着时间的推移。在这里做什么比我假设这个文件是固定的更好吗?

I have the following struct which will be used to hold plugin information. I am very sure this will change (added to most probably) over time. Is there anything better to do here than what I have done assuming that this file is going to be fixed?

struct PluginInfo
{
    public:
        std::string s_Author;
        std::string s_Process;
        std::string s_ReleaseDate;
        //And so on...

        struct PluginVersion
        {
            public:
                std::string s_MajorVersion;
                std::string s_MinorVersion;
                //And so on...
        };
        PluginVersion o_Version;

        //For things we aren't prepared for yet.
        void* p_Future;
};

此外,在为此系统构建共享对象时,我应该采取什么预防措施。我的预感是我会遇到很多库的不兼容性。请帮忙。感谢

Further, is there any precautions I should take when building shared objects for this system. My hunch is I'll run into lots of library incompatibilities. Please help. Thanks

推荐答案

正如别人说的,对于二进制兼容性,你很可能会限制自己使用一个C API。

As was said by someone else, for binary compatibility you will most likely restrict yourself to a C API.

Windows API在许多地方通过将 size 成员添加到结构中来维护二进制兼容性:

The Windows API in many places maintains binary compatibility by putting a size member into the struct:

struct PluginInfo
{
    std::size_t size; // should be sizeof(PluginInfo)

    const char* s_Author;
    const char* s_Process;
    const char* s_ReleaseDate;
    //And so on...

    struct PluginVersion
    {
        const char* s_MajorVersion;
        const char* s_MinorVersion;
        //And so on...
    };
    PluginVersion o_Version;
};

当你创建这样的野兽时,你需要设置 code> member:

When you create such a beast, you need to set the size member accordingly:

PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members

当您针对较新版本的API编译代码,其中 struct 有其他成员,其大小更改,并在 size 成员中注明。 API函数,当传递这样一个 struct 可能会先读其 size 成员和分支以不同的方式处理 struct ,具体取决于其大小。

When you compile your code against a newer version of the API, where the struct has additional members, its size changes, and that is noted in its size member. The API functions, when being passed such a struct presumably will first read its size member and branch into different ways to handle the struct, depending on its size.

当然,这假设进化是线性的,并且新数据总是只在 struct 。也就是说,你永远不会有这种类型的不同版本具有相同的大小。

Of course, this assumes that evolution is linear and new data is always only added at the end of the struct. That is, you will never have different versions of such a type that have the same size.

但是,使用这种野兽是一种确保用户在他们的代码中引入错误的好方法。当他们使用新的API重新编译代码时, sizeof(pluginInfo)会自动适应,但不会自动设置其他成员。通过初始化 struct C方式可以获得相当安全的:

However, using such a beast is a nice way of ensuring that user introduce errors into their code. When they re-compile their code against a new API, sizeof(pluginInfo) will automatically adapt, but the additional members won't be set automatically. A reasonably safety would be gained by "initializing" the struct the C way:

PluginInfo pluginInfo;
std::memset( &pluginInfo, 0, sizeof(pluginInfo) );
pluginInfo.size = sizeof(pluginInfo);

然而,即使抛开一个事实,技术上,归零内存可能不会给每个成员(例如,可能有一些架构,其中所有位设置为零不是浮点类型的有效值),这是令人讨厌和容易出错的,因为它需要三步构造。

However, even putting aside the fact that, technically, zeroing memory might not put a reasonable value into each member (for example, there could be architectures where all bits set to zero is not a valid value for floating point types), this is annoying and error-prone because it requires three-step construction.

一个出路将是围绕该C API设计一个小型内联C ++封装。类似:

A way out would be to design a small and inlined C++ wrapper around that C API. Something like:

class CPPPluginInfo : PluginInfo {
public:
  CPPPluginInfo()
   : PluginInfo() // initializes all values to 0
  {
    size = sizeof(PluginInfo);
  }

  CPPPluginInfo(const char* author /* other data */)
   : PluginInfo() // initializes all values to 0
  {
    size = sizeof(PluginInfo);
    s_Author = author;
    // set other data
  }
};

类甚至可以存储由C struct 在缓冲区中的成员,以便类的用户甚至不必担心。

The class could even take care of storing the strings pointed to by the C struct's members in a buffer, so that users of the class wouldn't even have to worry about that.

编辑:

假设非常相同的 struct 将在API的更高版本中获得一些额外的成员:

Since it seems this isn't as clear-cut as I thought it is, here's an example.
Suppose that very same struct will in a later version of the API get some additional member:

struct PluginInfo
{
    std::size_t size; // should be sizeof(PluginInfo)

    const char* s_Author;
    const char* s_Process;
    const char* s_ReleaseDate;
    //And so on...

    struct PluginVersion
    {
        const char* s_MajorVersion;
        const char* s_MinorVersion;
        //And so on...
    };
    PluginVersion o_Version;

    int fancy_API_version2_member;
};

当链接到旧版本API的插件现在初始化其 struct 像这样

When a plugin linked to the old version of the API now initializes its struct like this

PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members

struct 将是旧版本,缺少新的和闪亮的数据成员从版本2的API。如果现在调用接受 PluginInfo 的指针的第二API的函数,它将传递旧的 PluginInfo ,缩短一个数据成员,到新的API函数。但是,对于版本2的API函数, pluginInfo-> size 将小于 sizeof(PluginInfo)它将能够捕获,并将指针视为指向一个没有 fancy_API_version2_member 的对象。 (可能是主机应用程序的API内部, PluginInfo 是新的和闪亮的一个与 fancy_API_version2_member PluginInfoVersion1 是旧类型的新名称,因此所有新的API都需要转换 PluginInfo * 将插件插入到 PluginInfoVersion1 * 中,然后分支到可以处理这个尘埃岁月的代码。)

its struct will be the old version, missing the new and shiny data member from version 2 of the API. If it now calls a function of the second API accepting a pointer to PluginInfo, it will pass the address of an old PluginInfo, short one data member, to the new API's function. However, for the version 2 API function, pluginInfo->size will be smaller than sizeof(PluginInfo), so it will be able catch that, and treat the pointer as pointing to an object that doesn't have the fancy_API_version2_member. (Presumably, internal of the host app's API, PluginInfo is the new and shiny one with the fancy_API_version2_member, and PluginInfoVersion1 is the new name of the old type. So all the new API needs to do is to cast the PluginInfo* it got handed be the plugin into a PluginInfoVersion1* and branch off to code that can deal with that dusty old thing.)

另一种方法是使用新版本的API编译的插件,其中 PluginInfo 包含 fancy_API_version2_member 插入旧版本的主机应用程序,什么也不知道。再次,主机应用程序的API函数可以通过检查 pluginInfo-> size 是否大于 sizeof / code>自己的 PluginInfo 。如果是这样,插件推测是根据API的新版本编译的,而不是宿主应用程序知道的。 (或者插件写入无法正确初始化 size 成员。请参见下文,了解如何简化这种有点脆弱的方案。)

有两种方法处理:最简单的就是拒绝加载插件。或者,如果可能,主机应用程序可以使用这个无论如何,只是忽略在它的$ PluginInfo 对象的结尾的二进制文件,它不知道如何解释。

然而,后者是棘手的,因为你需要在实现旧的API 时决定这个,而不知道新的API将是什么样子。

The other way around would be a plugin compiled against the new version of the API, where PluginInfo contains the fancy_API_version2_member, plugged into an older version of the host app that knows nothing about it. Again, the host app's API functions can catch that by checking whether pluginInfo->size is greater than the sizeof their own PluginInfo. If so, the plugin presumably was compiled against a newer version of the API than the host app knows about. (Or the plugin write failed to properly initialize the size member. See below for how to simplify dealing with this somewhat brittle scheme.)
There's two ways to deal with that: The simplest is to just refuse to load the plugin. Or, if possible, the host app could work with this anyhow, simply ignoring the binary stuff at the end of the PluginInfo object it was passed which it doesn't know how to interpret.
However, the latter is tricky, since you need to decide this when you implement the old API, without knowing exactly what the new API will look like.

这篇关于有没有办法准备一个结构以供将来添加?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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