SystemFramework 库
SystemFramework 定义了接口、类和类型,以支持具有自己的垃圾回收器、委托等的原生运行时系统。SystemFramework 类的设计与 .NET Framework 的类相似。
引言
标准原生 C++ 没有真正的面向对象的垃圾回收器、面向对象的函数指针(委托)、真正面向对象的异常处理和面向对象的多线程系统。但 CLR C++,实际上是 .NET Framework,拥有所有这些东西。几年前,当我使用 C++ 在不支持 .NET Framework 的平台上编写应用程序时,我深切地感到缺乏所有这些功能。幸运的是,我在 CodeProject 上找到了这篇文章:FastDelegate.aspx。
在研究了 FastDelegate 文章的内容以及更多关于实现垃圾回收器的文章后,我决定编写我自己的 C++ 原生系统框架,它将允许几乎所有 .NET Framework 和 C# 语法中的优点。让所有东西都活起来是一个非常紧张的过程,但谢天谢地,“类模板的局部特化”极大地帮助了我。
好了,这里是 SystemFramework,它包含了智能指针、委托和多播委托、枚举类以及标记-清除垃圾回收器,这样您就不再需要调用 free
或 delete
来释放分配的内存了。我还支持使用相同的 SystemFramework DLL 来创建我自己的 DLL 样式的程序集。我还添加了一个非常简单的 XML 解析器。所有类都在代码中进行了文档记录,并且可以使用 Doxygen 工具生成帮助文件,该工具可以在 http://www.stack.nl/~dimitri/doxygen/ 上找到。
已完全或部分实现的支持的 .NET 命名空间摘要
cli |
cli 命名空间包含支持运行时框架基础结构的内部结构、类和模板,不打算直接从您的代码中使用。 |
cli::XML |
用于读取、写入和解析 XML 数据的辅助和工具类。 |
cli::XML::Streaming |
包含用于流式处理 XML 的辅助类和模板的命名空间。 |
系统 |
System 命名空间包含根据 .NET Framework 系统数据类型设计的结构和类。这些基础是运行时系统的核心,并赋予了以 .NET 风格编写运行时应用程序的能力,并支持几乎所有功能。 |
System::Collections |
System.Collections 命名空间包含定义对象各种集合的接口和类,例如列表、队列、位数组、哈希表和字典。 |
System::ComponentModel |
System.ComponentModel 命名空间提供了用于实现组件和控件的运行时和设计时行为的类。 |
System::Diagnostics |
System.Diagnostics 命名空间提供了允许您与系统进程、事件日志和调试跟踪进行交互的类。 |
System::IO |
System.IO 命名空间包含允许读写文件和数据流的类型,以及提供基本文件和目录支持的类型。 |
System::IO::Ports |
System.IO.Ports 命名空间包含用于控制串行端口的类。最重要的类,System.IO.Ports.SerialPort ,为同步和事件驱动 I/O、引脚和中断状态的访问以及串行驱动程序属性的访问提供了一个框架。 |
System::Net |
System.Net 命名空间提供了对当今网络上使用的许多协议的简单编程接口。 |
System::Net::Sockets |
System.Net.Sockets 命名空间为需要严格控制网络访问的开发人员提供了 Windows Sockets (Winsock) 接口的托管实现。 |
System::Resources |
System.Resources 命名空间提供了允许开发人员创建、存储和管理应用程序中使用的各种特定于文化背景的资源的类和接口。 |
System::Runtime |
System.Runtime 命名空间包含支持 System 、Runtime 命名空间以及 Security 命名空间等各种命名空间的先进类型。 |
System::Runtime::InteropServices |
System.Runtime.InteropServices 命名空间提供了支持平台调用服务的各种成员。 |
System::Security |
System.Security 命名空间提供公共语言运行库安全系统的底层结构,包括权限的基类。 |
System::Security::AccessControl |
System.Security.AccessControl 命名空间提供了用于控制对可安全对象的访问和审计安全相关操作的编程元素。 |
System::Threading |
System.Threading 命名空间提供了启用多线程编程的类和接口。 |
限制
要正确使用该库,需要使用特殊的宏和关键字,并遵守这些限制和说明
- 在项目设置中,设置 **“/Zp1”** 编译器指令。[C/C++ -> 代码生成 -> 结构成员对齐 -> 1 字节 (/Zp1)]
- 静态构造函数不支持(您必须自己初始化静态成员)。
- 不支持属性。
- 析构函数是执行清理操作的机制。析构函数提供适当的保护,例如自动调用基类型析构函数。例如在 C# 代码中,它们表示对象的
Finalize
方法。 - 当两个对象相互保持完全引用(
gcptr
而不是gcmember
)时,必须确保它们在离开 GC 之前删除它们的引用。(您可以实现System.IDisposable
接口,或者简单地在代码中,在应用程序离开对象之前将引用设置为nullptr
。) - 当一个对象实现
System.IDisposable
接口时,建议重写受保护的析构函数(终结器方法)并添加对Dispose
方法的调用。当Dispose
方法在后续类中被重写时,情况也是如此。 - 所有引用类型参数都必须声明为
gcptr
宏或gcmember
作为类成员声明。 - 值类型作为引用或 out 参数必须使用
gcref
和gcout
宏进行声明。 - 属性必须使用适当的宏进行定义。
- 事件必须使用
event
宏进行声明。 - 索引器必须使用适当的宏进行定义,并且仅限于一个参数(仅支持一维索引器)。
- 委托必须使用适当的
delegate
宏进行定义。 - 默认索引器必须在特定术语下调用:
this->default[index]
(不能像this[index]
那样调用,只能由数组调用)。 - 每个接口类都必须通过 public 虚继承派生自
System.Object
类,并且必须有一个protected
默认构造函数。 - 派生自接口必须始终声明为
public virtual
。 - 派生自
System.Object
类必须始终声明为public virtual
。 - 框架中使用的每个类都必须派生自 System.Object(备注:结构不是类,而是值对象)。
- 异常处理必须使用以下宏:
throw
、try
、catch
和end_catch
。 - 使用
throw(exception)
语句抛出异常时,应将其包含在大括号 {} 中,以避免在if-else
语句中使用时出现编译错误。 - 不支持
finally
语句。 - 尽量避免在构造函数中抛出异常。
- 数组类型必须定义为
gcptr(array<type, dim>)
,因为它仅支持一维、二维或三维数组。 - 尽量避免实现包含任何引用类型的结构(值类型)对象,因为无法解决循环引用,因为结构不派生自
System.Object
。
已知错误
在**构造函数**中引发的**未处理异常**可能会破坏垃圾回收器的一致性,但以目前的 C++ 语法和 new
运算符语义,没有简单的方法可以阻止这种不愉快的副作用。
使用代码
几乎不可能简要描述如何使用库代码,因此我在库解决方案中放置了示例项目,如下所示。有一些宏和特殊关键字有助于遵循类似 C# 的语法,并允许定义“托管”类、成员等。有些结构相当复杂,但一切都是可推导的,研究源代码是关键:)
// Example.cpp : Defines the entry point for the console application.
//
#include "Framework.h"
using namespace System;
using namespace System::Collections;
using namespace System::Threading;
MAIN_FUNCTION_IMPL(_tmain)
delegate(System::Guid, ExecutingHandler, (gcptr(System::Object)));
delegate(System::Guid, ExecuteDelegate, ());
extern void DelegatesDemo();
extern void SocketsDemo();
extern void SerialDemo();
extern void EventsDemo();
// --- DemoLunched enum ---
struct DemoLunched : cli::TEnum<DemoLunched, true>
{
public:
enum States
{
Halt = 0,
Running
};
ENUM_DECL(DemoLunched, States, Halt);
};
ENUM_BEGIN_STRINGS(DemoLunched)
{
_T("Halt"),
_T("Running")
}
ENUM_END_STRINGS
ENUM_BEGIN_VALUES(DemoLunched)
{
DemoLunched::Halt,
DemoLunched::Running
}
ENUM_END_VALUES
ENUM_IMPL(DemoLunched)
// --- DelegatesDemoLuncher class ---
class DelegatesDemoLuncher : public virtual System::IDisposable
{
public:
event(ExecutingHandler, DelegatesDemoLuncher, Executing);
PROPERTYGETSET_DECL(DemoLunched, State);
private:
gcmember(System::Object) _self;
public:
DelegatesDemoLuncher();
override ~DelegatesDemoLuncher();
virtual System::Guid Execute(gcptr(System::Object) obj);
override void Dispose();
System::Guid OnExecuting();
};
PROPERTYGETSET_IMPL(DemoLunched, State, DelegatesDemoLuncher)
DelegatesDemoLuncher::DelegatesDemoLuncher()
: _self(this),
Executing(this),
PROPERTYGETSET_INIT(DemoLunched, State, DelegatesDemoLuncher)
{
this->State = DemoLunched::Halt;
this->Executing += gcnew ExecutingHandler(this, &DelegatesDemoLuncher::Execute);
_self = this;
}
DelegatesDemoLuncher::~DelegatesDemoLuncher()
{
}
System::Guid DelegatesDemoLuncher::Execute(gcptr(System::Object) obj)
{
this->State = DemoLunched::Parse(_T("Running"));
Console::WriteLine(obj);
Console::WriteLine();
DelegatesDemo();
return Guid::NewGuid();
}
void DelegatesDemoLuncher::Dispose()
{
this->State = DemoLunched::Halt;
this->Executing -= gcnew ExecutingHandler(this, &DelegatesDemoLuncher::Execute);
Console::WriteLine(_T("DelegatesDemoLuncher disposed."));
}
System::Guid DelegatesDemoLuncher::OnExecuting()
{
return this->Executing(this);
}
// --- Helper functions ---
static void WriteChar(Char c)
{
Console::Write(c);
}
void Execution()
{
Console::WriteLine(_T("Directory list of \"C:\\Program Files\":\n"));
gcptr(array<gcptr(System::String)>) dirs =
System::IO::Directory::GetDirectories(_T("C:\\Program Files"));
for (int i=0; i<dirs->Length; i++)
Console::WriteLine(dirs[i]);
Console::Write(_T("Reading Test.xml..."));
gcptr(System::IO::FileStream) fs =
gcnew System::IO::FileStream(_T("Test.xml"), System::IO::FileMode::Open,
System::IO::FileAccess::Read, System::IO::FileShare::Read, 4000, true);
gcptr(array<Byte>) ba = gcnew array<Byte>(1000);
gcptr(System::IAsyncResult) ares = fs->BeginRead(ba, 0, 700, nullptr, nullptr);
ares->AsyncWaitHandle->WaitOne();
fs->EndRead(ares);
int read = fs->Read(ba, 700, 100);
ba[700+read] = fs->ReadByte();
Byte ba700 = ba[700+read];
read = fs->Read(ba, 801, 100);
ba[801+read] = 0;
fs->Close();
Console::WriteLine(_T("Done\n"));
gcptr(System::String) xmlData = gcnew System::String((const char *)&(ba[0]));
cli::XML::StringXMLReader reader(cli::StringBufferAccessor::GetString(xmlData));
cli::XML::XMLStreamArchive ar(&reader, cli::XML::XMLStreamArchive::eRead);
ar.SkipHeader();
ar.Read();
cli::XML::XMLString str = ar.GetName();
gcptr(IDisposable) disposableObj = gcnew DelegatesDemoLuncher();
gcptr(DelegatesDemoLuncher) ddl = safe_cast<DelegatesDemoLuncher>(disposableObj);
gcptr(array<gcptr(DelegatesDemoLuncher)>) ddla =
gcnew array<gcptr(DelegatesDemoLuncher)>(1);
ddla[0] = ddl;
gcptr(ExecuteDelegate) mcd;
mcd += gcnew ExecuteDelegate(ddla[0], &DelegatesDemoLuncher::OnExecuting);
System::Guid g = mcd();
gcptr(array<System::Byte>) byteArray = g.ToByteArray();
Array::Sort(byteArray);
Array::Reverse(byteArray);
gcptr(System::String) s = System::Guid(byteArray).ToString();
gcptr(array<gcptr(System::String)>) ss = s->Split(_T('-'));
s = String::Join(_T("-"), ss);
s = String::Concat(ddl->ToString(), _T(" - {"), s, _T("}"));
try
{
gcptr(System::Object) co = s[0];
// The lock of object will be released when the exception has been raised
// within the locked part of code
lock(ddl)
{
// Index out of range exception will be trown.
Char c = s[1000];
}
end_lock
}
catch(IndexOutOfRangeException, ex)
{
Console::WriteLine(_T("\nException: {0}"), ex->Message);
Console::Write(_T(" -> "));
Console::WriteLine(ex->StackTrace);
Console::WriteLine();
}
end_catch
}
// --- Main function ---
void Main(gcptr(array<gcptr(System::String)>) args)
{
Console::WriteLine(_T("Total memory at beginning is: {0}.\n"),
GC::GetTotalMemory(false));
Console::WriteLine(String::Empty);
gcptr(System::Threading::Thread) t =
gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(Execution));
t->Start();
t->Join();
Console::WriteLine(_T("\nTotal memory before collection is: {0}."),
GC::GetTotalMemory(false));
Console::WriteLine(_T("Total memory after collection is: {0}."),
GC::GetTotalMemory(true));
}
历史
- 1.0 - 2011 年 11 月 23 日:初始发布。