65.9K
CodeProject 正在变化。 阅读更多。
Home

CoreObjects/GoliahCore17:在纯 C 中实现高效的 OOP

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (3投票s)

2018 年 12 月 25 日

CPOL

4分钟阅读

viewsIcon

7536

downloadIcon

94

一种在纯粹的 ANSI C 中处理 OOP 的全新、完善和有效的方法

引言

这是我的第一篇文章,让我介绍一下我的 OOP 本质的起源。

在过去的 30 年里,我一直与 OOP 密切合作,在我能接触到 “Turbo Pascal 5.5”(1989 年)时就开始理解它的原理。不仅理解如何使用它,而且促使我看到它如何工作到汇编级别。多年来,我以同样的方式接触了 Borland、Watcom、Microsoft 等的 C++。直到十年后(1999 年)定义了我自己制作的插件架构,该架构能够集成使用不同编译器生成的对象 DLL,并且具有“按需加载”机制,使程序能够立即启动(我一直讨厌像 Photoshop、Illustrator、Gimp 等软件在启动时等待加载插件)。从那时起,又过去了 20 年,现在是深入探讨文章实质的时候了。

根据维基百科(https://en.wikipedia.org/wiki/Object-oriented_programming#Class-based_vs_prototype-based),强类型基于类的面向对象语言具有以下特性

  • 对象和类
  • 动态调度/消息传递
  • 封装
  • 组合、继承和委托
  • 多态
  • 开放递归

CoreObjects/GoliahCore17 为纯 C 提供了所有这些特性,提供:

  • 巧妙的组织(比某些 OOP 本地语言更好)
  • 一种定义和使用对象的标准和完善的方式(没有魔法宏,也没有曲折的模板技巧)
  • 并且在生成的/执行的汇编代码方面的开销更小(比某些 C++ 编译器)

只有一个真正的缺点,由于需要手动编写 VMT(虚方法表)。
虽然(我在过去的 15 年里一直在非常有限的资源嵌入式设计中工作,所以让我告诉你)当只有几十 KB 可用时,完全控制这方面也至关重要。

路由

这篇文章是围绕简单示例中出现的关键点进行导览:实现一个控制台,在假想的伪代码中定义如下

- class Console
  - extends CoreObject
  - adds some data
  - implements interface CoreStreamOutput defined as:
    - putChar( int charValue )
    - flush()
  - implements interface CoreStreamInput defined as:
    - bool hasChar()
    - int getChar()

输入/输出流的实现基于以下 4 个 <conio.h> 提供的函数

    - putChar(charValue) => _putch(charValue)
    -            flush() => _putch('\n')
    -          hasChar() => _kbhit()
    -          getChar() => _getche()

目标

最后,实例化了一个 Console,并使用其输入/输出流与控制台进行交互。

void Demo1a_Console_main() {
    /* Allocate a memory region to hold the Console instance */
    CoreByte consoleMemoryRegion[sizeof(struct Console)];
    /* Build and initialize the Console instance */
    struct Console* con = Console_build(consoleMemoryRegion);
    /* Take the CoreStreamOutptu interface implementation from the Console instance */
    struct CoreStreamOutput* out = con->Output;
    /* Directly invoke the interface member function putChar, to output a character to the console */
    out->invoke->putChar(out,'C');
    /* Invoke the interface putChar through an inline stub, 
       to output a line-feed character to the console  */
    StreamOutput_putChar(out, '\n');
    /* Invoke the printf-like function to output formatted string 
       to the console through the putChar member function of its CoreStreamOutput interface */
    StreamOutput_printf(out, "Hello %s!\n", "USER");
    StreamOutput_printf(out, "Press a key to terminate...\n");
    /* wait for a key */
    StreamInput_getChar(con->Input);
}

概念

要定义一个对象类,我们必须处理该类的 7 个方面

  • TypeId 唯一标识符
  • 描述符
  • 协议
  • 接口
  • 组件 (Component)
  • 对象

TypeId 唯一标识符

它可以是一个简单的数字常量、一个 UUID,或者任何适合唯一标识类类型的东西。

就本文而言,我们使用一个简单的 enum

enum CoreObjectTypeId {
    CoreObject_TypeId=1
  , CoreStreamOutput_TypeId
  , CoreStreamInput_TypeId
  , Console_TypeId
};

该类是 struct CoreObjectClass 的一个实例,定义如下

struct CoreObjectClass {
  CoreWord32 (*tell)(CoreTarget target, CoreWord16 message, ...);
};

此外,我们必须定义一个函数来分配给 tell 成员。 此函数充当该类的通用服务例程。 就本文而言,我们定义了一个空函数,如下所示

CoreResultCode Console_Class_tell(CoreTarget target, CoreWord16 message, CoreArgumentList* argList) {
 //struct Console* self = (struct Console*)CoreObject_getSelf(ConsoleType->Class, target);
 return CoreResultCode_Success;
}

CoreResultCode Console_tell(CoreTarget target, CoreWord16 message, ...) {
  CoreArgumentList argList[1];
  CoreArgumentList_init(argList, message);
  CoreResultCode resultCode = Console_Class_tell(target,message,argList);
  CoreArgumentList_done(argList);
  return resultCode;
}

描述符

这是一个用于填充运行时猜测的基本信息的结构。

struct CoreObjectDescriptor {
  const struct CoreObjectClass* Class;
  const struct CoreObjectInterface* Interface;
  CoreInt16 TypeId;
  CoreInt16 Size;
  CoreWord32 Options;
  CoreInt32 Offset;
};

协议

这也是众所周知的 VMT,特别是我们去定义仅由实际类添加的方法的部分。 以下是一个示例

struct CoreStreamOutputProtocol {
  CoreResultCode(*putChar)(CoreTarget target, CoreInt16 data);
  CoreResultCode(*flush)(CoreTarget target);
};

接口

这是完整的 VMT,由指向 Class 的指针(如上定义)以及在此接口中实现的所有协议组成。 以下是一个示例

struct ConsoleInterface {
  const struct CoreObjectClass* Class;
  const struct CoreStreamInputProtocol* Input;
  const struct CoreStreamOutputProtocol* Output;
};

组件 (Component)

这是协议的数据对应物。 我们可以在那里定义仅由实际类添加的数据成员

struct ConsoleComponent {
  CoreWord16 __placeHolder; //A __placeHolder only for documentation purpose 
};

对象

这是类实例的实际类型。 它将指向接口实例的指针和所有组件组合在一起

struct Console {
 const struct ConsoleInterface* invoke;
 struct ConsoleComponent Console[1];
 struct CoreStreamOutput Output[1];
 struct CoreStreamInput Input[1];
};

对象类运行时类型信息

现在我们需要一个地方来将所有部分连接在一起,填充与实际类相关的 type-idsclasssubclassesdescriptorsprotocolsinterfaces 的实例。

乍一看,这似乎有点复杂

const struct ConsoleType {
    struct CoreObjectDescriptor Descriptor[1];
    struct CoreObjectClass Class[1];
    struct ConsoleInterface Interface[1];
    struct ConsoleStreamOutputType {
        struct CoreObjectDescriptor Descriptor[1];
        struct CoreObjectClass Class[1];
        struct CoreStreamOutputInterface Interface[1];
        struct CoreStreamOutputProtocol Protocol[1];
    } Output[1];
    struct ConsoleStreamInputType {
        struct CoreObjectDescriptor Descriptor[1];
        struct CoreObjectClass Class[1];
        struct CoreStreamInputInterface Interface[1];
        struct CoreStreamInputProtocol Protocol[1];
    } Input[1];
} ConsoleType[1] = {{
        .Descriptor = { {
                .Class = ConsoleType->Class
            ,    .Interface = (const struct CoreObjectInterface*)ConsoleType->Interface
            ,    .TypeId = Console_TypeId
            ,    .Size = sizeof(struct Console)
            ,    .Options = 0
            ,    .Offset = 0
        } }
    ,    .Class = { {
            .tell=Console_tell
        } }
    ,    .Interface = { {
                .Class = ConsoleType->Class
            ,    .Output = ConsoleType->Output->Protocol
            ,    .Input =  ConsoleType->Input->Protocol
        } }
    ,    .Output = { {
                .Descriptor = { {
                        .Class=ConsoleType->Output->Class
                    ,    .Interface=(const struct CoreObjectInterface*)ConsoleType->Output->Interface
                    ,    .TypeId=CoreStreamOutput_TypeId
                    ,    .Size=sizeof(struct CoreStreamOutput)
                    ,    .Options=CoreObject_Option_Aggregate
                    ,    .Offset=offsetof(struct Console,Output)
                } }
            ,    .Class = { {
                    .tell=Console_tell
                } }
            ,    .Interface = {{
                        .Class = ConsoleType->Output->Class
                    ,    .Output = ConsoleType->Output->Protocol
                    ,    .putChar = Console_StreamOutput_putChar
                    ,    .flush = Console_StreamOutput_flush
                }}
            ,    .Protocol = {{
                        .putChar = Console_StreamOutput_putChar
                    ,    .flush = Console_StreamOutput_flush
                }}
        } }
    ,    .Input = { {
                .Descriptor = { {
                        .Class=ConsoleType->Input->Class
                    ,    .Interface=(const struct CoreObjectInterface*)ConsoleType->Input->Interface
                    ,    .TypeId=CoreStreamInput_TypeId
                    ,    .Size=sizeof(struct CoreStreamInput)
                    ,    .Options=CoreObject_Option_Aggregate
                    ,    .Offset=offsetof(struct Console,Input)
                } }
            ,    .Class = { {
                        .tell=Console_tell
                } }
            ,    .Interface = {{
                        .Class = ConsoleType->Input->Class
                    ,    .Input = ConsoleType->Input->Protocol
                    ,    .hasChar = Console_StreamInput_hasChar
                    ,    .getChar = Console_StreamInput_getChar
                }}            
            ,    .Protocol = {{
                        .hasChar = Console_StreamInput_hasChar
                    ,    .getChar = Console_StreamInput_getChar
                }}

        } }
    } };

生成器

现在我们需要实例化 Console 类,我们可以使用 Console_build 函数来做到这一点

struct Console* Console_build(CoreTarget address) {
    struct Console*self = (struct Console*)address;
    CoreMemory_clearIndirect(self);
    self->invoke = ConsoleType->Interface;
    self->Input->invoke = ConsoleType->Input->Interface;
    self->Output->invoke = ConsoleType->Output->Interface;
    return Console_init(self);
}
//Console_init actually does nothing.
struct Console* Console_init(struct Console* self) {
    return self;
}

如何使用源代码

附加的源代码是将代码片段粘贴到唯一 C 文件中的集合,以便立即将焦点放在主题上。 该软件包还包括创建它的 VisualStudio2017 Community Edition 项目和解决方案。 随意使用它并享受它。

待续...

该主题的广阔性无法容纳在一篇文章中,这只是一个简短的概述。 如果它能引起一些兴趣,我将返回该主题,以处理使用 CoreObjects/ColiahCore17OOP 的其他方面,从常见的 PC 到资源非常有限的嵌入式架构。

如果是这样,在不久的将来,我将开始讲述 GoliahCore17 的理念

GoliahCore17 - C for the Millennials

© . All rights reserved.