对于非POD类型,正在查找类似offsetof()的内容 我正在寻找一种方法来获取C++类的数据成员的偏移量,该类是非POD性质的。

对于非POD类型,正在查找类似offsetof()的内容 我正在寻找一种方法来获取C++类的数据成员的偏移量,该类是非POD性质的。,c++,hdf5,offsetof,C++,Hdf5,Offsetof,原因如下: 我希望以最适合我的材料(数值模拟输出)的格式存储数据,但它可能是一个面向C的库。我想通过C++接口使用它,它需要我声明像SO这样的存储类型(下面是文档(和4.3.2.1.1)): 其中HOFFSET是一个使用offsetof的HDF特定宏 当然,问题是,一旦示例类变得更加有特色,它就不再是POD类型,因此使用offsetof将给出未定义的结果 我能想到的唯一解决办法是首先将要存储的数据导出到一个更简单的结构,然后将其传递给HDF。然而,这确实涉及到数据复制,这正是HDF试图避免的(以

原因如下:

我希望以最适合我的材料(数值模拟输出)的格式存储数据,但它可能是一个面向C的库。我想通过C++接口使用它,它需要我声明像SO这样的存储类型(下面是文档(和4.3.2.1.1)):

其中HOFFSET是一个使用offsetof的HDF特定宏

当然,问题是,一旦示例类变得更加有特色,它就不再是POD类型,因此使用offsetof将给出未定义的结果

我能想到的唯一解决办法是首先将要存储的数据导出到一个更简单的结构,然后将其传递给HDF。然而,这确实涉及到数据复制,这正是HDF试图避免的(以及为什么他们有这个CompType,它使库能够访问对象以将其数据保存到文件中)

所以我希望你有更好的主意。理想情况下,我会为这个问题寻找一个可移植的解决方案,但如果没有,你可以给我一个想法,使用GCC在x86和x86_64上工作,我已经非常感激了

-----后附:-----

Greg Hewgill在下面建议将数据存储在一个简单的结构中,然后通过从该结构继承来构建实际的类。特别是对于HDF,我认为这实际上可能不起作用。比上面更详细的使用场景:

class base_pod {
public:
    double member_a;
    int member_b;
}; //class base_pod

class derived_non_pod : private base_pod {
public:
    //the following method is only virtual to illustrate the problem
    virtual double get_member_a() {return member_a; }
}; //class derived_non_pod

class that_uses_derived_non_pod {
public:
    void whatever();
private:
    derived_non_pod member_c;
}; //class that_uses_derived_non_pod

现在,当我们存储类的实例时,我们不能把它的内存布局描述成它的成员是一个基本的pod。这将导致偏移量错误,因为派生的非pod添加了时髦的东西(我想是虚拟函数表吧?)

您可以在基类中声明pod类型,然后扩展该类(可能使用
私有继承)以添加附加功能

更新到您的更新:因为
派生的\u非\u pod
的实例也可以被视为
基本的\u pod
,因此到数据成员的偏移量必须相同。关于实现,在布置
派生的非pod
结构时,编译器将在
基本pod
的字段后分配vtable指针


我突然想到,如果使用私有继承,编译器可能能够选择对数据字段重新排序。然而,不太可能这样做,并且继承或保护继承将避免这种可能的陷阱。

< P>问题是一旦你的结构/类不被“C”,C++编译器就可以自由地重新排列和优化结构/类的布局,所以你最终可能会根据编译器重新排序结构。
类C行为有预处理器(例如#pragma pack)标志,但在大多数情况下它们是不可移植的。

Greg Hewgill的解决方案可能比这更可取(可能使用组合而不是继承)

然而,我认为在x86和x86_64上使用GCC时,offsetof实际上会对非POD类型的成员起作用,只要它“有意义”。例如,它不适用于从虚拟基类继承的成员,因为在GCC中,这是通过额外的间接寻址实现的。但是,只要您坚持简单的公共单继承,GCC就会以这样一种方式布局您的对象,这意味着每个成员都可以在对象指针的某个偏移处访问,因此offsetof实现将给出正确的答案

当然,这样做的问题是您必须忽略警告,这意味着如果您做了一些不起作用的事情,您将取消对接近空指针的引用。从好的方面来看,问题的原因在运行时可能是显而易见的。在消极的一面,eew


[编辑:我刚刚在gcc 3.4.4上对此进行了测试,实际上,当获取从虚拟基类继承的成员的偏移量时,警告升级为错误。这很好。我仍然有点担心gcc的未来版本(4,甚至,我不必手动)将更加严格,如果您采用这种方法,您的代码将来可能会停止编译。]

根据您希望的可移植性,您甚至可以在非POD类型上使用offsetof()。它不是严格一致的,但在gcc和MSVC上实现offsetof()的方式中,它将在当前版本和最近的过去使用非POD类型。

是否使用指向成员工作的指针而不是offsetof()?我知道您可能需要进行各种类型的转换才能真正使用指针,因为我猜InsertMember在运行时作用于最后一个参数中指定的类型


但是,使用当前的解决方案,您已经绕过了类型系统,因此我不确定您是否会丢失任何内容。除了指向成员的指针的语法很糟糕之外。

我很确定Roel和onebyone的考虑涵盖了您所要求的大部分内容

struct A
{
  int i;
};

class B: public A
{
public:
  virtual void foo ()
  {
  }
};

int main ()
{
  std::cout << offsetof (B, A::i) << std::endl;
}

现在,如果您将一个成员添加到B或它的一个基中,那么对该表的简单添加将生成正确的HDF类型。

这对我来说很好,我不明白为什么它不会:

#define myOffset(Class,Member) ({Class o; (size_t)&(o.Member) - (size_t)&o;})

只是一件小事:重新排列适用于所有C结构。C(或C++)结构中成员变量的排列方式完全取决于编译器的实现。例如,9.2(12)说,在没有插入访问说明符的情况下声明的成员以声明的顺序出现在内存中(可能带有插入的字节)。因此,可以防止重新排序。“完全依赖于编译器”是一条很好的经验法则。这很有趣——虽然编译器不能完全自由地对成员进行重新排序(正如onebyone所指出的),但我还是很惊讶数据成员的重新排序竟然被允许。我今天学到了一些新东西。嗨,谢谢
struct A1 {
  int i;
};

struct A2 {
  int j;
};

struct A3 : public virtual A2 {
};

class B: public A1, public A3 {
public:
  virtual void foo () {
  }
};

template <typename MostDerived, typename C, typename M>
ptrdiff_t calcOffset (M C::* member)
{
  MostDerived d;
  return reinterpret_cast<char*> (&(d.*member)) - reinterpret_cast<char*> (&d);
}

int main ()
{
  B b;
  std::cout << calcOffset<B> (&A2::j) << ", " 
            << calcOffset<B> (&A1::i) << std::endl;
}
struct H5MemberDef
{
  const char * member_name;
  ptrdiff_t offset;
  H5PredType h5_type;
};


class B  // ....
{
public:

  // ...

  static H5memberDef memberDef[];
};

H5MemberDef B::memberDef[] = {
  { "i", calcOffset<B> (&A1::i), H5::PredType::NATIVE_INT }
  , { "j", calcOffset<B> (&A2::j), H5::PredType::NATIVE_INT }
  , { 0, 0, H5::PredType::NATIVE_INT }
};
H5::CompType func_that_creates_example_CompType(H5MemberDef * pDef) {
  H5::CompType ct;
  while (*pDef->member_name != 0)
  {
    ct.insertMember(pDef->member_name, pDef->offset, pDef->h5_type);
    ++pDef;
  }
  return ct;
}
#define myOffset(Class,Member) ({Class o; (size_t)&(o.Member) - (size_t)&o;})