易结构继承和放大器;伪多态性VS严格别名 [英] easy struct inheritance & pseudo-polymorphism vs strict aliasing

查看:148
本文介绍了易结构继承和放大器;伪多态性VS严格别名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果有人回答我的问题,请不要告诉我的使用C ++

所以,我正在用C一个小型图书馆,它使用面向对象的方法。我选择使用不太常见的两种主要方法继承在C:基类型的成员复制到派生类型的开始。事情是这样的:

 结构基地{
  int类型的;
  INT B:
  焦炭℃;
};结构派生{
  int类型的;
  INT B:
  焦炭℃;
  unsigned int类型D组;
  无效(* virtual_method)(INT,CHAR);
};

此方法比另一个(基类型作为派生类型的第一个成员的一个实例),因为

冷门

  1. 技术上,没有standartized保证基体的第一公共成员和衍生的结构将具有相同的偏移量。然而,随着情况除外时,结构中的一个被填充,另一个是没有,他们将有最相同的偏移,如果不是全部,已知的编译器。

  2. 这种方法最严重的缺陷:它的违反严格别名的。铸造一个指向派生结构其基本类型,然后取消引用指针在技术上是未定义的行为。

然而,相较于其他的方法也有它的好处:


  1. 详细程度减:访问已继承了衍生结构的成员是一样的访问还没有被继承的,而不是铸造的基本类型和然后的访问所需要的成员;

  2. 这其实是真正的继承的而不是组成的;

  3. 这是容易实现的另一种方法,虽然有点preprocessor滥用可能需要;

  4. 我们可以得到实际的多重继承,在这里我们可以从几个基本类型继承的半生不熟的形式,但可以转换为只是其中之一。

我一直在寻找进入的可能性,让我的图书馆编译,并与严格执行走样编译器正常工作(例如 GCC )无需用户手动将其关闭。下面是我看着的可能性:


  1. 工会。这是可悲的是,一个禁忌有以下几个原因:


    1. 详细程度的回报!要遵循标准的规则,通过工会访问的2结构的第一个成员共同,我们必须(如C99)的明确使用联合来访问的第一个共同的成员。我们需要特殊的构造结来访问每个类型的成员在工会!

    2. 空间。考虑一个继承层次。我们有我们希望能够从转换为一个类型的每个的其派生类型。我们希望这样做对的每个的类型。唯一可行的用人工会解决方案,我看到的是,将不得不被用来派生类型的实例转换为基本类型的整个层次结构的联合。它必须是就像在整个层次最派生类型...


  2. 使用的memcpy ,而不是直接间接引用(喜欢这里一>)。这看起来像一个很好的解决方案。但是,函数调用招致的开销,是的,再次,详细程度。据我了解,是什么的memcpy 确实也可以通过铸造一个指向一个结构的指针字符手动完成然后取消引用它,像这样:(MEMBER_TYPE)(*((字符*)(安培; struct_pointer->成员)))= NEW_VALUE; 尔加,冗长又。那么,这可以用一个宏包裹。但仍然工作,如果我们铸造了指针的指针不兼容的类型,而然后的它铸造于的char * 并取消引用它?像这样:(MEMBER_TYPE)(*((字符*)(及((结构incompatible_type *)struct_pointer) - GT;成员)))= NEW_VALUE;


  3. 宣布,我们将要转换为挥发性类型的所有实例。我不知道为什么这不上来频繁。 挥发性是,据我所知,用来告诉内存由指针指向编译器可能会意外更改,因此基于这样的假设取消的优化pointed-的片段内存是不会改变的,这是所有严格走样问题的原因。这是当然,仍然不确定的行为;但无法将其对某些类型的某些情况下hackishly禁用严格别名优化技术可行的跨平台解决方案?


除了上述问题,这里有两个:


  1. 是我上面的错误?

  2. 有我错过了什么,可能在我的情况帮助?


解决方案

我不认为你关于通过的char * 有效铸的想法。
规则是:


  

对象应具有其存储的价值只能通过左值访问
  具有下列类型之一前pression


你的前pression的子前pression是兼容的,但整体的前pression不兼容。

我想唯一现实的办法是组成:

 结构基地{
  int类型的;
  INT B:
  焦炭℃;  无效(* virtual_method)(基* / * *本/,INT,CHAR);};结构派生{
    结构基础;
    unsigned int类型D组;
};

