[ARM-2D 专题]7. OOP实现之继承,宏implement_ex的实现和解析

发布于:2025-06-28 ⋅ 阅读:(13) ⋅ 点赞:(0)

在这里插入图片描述

implement_ex宏是 Arm-2D 库中用于面向对象编程(OOP)支持的核心宏定义。

implement_ex 宏的定义和作用

implement_ex 宏在 Library/Include/arm_2d_utils.h 中定义,用于在 C 语言中实现类似继承的功能:


/*!
 * \note do NOT use this macro directly
 */
#ifdef   __cplusplus
#   define __implement_ex(__type, __name)   __type  __name
#else
#   define __implement_ex(__type, __name)                                       \
            union {                                                             \
                __type  __name;                                                 \
                __type;                                                         \
            }
#endif
/*!
 * \note do NOT use this macro directly
 */
#define __implement(__type)             __implement_ex( __type,                 \
/*!
 * \brief inherit a given class
 * \param __type the base class, you can use .use_as_xxxxx for referencing 
 *               the base.
 * \note this macro supports microsoft extensions (-fms-extensions)
 */
#define implement(__type)               __implement(__type)

/*!
 * \brief inherit a given class and give it an alias name
 * \param __type the base class
 * \param __name an alias name for referencing the base class
 * \note this macro supports microsoft extensions (-fms-extensions)
 */
#define implement_ex(__type, __name)    __implement_ex(__type, __name)

该宏的具体实现根据编译器环境有所不同:

  • C++ 环境下:直接定义为普通的结构体成员
  • C 环境下:使用匿名联合体来实现成员访问的灵活性

实际使用示例

在 Arm-2D 的数据结构中,implement_ex 被广泛使用。例如在 arm_2d_region_t 结构体中:

 /*!
 * \brief a type for an rectangular area
 *
 */
typedef struct arm_2d_region_t {
    implement_ex(arm_2d_location_t, tLocation); //!< the location (top-left corner)
    implement_ex(arm_2d_size_t, tSize);         //!< the size
} arm_2d_region_t;

这里 implement_ex(arm_2d_location_t, tLocation) 表示该结构体继承自基类 arm_2d_location_t,可以通过 tLocation来访问这个基类的成员

与其他相关宏的关系

implement_ex 是一系列 OOP 支持宏的一部分:

  • implement(__type) - 不指定别名的继承
  • implement_ex(__type, __name) - 指定别名的继承
  • inherit(__type)inherit_ex(__type, __name) - 不支持 Microsoft 扩展(-fms-extensions)的版本
    inherit主要作用是:当基类里的成员名称与派生类中的成员名称,或者进行多继承的时候,多个基类里的成员名称存在冲突时,就要使用 inherit 来避免问题。如果不存在冲突时,应该尽可能使用 implement和implement_ex

了解如上的作用和定义后,要完全理解它的功能,我们需要两个基本知识:

匿名结构和联合体的定义和使用

在C语言中,可以定义匿名结构体或联合体,这通常用于创建复杂的数据类型,而不需要为每个单独的成员定义一个名称。
匿名结构体和联合体通常用于以下场景:

  • 简化代码:当不需要为结构体或联合体的每个成员单独命名时,可以使用匿名类型来简化代码。

  • 嵌套类型:在定义更复杂的数据结构时,可以使用匿名类型来避免重复定义相同的结构体或联合体。

  • 接口定义:在定义函数接口时,可以使用匿名类型来定义参数或返回值的结构
    正常结构体定义(联合体union类似,不再赘述):

struct tagVar{
    int a;
    float b;
} var1;

匿名结构体定义(联合体union类似,不再赘述):

struct {
    int a;
    float b;
} var1;

两者唯一的区别就在前者多了tagVar名字,我们可以使用如下方式再次定义其他结构变量:

struct tagVar Var2;

而后者由于没有具体的名称,所以我们无法再次用它去定义其他变量。

Microsoft 扩展(-fms-extensions)

Microsoft 扩展的作用是对匿名结构体(或者联合体)的进一步展开支持,为了理解它的作用,我们以arm2d中的一个数据结构定义arm_2d_region_t 的定义为例来说明其作用:
该结构体定义如下:

/*!
 * \brief a type for an rectangular area
 *
 */
