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






3.67/5 (3投票s)
一种在纯粹的 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-ids
、class
、subclasses
、descriptors
、protocols
和 interfaces
的实例。
乍一看,这似乎有点复杂
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
/ColiahCore17
的 OOP
的其他方面,从常见的 PC 到资源非常有限的嵌入式架构。
如果是这样,在不久的将来,我将开始讲述 GoliahCore17
的理念