1

我正在用 C 语言实现一个小程序,它使用一个名为“libhelper.so”的共享库。“libhelper.so”在它的 h 文件中定义了一个结构,但遗憾的是,根据目标系统,这些定义是不同的(libhelper.so 总是由系统提供,而不是我自己提供):

系统A:

struct theStruct {
        int fd;
        unsigned int flags;
        struct config config; // only in System A
        int foo; // in both systems
        int bar; // only in System A
};

系统 B:

struct theStruct {
        int fd;
        unsigned int flags;
        int foo; // in both systems
        int foobar; // only in system B
};

在我的程序中,我认为我只是像这样自己定义该结构:

struct theStruct {
        int fd;
        unsigned int flags;
        struct config config; // only in System A
        int foo; // in both systems
        int foobar; // only in system B
        int bar; // only in System A
};

作为调用“libhelper.so”中函数的结果,我得到了一个“theStruct”实例,现在我可以检查“theStructInstance->bar”或“theStructInstance->foobar”是否填充了有效的值来检测库使用了哪个实现。

但似乎我得到的只是像 1...6 这样的值,它看起来像结构中字段的位置。

有谁知道我该怎么做?

4

4 回答 4

5

不,这行不通。

首先,结构的所有定义必须相同,否则你会得到臭名昭著的 Undefined Behaviour。

其次,看内存布局。bar从结构开始的偏移量应该是多少?第一个和第三个定义不同意这一点(字段最有可能连续定位)。

也许你可以试试工会?

struct theStruct {
        int fd;
        unsigned int flags;
        struct config config;
        int foo; // in both systems
        union {
            int bar; // only in System A
            int foobar; // only in system B
        };
};

如果选择此选项,则应仅bar在系统 A 和foobar系统 B 上使用。


如果两个系统不兼容,并且需要的实际类型bar在系统 B 上不可用(反之亦然),您可以使用以下代码:

struct theStruct {
        int fd;
        unsigned int flags;
        struct config config;
        int foo; // in both systems
#ifdef SYSTEM_A
        int bar; // only in System A
#else
#ifdef SYSTEM_B
        int foobar; // only in system B
#else
#pragma error(either SYSTEM_A or SYSTEM_B must be enabled)
#endif
#endif
};

这样一来,您将始终使用为系统 A 或系统 B 编译的代码,因此您需要有不同的可执行文件(如果您为系统如此不同,这似乎是不可避免的)。

您需要将访问字段的部分代码包装到#ifdefs 中:

#ifdef SYSTEM_A
s.bar = 5;
#endif

-- 否则你会在系统 B 上得到编译错误。

于 2013-06-18T22:31:12.573 回答
2

另一种可能的解决方案是编写依赖于平台的代码来处理每个单独的结构,然后将它们的数据加载到一个公共结构中。然后,这将允许您处理结构的相同成员,无论代码路径如何,而不是始终基于系统引用两个联合成员之一:

struct mystruct;
mystruct.member1 = theStruct.member1; //the common part of the struct
mystruct.member2 = theStruct.member2;
#ifdef platform1
  mystruct.member3 = theStruct.p1member; //specific to platform1
  mystruct.member4 = -1;
#else
  mystruct.member3 = -1; 
  mystruct.member4 = theStruct.p2member; //specific to platform2
#endif
于 2013-06-18T22:45:56.017 回答
1

这是一个思考的方法。

关于我首先做出的假设的一些背景知识。

听起来您有一些函数libraryFunction ()返回指向 a 的指针struct theStruct。但是,实际布局struct theStruct取决于您的应用程序在其上运行的特定系统。在这个结构中是您需要访问的一些信息。您不指定库函数的调用参数或签名,如果指向的指针struct theStruct作为函数值返回,或者指向指针的指针是参数列表的一部分。我会假设它是一个函数返回值。

创建一个您为所需信息定义的结构。创建两个文件,每个文件都有一个函数,该函数接受一个 void 指针和一个指向新结构的指针,然后用库提供的结构中所需的数据填充结构。这两个文件中的每一个都将使用指定的特定系统目标(SystemA 或 SystemB)进行编译,以便您的转换函数将根据目标系统解释库函数提供的结构,并用您想要的数据填充您的结构。

系统 A 的文件 1

// copy of the struct used in System A which is in the library header file
// put here for reference only as should be in the header file
struct theStruct {
    int fd;
    unsigned int flags;
    struct config config; // only in System A
    int foo; // in both systems
    int bar; // only in System A
};

// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
   int foo;
};

void convert2SystemA (void *structPtr, struct *myStruct)
{
   myStruct->foo = ((struct theStruct *)structPtr)->foo;
}

系统 B 的文件 2

// copy of the struct used in System B which is in the library header file
// put here for reference only as should be in the header file
struct theStruct {
    int fd;
    unsigned int flags;
    int foo; // in both systems
    int foobar; // only in system B
};

// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
   int foo;
};

void convert2SystemB (void *structPtr, struct *myStruct)
{
   myStruct->foo = ((struct theStruct *)structPtr)->foo;
}

文件 3 使用转换函数

// my struct that contains the data from struct theStruct that I want
// would be in a header file included into each of these files but here for reference
struct myConvertStruct {
   int foo;
};

{
  struct myConvertStruct myStruct;
  // some function body and now we come to the library call

  if (mySystem == SystemA) {
    void *pStruct = libraryFunction (......);
    convert2SystemA (pStruct, &myStruct);
  } else if (mySystem == SystemB) {
    void *pStruct = libraryFunction (......);
    convert2SystemB (pStruct, &myStruct);
  } else {
     //  some error conditions
  }
  // now use the data that you have pulled as you want to use it

}
于 2013-06-19T00:13:31.810 回答
0

您的建议不起作用的原因foo是系统 A 和系统 B 对成员的偏移量不同。您说您只能在运行时弄清楚您正在使用的系统。因此,当 System B 设置时foo,它可能最终会在内部设置一些东西config

enum system { SystemUnknown, SystemA, SystemB };

struct theStructSystemA {
        int fd;
        unsigned int flags;
        struct config config; // only in System A
        int foo; // in both systems
        int bar; // only in System A
};

struct theStructSystemB {
        int fd;
        unsigned int flags;
        int foo;
        int foobar;
};

struct myStruct {
        union {
            struct theStructSystemA a;
            struct theStructSystemB b;
        } u;
        enum system sys;
};

struct myStruct s = { 0 };

现在,您可以设置bar一些无效值:s.u.a.bar = -1例如。现在,当你调用你的库时,你可以检查:

s.u.a.bar = -1;
some_libhelper_call((void *)&s);
if (s.u.a.bar != -1) s.sys = SystemA;
else s.sys = SystemB;

所以现在,在s.sys已知之后,您可以切换到完全处理已知系统版本的不同代码路径。

于 2013-06-18T23:55:11.473 回答