ACF介绍
ACF的介绍
【摘要】本文提供ACF的介绍说明。ACF是一种C++框架,这是一种由.NET 框架转换为的标准C++框架。假定读者已经掌握了C++和.Net FrameWork的知识。
目录
什么是ACF
为什么有ACF
开始
基本类型系统
异常
数组和集合
委托和事件
Strings和text
I/O
线程
什么是ACF
ACF(Another C++ Framework),是结合.NET Framework 的优势和RAD的特点而设计的标准C++ Framework。ACF以标准C++的语言规则实现.NET Framework 的特点和接口,并同时融入C++特有的优势,例如模板(templates)。
为什么需要ACF
.NET Framework 无疑是微软未来的开发平台,而C#是这个平台上最合适的开发语言。然后,如今仍然有成千上万的C++项目和开发者,并且这些项目不太可能被完全改写为.NET平台下的,于是令开发者头疼的是在RAD、C#及.NET Framework的优点和C++的管理、性能之间做出选择。显然,这些使用C++的团体在转移到.NET平台上之前需要一个连接的纽带。微软的解决方案是托管的C++。MC++尽力将C++转接到.NET平台上,其结果是C++可以被编译成IL(中间语言),并且托管的代码可以很容易地与本地代码交互。然而,MC++或许对于许多项目和开发者来讲并不是一个好的选择。首先,C++几乎是性能和控制的象征,但是JIT和GC的速度很慢,并且许多应用程序的运行时都非常庞大;其次,C++已经相当复杂非常难以使用,MC++使其变得更难。ACF通过将.NET引入C++这个方面考虑,从而试着解决以上问题。通过以C++标准实现.NET Framework,ACF为显存的项目和开发者提供了以下帮助:
1.ACF可以用于已经存在的或者是新开发的C++项目(ACF可以与MFC/ATL/STL等无缝对接),是所有的源码编译成机器码。
2.开发者可以利用现有的C++技术,同时使用API和一些与.NET Framework很接近的思想。这项新的技术在C#、C++和MC++(例如,人们经常使用String;;Format来建立格式化的string)中是可以重用的。
3.ACF也可以用于C++与C#之间的代码移植。
开始
目前,ACF是在Visual C++ 2003下开发的,在我们开始以前,你最好已经暗转了它。(如果你仍然在使用Visual C++ 6.0,丢掉它吧,因为它的标准不是很一致并且它对模板的支持也非常有限)如果之前没有构造ACF,第一步便是构造ACF。构造Corlib,在Visual Studio中打开Corlib.sln(在"{...}\Acf\src\Corlib"目录下, 其中{...}表示包含ACF的目录),并且组建它。就像使用其他的C++库一样,在你的应用程序中使用ACF之前,你需要设置你的开发环境和库目录。Corlib的包含目录是"{...}\Acf\src\Corlib",库目录是"{...}\Acf\lib"。
“Hello world”可以按如下方式编写:
1 #include <AcfCorlib.h> 2 int main() { 3 Acf::Console::WriteLine(L"hello, world"); 4 }
编译这个程序,你需要设置”把wchar_t看做内置类型“并且”启动运行时信息“编译选项,连接到多线程CRT。这对于所有的ACF项目都是必须的。
参照.NET Framework的设计,ACF被组织成许多的命名空间。在.NET Framework中,Acf命名空间响应系统(System)命名空间。AcfCorlib是Corlib的头文件。在我们写出更有用的应城程序之前,让我们先来看看ACF的类型体系。
基本类型体系
C++类型系统与C#/CLI类型系统之间有着显著的差异,这决定了ACF类型体系该如何实现。C++没有同意的类型体系。基本类型充满“魔力”:类型不需要继承自同一个根类型;并且MI被许可的。C++还有非常灵活的内存模型,开发者自己控制何时开辟/释放内存。
C#/CLI有着统一的类型系统。所有的类型直接或间接的继承自System.Object;MI是不被许可的,但是一种类型可以实现多个接口。类型的值既可以是值类型也可以是引用类型。值类型的实例直接包含数据,引用类型的实例的空间是在GC堆上分配的,并且会自动回收。值类型和引用类型之间的转换被称为装箱和拆箱(boxing/unboxing),是在编译和运行时处理的。ACF尝试在C++中模仿C#/CLI的类型系统,并且保持C++的性能,结果就是一个混合类型的类型体系。在ACF中,类型包括引用类型和其他类型引用类型在堆上分配空间,用引用计数进行管理(类似COM)。引用类型要求继承自同一个根——Acf::Object。MI仅在实现接口时使用,并且所有的引用接口都必须继承自Acf::InterfaceBase。其他的类型包括基本类型(例如int,float),枚举类型和其他的C++结构体和类类型。如下的图展示了几杯对象模型(绿色的类为值类型,蓝色的类为引用类型):
由于需要编译器和运行时的支持,在ACF中,没有自动的装箱/拆箱。开发者不得不分开定义值类型和引用类型,并且处理装箱/拆箱的请求。(在C#/CLI中,仅仅只有一种类型,装箱和拆箱自动被处理);例如,Int32是一个值类型的,并且没有基类,它有一个int实例字段,提供解析方法和格式化。Int32Object引用类型有一个实例Int32字段,继承自Acf::Object并实现了IFormattable等接口。(ACF使用X和XObject命名,用来区别值类型和它们相应的引用类型,另外一个例子是Acf::Guid和AcfGuidObject)
例子:
1 int main() { 2 int i = 123; 3 ObjectPtr o = box(i); // boxing 4 int j = unbox<int>(o); // unboxing 5 }
显示了装箱和拆箱如何使用。box和unbox<T>函数是Acf命名空间下的模板函数。默认的box函数的实现是返回一个Acf::Boxed<T>的实例,这个实例对应的类继承自Acf::Object,并且行为同object box。box和unbox<T>函数应该被重写,为了用户自定义的类型,将值类型和引用类型分开定义的类型。(例如Int32和Int32Object)。
在ACF中,所有的引用类型都是通过new操作符在堆上分配的内存。new操作符被重载,当内存分配失败时,Acf::OutOfMemoryException异常将被抛出,返回的内存是被清空了的,所以不会有随机位出现在这一个分配的区域。如上所述,ACF使用引用计数来管理对象的声明周期(所有的接口与所属类对象共享相同的引用计数器)。当需要一个对象或接口时,你应该调用AddRef;结束调用后,你应该调用Release释放空间。一个对象有一个初始的引用计数值为0,当引用计数值再次变为0时,这个对象即被释放了。智能指针类,RefPtr<T>,设计用来实现自动化AddRef/Release。作为一种常见的设计理念,RefPtr<T>设计为强引用,原始的C++指针被视为弱引用。我们需要区分强引用和弱引用,因为引用计数方法处理循环引用时会产生问题。例如,如果对象A拥有一个B对象的强引用,而对象B也有一个A对象的强引用,这两个对象陷入循环引用,两者都不会被释放了。这种问题可以通过使B拥有一个A的弱引用来解决。(或者反过来,这取决于具体情况)
下面的例子展示了如何定义使用引用类型
1 #include <AcfCorlib.h> 2 using namespace Acf; 3 4 class Person : public Object { 5 public: 6 StringPtr Name; 7 int Age; 8 9 Person() { 10 } 11 12 protected: 13 virtual ~Person() { 14 } 15 }; 16 17 int main() { 18 StringPtr name = new String(L"Foo"); 19 20 RefPtr<Person> person = new Person(); 21 person->Name = name; 22 person->Age = 25; 23 24 Console::WriteLine(L"Name: {0}, Age: {1}", 25 person->Name, str(person->Age)); 26 }
Person类是一个引用类型,继承自Acf::Object。它有两个实例字段,Name(string)和Age(int)。StringPtr是一个RefPtr<String>的一个宏定义。析构函数是保护的,所以这个类型不能在栈上分配空间(引用类型始终是在堆上分配空间)。
在Main函数中,第一个声明语句定义并分配了一个string的实例空间。接下来,一个新的Person实例被分配空间,并且字段改变了。然后,Console::WriteLine被用来格式化并输出person的姓名和年龄(格式化string是.NET Framework中定义的)。
异常
ACF使用异常处理作为其基本错误检出和处理机制。在.NET Framework中,异常也是引用类型,并且由GC回收。在ACF中,异常是值类型。这是因为在C++中引发和捕捉异常的首选方案是抛出MyException()和捕捉const MyException&e。 Acf::Exception是所有异常类的基类,一般的异常类包括ArgumentNullException,IOException,FormatException等。
数组和集合
ACF中的数组和集合不同于.NET Framework,因为C++提供很好的模板和模板特例化支持(特别是部分特例化)。
Array<T>是ACF中的数组类(目前尚不支持多维数组)。实际的类声明是Array<T,Tr>,Tr是集合特点类(大部分的类都有默认的实现)。集合的特性控制如何传递参数,如何清理元素,如何比较元素,如何生成哈希码。这项技术被所有的ACF集合类和接口所使用。
下面是两个数组的例子:
1 RefPtr<Array<int> > array1 = Array<int>::Build(3, 1, 2, 5, 4); 2 Array<int>::Sort(array1); 3 4 RefPtr<Array<StringPtr> > array2 = new Array<StringPtr>(2); 5 array2->Item[0] = str(L"hello"); 6 array2->Item[1] = str(L"world");
第一个例子建立了一个5个元素的int数组并且排序了。第二个例子创建了一个2个元素的string数组,并且指定了值。Item[0]语法是一种C++语言的拓展,它代表一个索引属性。在编译时它被翻译为array1->set_Item(0,str(L“hello”))。
ACF中其他的集合类和接口(在Acf::Collection命名空间下的)就像是.NET Framework中的一样,除了它们是基于模板的。这里有一些例子:
1 RefPtr<List<int> > list = new List<int>(); 2 list->Add(10); 3 list->Add(20); 4 5 RefPtr<Dictionary<StringPtr, int> > map = new Dictionary<StringPtr, int>(); 6 map->Item[str(L"s1")] = 1; 7 map->Item[str(L"s2")] = 10;
C#用来遍历数组和集合中的所有元素的foreach,深受开发者青睐。ACF定义了一个FOREACH宏用来模仿foreach的特性。例如,给定一个存储有int的数组,我们可以遍历这个数组按如下方式进行:
1 FOREACH (int, n, array) { 2 Console::WriteLine(n); 3 }
委托和事件
C#和.NET Framework中的委托和事件是如此的受欢迎,ACF的定义不能错失了他们。
ACF中定义的委托与C#/CLI中的并不相同。例如,事件处理(EventHandler)委托在C#中定义如下
delegate void EventHandler(object sender, EventArgs e);
在ACF中则是这样的
typedef Delegate2<void, Object*, EventArgs*> EventHandler;
这里的DelegateN<R,P1,P2,…,Pn>是ACF中的委托类,N是函数的参数个数,R是函数的返回值的类型。要调用一个委托,调用实例的方法,调用函数的参数。
在C#中,事件是很容易定义和使用的。但是在ACF中,由于没有编译器的支持,事件的定义和适用变得有点复杂。为了定义事件,使用ACF_DECLARE_EVENT宏。
下面是一个ACF中如何使用委托和事件的例子:
1 typedef Delegate2<void, Object*, EventArgs*> EventHandler; 2 typedef RefPtr<EventHandler> EventHandlerPtr; 3 4 class Button : public Object { 5 public: 6 ACF_DECLARE_EVENT(EventHandler, Click) 7 8 protected: 9 void OnClick(EventArgs* e) { 10 if (Click != null) 11 Click->Invoke(this, e); 12 } 13 }; 14 15 class Form1 : public Object { 16 private: 17 RefPtr<Button> _button; 18 19 public: 20 Form1() { 21 this->_button = new Button(); 22 23 EventHandlerPtr h1 = new EventHandler(this, Form1::ButtonClickHandler); 24 this->_button->add_Click(h1); 25 26 EventHandlerPtr h2 = new EventHandler(Form1::StaticButtonClickHandler); 27 this->_button->add_Click(h2); 28 } 29 30 private: 31 void ButtonClickHandler(Object* sender, EventArgs* e) { 32 std::cout << "Button clicked!" << std::endl; 33 } 34 35 static void StaticButtonClickHandler(Object* sender, EventArgs* e) { 36 std::cout << "Static: Button clicked!" << std::endl; 37 } 38 };
ACF_DECLARE_EVENT宏自动生成如下的代码:
class Button : public Object { private: RefPtr<EventHandler> Click; public: void add_Click(EventHandler* h) { LOCK (this) this->Click = EventHandler::Combine(this->Click, h); } void remove_Click(EventHandler* h) { LOCK (this) this->Click = EventHandler::Remove(this->Click, h); } };
在Form1的构造函数中,两个委托被创建,一个关联一个静态函数,另一个关联一个实例的函数。对于这个实例的方法,委托还需要一个该对象的引用(这里是Form1)。由于Form1有一个Button的强引用,而Button又有一个Click的强引用,所以,如果Click有一个Form1的强引用,那么循环引用便也形成了。然而,在其他的例子中,委托可能确实需要对象的强引用。所以,委托类的构造函数既重载了T*也重载了RefPtr<T>。如果创建一个委托时传递的是一个T*(特别是使用this指针时),委托将会拥有对象的一个弱引用。否则,委托将会有一个对象的强引用。
String和Text
String和StringBuilder类提供了基本的字符串操作。Acf::Text命名空间,也提供了一些编码和解码的类供使用,类似于.NET Framwork。下面是一个简单的例子:
1 StringPtr s = String::Format(L"Text: {0} {1}", s1, obj); 2 3 EncodingPtr enc = Encoding::get_Default(); 4 RefPtr<Array<byte> > bytes = enc->GetBytes(s);
Str函数将数值和C语言风格的string转换为了String。
1 int a = 10; 2 float b = 15.2; 3 const char* c = "hello"; 4 5 StringPtr s = String::Format(L"{0}{1}{2}", str(a), str(b), str(c));
I/O
Acf::IO命名空间中包含一些读写流和文件的类。(目前Acf不支持异步文件I/O)。
例如,从文件中读取文本,你可以按如下方式编写代码:
1 StringPtr path = new String(L"C:\in.txt"); 2 StreamReaderPtr reader = new StreamReader(path); 3 StringPtr text = reader->ReadToEnd();
ACF支持基本的流包括FileStream和MemoryStream,支持读/写包括BinaryReader/BianryWriter,TextReader/TextWriter,StreamReader /Stream -Writer 和StringReader/StringWriter。
ACF也支持简单的文件系统管理,通过Path,File和Directory类来实现。
线程
ACF为编写多线程应用程序提供基本的支持。如下的例子展示如何创建和开始工作线程:
1 static void WorkerThreadProc() { 2 ... 3 } 4 5 class MyObject : public Object { 6 public: 7 void WorkerThreadProc() { 8 ... 9 } 10 }; 11 12 int main() 13 { 14 ThreadPtr thread1 = new Thread(WorkerThreadProc); 15 thread1->Start(); 16 17 RefPtr<MyObject> obj = new MyObject(); 18 19 ThreadStartPtr start = new ThreadStart(obj, MyObject::WorkerThreadProc); 20 ThreadPtr thread2 = new Thread(start); 21 thread2->Start(); 22 23 ... 24 }
为了实现同步数据访问,使用LOCK宏或者Interlocked,Monitor,AutoResetEvent,ManualResetEvent或者Mutex类。
源:http://www.codeproject.com/Articles/6840/Introduction-to-ACF-Another-C-Framework
浙公网安备 33010602011771号