我知道这是实现继承一个理智的吸引力的方式。

PS:我没有把你的虚成员函数指针在我的派生类。它需要访问从因此被宣布有需求(假设它的存在对于基地多态函数导出)。
我还添加了一个这个参数来充实模式触摸。

If anybody answers my question, please don't tell me to use C++.

So, I'm making a small library in C that uses an object-oriented approach. I chose to use the less-common of the two main approaches to inheritance in C: copying the members of the base type to the beginning of the derived type. Something like this:

struct base {
  int a;
  int b;
  char c;
};

struct derived {
  int a;
  int b;
  char c;
  unsigned int d;
  void (*virtual_method)(int, char);
};

This approach is less popular than the other one (an instance of the base type as the first member of the derived type) because

  1. technically, there is no standartized guarantee that the first common members of the base and derived structs will have the same offsets. However, with the exception of cases when one of the structs is packed and the other is not, they will have the same offsets on most, if not all, known compilers.
  2. this approach's most serious flaw: it violates strict aliasing. Casting a pointer to a derived struct to its base type and then dereferencing the pointer is technically undefined behaviour.

However, it also has its benefits compared to the other approach:

  1. Less verbosity: accessing a member of a derived struct that has been inherited is the same as accessing one that has not been inherited, instead of casting to the base type and then accessing the needed member;
  2. This is actually real inheritance and not composition;
  3. It is as easy to implement as the other approach, although a little preprocessor abuse may be needed;
  4. We can get a half-baked form of actual multiple inheritance, where we can inherit from several base types, but can cast to only one of them.

I have been looking into possibilities for making my library compile and work correctly with compilers that enforce strict aliasing (like gcc) without the user needing to turn it off manually. Here are the possibilities that I've looked into:

  1. Unions. These are, sadly, a no-no for several reasons:

    1. Verbosity returns! To follow the standard's rules for accessing the first common members of 2 structs via a union, one must (as of C99) explicitly use the union to access the first common members. We'd need special syntaxis to access members of each type in the union!
    2. Space. Consider an inheritance hierarchy. We have a type that we want to be able to cast to from each of its derived types. And we want to do it for every type. The only feasible union-employing solution I see is a union of the entire hierarchy that would have to be used to convert instances of a derived type to a base type. And it would have to be just as large as the most derived type in the entire hierarchy...

  2. Using memcpy instead of direct dereferencing (like here). That looks like a nice solution. However, the function call incurs an overhead, and yes, once again, verbosity. As I understand, what memcpy does can also be done manually by casting a pointer to a struct to a pointer to char and then dereferencing it, something like this: (member_type)(*((char*)(&struct_pointer->member))) = new_value; Gah, verbosity again. Well, this can be wrapped with a macro. But will that still work if we've casted our pointer to a pointer to an incompatible type, and then casted it to char* and dereferenced it? Like this: (member_type)(*((char*)(&((struct incompatible_type*)struct_pointer)->member))) = new_value;

  3. Declaring all instances of types that we're going to cast as volatile. I wonder why this doesn't come up often. volatile is, as I understand, used to tell the compiler that the memory pointed to by a pointer may change unexpectedly, thus cancelling optimizations based on the assumption that a segment of pointed-to memory is not going to change, which is the cause of all strict-aliasing problems. This is, of course, still undefined behaviour; but can't it be a feasible cross-platform solution for "hackishly" disabling strict aliasing optimisations for certain instances of certain types?

Aside from the questions above, here's two more:

  1. Is something I said above erroneous?
  2. Have I missed something that could help in my case?

解决方案

I don't think your idea about casting via char* is valid. The rule is:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types

A sub-expression of your expression is compatible but the overall expression isn't compatible.

I think the only realistic approach is composition:

struct base {
  int a;
  int b;
  char c;

  void (*virtual_method)(base*/*this*/,int, char);

};

struct derived {
    struct base;
    unsigned int d;
};

I realize that's an intellectually unappealing way to achieve inheritance.

PS: I haven't put your virtual member function pointer in my derived class. It needs to be accessible from base so needs to be declared there (assuming it's a polymorphic function that exists for both base and derived). I've also added a this parameter to flesh out the model a touch.

这篇关于易结构继承和放大器;伪多态性VS严格别名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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