typedef struct arm_2d_region_t {
    implement_ex(arm_2d_location_t, tLocation); //!< the location (top-left corner)
    implement_ex(arm_2d_size_t, tSize);         //!< the size
} arm_2d_region_t;

我们将宏implement_ex展开,得到如下代码:

typedef struct arm_2d_region_t {  
    // 展开 implement_ex(arm_2d_location_t, tLocation)  
    union {  
        arm_2d_location_t tLocation;    //!< the location (top-left corner)  
        arm_2d_location_t;              //!< 匿名成员  
    };  
      
    // 展开 implement_ex(arm_2d_size_t, tSize)    
    union {  
        arm_2d_size_t tSize;           //!< the size  
        arm_2d_size_t;                 //!< 匿名成员
    };  
} arm_2d_region_t;

对于以上代码,我们就可以采用如下方式进行成员访问:

arm_2d_region_t region;  
  
// 通过命名成员访问  
region.tLocation.iX = 10;  
region.tLocation.iY = 20;  
region.tSize.iWidth = 100;  
region.tSize.iHeight = 50;  

这里就引出一个疑问:展开后的匿名结构体arm_2d_location_t和arm_2d_size_t有什么作用呢?是不是很奇怪?
其实,我们本意是希望这个定义完全展开后,形如下面的代码(将匿名结构体也展开):

typedef struct arm_2d_region_t {  
    // tLocation 成员  
    union {  
        struct {  
            int16_t iX;                 //!< x in Cartesian coordinate system  
            int16_t iY;                 //!< y in Cartesian coordinate system  
        } tLocation;  
        struct {  
            int16_t iX;  
            int16_t iY;  
        };  
    };  
      
    // tSize 成员  
    union {  
        struct {  
            int16_t iWidth;             //!< width of an rectangular area  
            int16_t iHeight;            //!< height of an rectangular area  
        } tSize;  
        struct {  
            int16_t iWidth;  
            int16_t iHeight;  
        };  
    };  
} arm_2d_region_t;

那么,我们就可以方便的用如下两种方式来访问成员变量:

arm_2d_region_t region;  
  
// 通过命名成员访问  
region.tLocation.iX = 10;  
region.tLocation.iY = 20;  
region.tSize.iWidth = 100;  
region.tSize.iHeight = 50;  
  
// 通过匿名成员直接访问  
region.iX = 10;  
region.iY = 20;  
region.iWidth = 100;  
region.iHeight = 50;

是不是后面一种方式更简洁和直观?如此一来,写代码会清爽很多了。

要达到上述目的,让编译器将匿名结构体也展开,我们就需要使用-fms-extensions编译选项,明确指示编译器完成这个工作,对匿名结构体进行展开。

关于-fms-extensions
-fms-extensions 是 GCC(GNU Compiler Collection)和 Clang 编译器的一个编译选项,它用于启用对 Microsoft 编译器(MSVC)特定扩展的支持。这些扩展包括:

  • 允许 Microsoft 版本的匿名联合和结构体,这包括对 C11 匿名联合和结构体的支持,以及 Microsoft 特定的变体,如完全省略大括号成员列表,并将成员放置在父命名空间中,即使结构体/联合体有标识符。
  • 在 C++ 中,允许类成员与其类型同名(例如 using foo = int; struct A { foo foo; })。当禁用 ms-extensions 时,这种行为在 C 中是合法的;或者在没有给出 -pedantic 标志的 extern “C” 块中是合法的。对于这种情况的错误消息是 declaration of NAME changes meaning of NAME。
  • 在 C++ 中,允许隐式 int;任何会产生 ISO C++ forbids declaration of NAME with no type 诊断的情况现在都被允许,假设类型为 int。例如:const *p; 或 const f();。
  • 在 C++ 中,允许从命名一个非静态成员函数的 qualified-id 隐式转换为指向成员的指针。在 ISO C++ 中,执行该转换需要 & 运算符。
  • 在 C++ 中,如果 f(一个未限定的标识符)在该上下文中命名一个非重载的成员函数,则允许 &f 形成指向成员的指针。ISO C++ 要求使用类名进行明确的限定。

文章原创,欢迎转载,请注明出处,未经书面允许,不得用于商业用途。


网站公告

今日签到

点亮在社区的每一天
去签到