easy struct继承&;伪多态与严格别名 如果有人回答我的问题,请不要告诉我使用C++。

easy struct继承&;伪多态与严格别名 如果有人回答我的问题,请不要告诉我使用C++。,c,pointers,inheritance,struct,strict-aliasing,C,Pointers,Inheritance,Struct,Strict Aliasing,所以,我正在用C语言制作一个小型库,它使用面向对象的方法。我选择使用C中两种主要继承方法中较不常见的一种:将基类型的成员复制到派生类型的开头。大概是这样的: struct base { int a; int b; char c; }; struct derived { int a; int b; char c; unsigned int d; void (*virtual_method)(int, char); }; 这种方法不如另一种(作为派生类型第一个成员

所以,我正在用C语言制作一个小型库,它使用面向对象的方法。我选择使用C中两种主要继承方法中较不常见的一种:将基类型的成员复制到派生类型的开头。大概是这样的:

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

struct derived {
  int a;
  int b;
  char c;
  unsigned int d;
  void (*virtual_method)(int, char);
};
这种方法不如另一种(作为派生类型第一个成员的基类型的实例)流行,因为

  • 从技术上讲,不存在标准化的保证,即基本结构和派生结构的第一个公共成员将具有相同的偏移量。但是,除了一个结构已打包而另一个未打包的情况外,在大多数(如果不是所有)已知编译器上,它们的偏移量都相同
  • 这种方法最严重的缺陷是:它违反了严格的别名。将指向派生结构的指针强制转换为其基类型,然后取消引用该指针在技术上是未定义的行为
  • 然而,与其他方法相比,它也有其优点:

  • 减少冗余:访问继承的派生结构的成员与访问未继承的成员相同,而不是强制转换到基类型,然后访问所需的成员
  • 这实际上是真正的继承,而不是组合
  • 它与其他方法一样易于实现,尽管可能需要一些预处理器滥用
  • 我们可以得到一个不成熟的实际多重继承形式,我们可以从几个基本类型继承,但只能强制转换到其中一个
  • 我一直在寻找使我的库能够正确编译并使用强制使用严格别名(如gcc)的编译器而无需用户手动关闭它的可能性。以下是我研究过的可能性:

  • 工会。可悲的是,这些都是否定的,原因有几个:

  • 长篇大论又回来了!要遵循通过联合访问2个结构的第一个公共成员的标准规则,必须(从C99开始)显式使用联合访问第一个公共成员。我们需要特殊的syntaxis来访问联盟中每种类型的成员
  • 空间。考虑继承层次结构。我们有一个类型,我们希望能够从它的每个派生类型强制转换到该类型。我们希望对每种类型都这样做。我看到的唯一可行的使用联合的解决方案是整个层次结构的联合,它必须用于将派生类型的实例转换为基类型。它必须和整个层次结构中最派生的类型一样大
  • 使用
    memcpy
    而不是直接解引用(如)。这看起来是个不错的解决方案。但是,函数调用会产生开销,是的,再次是冗长的。据我所知,
    memcpy
    也可以通过将指向结构的指针强制转换为指向
    char
    的指针,然后取消对它的引用来手动完成,
    (member\u type)(*((char*)(&struct\u pointer->member))=new\u值嘎,又是冗长。这可以用宏来包装。但是,如果我们将指针强制转换为指向不兼容类型的指针,然后将其强制转换为
    char*
    并取消对它的引用,那么这仍然有效吗?这样:
    (成员类型)(*((字符*)(&((结构不兼容的类型*)结构指针)->成员))=新的值

  • 声明我们将强制转换为
    volatile
    的所有类型实例。我想知道为什么不经常出现这种情况<据我所知,code>volatile
    用于告诉编译器指针指向的内存可能会意外更改,从而基于指向的内存段不会更改的假设取消优化,这是所有严格别名问题的原因。当然,这仍然是未定义的行为;但是,对于某些类型的特定实例来说,这难道不是一个可行的跨平台解决方案吗

  • 除上述问题外,还有两个问题:

  • 我上面说的有什么不对吗
  • 我是否错过了一些对我有帮助的东西

  • 我认为你关于通过
    char*
    进行角色转换的想法是无效的。 规则是:

    对象的存储值只能由左值访问 具有以下类型之一的表达式

    表达式的子表达式兼容,但整个表达式不兼容

    我认为唯一现实的方法是组合:

    struct base {
      int a;
      int b;
      char c;
    
      void (*virtual_method)(base*/*this*/,int, char);
    
    };
    
    struct derived {
        struct base;
        unsigned int d;
    };
    
    我意识到这是一种在智力上没有吸引力的继承方式

    PS:我还没有将您的虚拟成员函数指针放在我的派生类中。它需要从
    base
    访问,因此需要在那里声明(假设它是一个多态函数,同时存在于
    base
    derived
    )。
    我还添加了一个
    这个
    参数来丰富模型的触感

    memcpy
    应该是一条出路。 不要担心函数调用开销。通常情况下,没有
    memcpy
    通常是编译器的固有特性,这意味着编译器应该为它内联最有效的代码,并且应该知道在哪里可以优化memcpies

    不要将指针强制转换为不兼容的指针,然后取消引用。这是一条通向未定义行为的道路

    如果您接受表达式语句和gcc的
    ###uuu VA_uargs_uuu
    ,您可以拥有一个
    MC_base_方法(BaseType、BaseMethod、Derived_ptr等)
    宏,该宏使用
    Derived_ptr
    调用
    BaseMethod
    ,只要您可以像使用原始结构的副本一样使用(例如,没有指向结构自身成员的指针)

    下面是一个支持宏sugar的附加OOP示例:

    //Helper macros for some C++-like OOP in plain C 
    #define MC_t_alias(Alias, ...)  typedef __VA_ARGS__ Alias               //like C++'s  using 
    #define Struct(Nm,...) MC_t_alias(Nm, struct Nm); struct Nm __VA_ARGS__ //autypedefed structs
    
    #define ro const //readonly -- I don't like the word const
    
    //Helper macros for method declarations following my 
    //Type__method(Type* X, ...) naming convention
    #define MC_mro(Tp,Meth, ...) Tp##__##Meth(Tp ro*X, ##__VA_ARGS__)
    
    #include <stdio.h>
    #include <string.h>
    //I apend my data structs with _d to know they're data structs
    Struct(base_d, {
      int a;
      int b;
      char c;
    });
    
    Struct(derived_d, {
      int a;
      int b;
      char c;
      unsigned int d;
      void (*virtual_method)(derived_d*, int, char);
    });
    
    //print method is unaware of derived_d 
    //it takes a `base_d const *X` (the mro (method, readonly) macros hides that argument (X==`this` in common OOP speak))
    int MC_mro(base_d,print) 
    {
        return printf("{ a=%d b=%d c=%d }", X->a, X->b, X->c);
    }
    
    /*
        Call a (nonvirtual) base method 
    */
    
    #define MC_base_method(BaseType, Method, Derived_p, ...)                       \
    ({                                                                             \
        int _r; /*if you conventionally return ints*/                                \
                /*otherwise you'll need __typeof__ to get the type*/               \
        BaseType _b;                                                               \
        memcpy(&_b, Derived_p, sizeof(_b));                                        \
        _r = BaseType##__##Method(&_b, ##__VA_ARGS__);                             \
        /*sync back -- for non-readonly methods */                                 \
        /*a smart compiler might be able to get rid of this for ro method calls*/  \
        memcpy(Derived_p, &_b, sizeof(_b));                                        \
        _r;                                                                        \
    })
    
    
    int main()
    {
        derived_d d = {1,2,3,4};
        MC_base_method(base_d, print, &d);
    }