|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
The latest version can be found here (ACF homepage). AbstractThis article provides an introduction to ACF, a C++ framework which brings the .NET framework to standard C++. Knowledge of C++ and the .NET Framework is assumed. Contents
What is ACFACF (Another C++ Framework), is a C++ framework designed to bring the power and RAD of the .NET framework to standard C++. ACF does this by implementing the features and APIs of the .NET framework in standard C++, and at the same time takes advantage of C++ unique features such as templates. Why ACFThe .NET framework is definitely the future development platform of Microsoft, and C# is the best programming language for that platform. However, today there are thousands of existing C++ projects and developers. These projects are not likely to be totally rewritten for .NET, and the developers are facing the headache choice between the RAD and benefits of C# and the .NET framework, and the performance and control of C++. Apparently, the C++ community need a bridge now before moving to .NET in the future. The solution from Microsoft is Managed C++. MC++ tries to bring C++ to .NET, the result is that C++ code can be compiled to IL, and managed code can easily interop with native code. However, MC++ may not be a good choice for many projects and developers. First, C++ is almost the symbol of performance and control, but JIT and GC are very slow, and the runtime is also too huge for many applications. Second, C++ is already very complex and hard to learn and use, MC++ adds to it. ACF tries to solve this problem from another side - bring .NET to C++. By implementing the .NET framework in standard C++, ACF helps existing C++ projects and developers:
Getting startedCurrently, ACF is developed under Visual C++ 2003, you'd better get it before we move on (if you are still using Visual C++ 6.0, throw it away since it is not very standards conformant and its template support is very limited). The first step will be to build ACF if not done before. For example, to build the Corlib, open Corlib.sln (under "{...}\Acf\src\Corlib", where {...} is the directory which contains ACF) with Visual Studio and build it. Like using other C++ libraries, you need to setup the include and library directories in your development environment before using ACF in your applications. The include directory for Corlib is "{...}\Acf\src\Corlib", library directory is "{...}\Acf\lib". The "hello world" program can be written as follows: #include <AcfCorlib.h>
int main() {
Acf::Console::WriteLine(L"hello, world");
}
To compile this program, you need to turn on "treat wchar_t as built-in type" and "enable runtime type info" compiler options, and link to the multithreaded CRT. This is required for all ACF projects. ACF is organized into a number of namespaces, following the design of the .NET
framework. The Basic type systemThere are some significant differences between the C++ type system and the C#/CLI type system, this affects how ACF type system is implemented. C++ does not have a unified type system. Primitive types are "magic"; types are not required to inherit from a common root; and MI is allowed. C++ also has a very flexible memory model, the developers control how and when to allocate/free memory. C#/CLI has an unified type system. All types directly or indirectly inherit from
ACF tries to emulate the C#/CLI type system on C++ and at the same time retain
performance. The result is a hybrid type system. In ACF, types include reference
types and other types. Reference types are allocated on the heap, and managed using
reference count (like COM). Reference types are required to inherit from a single
root -
In ACF, there is no automatically boxing/unboxing since it requires compiler
and runtime support. Developers have to define the value class and reference class
separately and handle the boxing/unboxing requests (in C#/CLI, there is only one
type, and boxing/unboxing are automatically handled). For example, The example: int main() {
int i = 123;
ObjectPtr o = box(i); // boxing
int j = unbox<int>(o); // unboxing
}
shows how boxing and unboxing are used. The In ACF, all reference types are allocated on the heap via operator The following example shows how to define and use reference types: #include <AcfCorlib.h>
using namespace Acf;
class Person : public Object {
public:
StringPtr Name;
int Age;
Person() {
}
protected:
virtual ~Person() {
}
};
int main() {
StringPtr name = new String(L"Foo");
RefPtr<Person> person = new Person();
person->Name = name;
person->Age = 25;
Console::WriteLine(L"Name: {0}, Age: {1}",
person->Name, str(person->Age));
}
The class In the main function, the first statement defines and allocates a new string
instance. Next, a new ExceptionsACF uses exceptional handling as its basic error raising and handling mechanism.
In the .NET framework, exceptions are also reference types and collected by GC.
In ACF, exceptions are value types. This is because in C++, the preferred way to
throw and catch exceptions is Arrays and collectionsArrays and collections in ACF are different from the .NET framework ones, because C++ provides good template and template specialization support (especially partial specialization).
Here are two array examples: RefPtr<Array<int> > array1 = Array<int>::Build(3, 1, 2, 5, 4);
Array<int>::Sort(array1);
RefPtr<Array<StringPtr> > array2 = new Array<StringPtr>(2);
array2->Item[0] = str(L"hello");
array2->Item[1] = str(L"world");
The first example builds an The other collection classes and interfaces in ACF (under namespace RefPtr<List<int> > list = new List<int>();
list->Add(10);
list->Add(20);
RefPtr<Dictionary<StringPtr, int> > map = new Dictionary<StringPtr, int>();
map->Item[str(L"s1")] = 1;
map->Item[str(L"s2")] = 10;
C# has a FOREACH (int, n, array) {
Console::WriteLine(n);
}
Delegates and eventsDelegates and events are so attractive features of C# and the .NET framework that ACF definitely cannot miss them. The delegate definition in ACF is quite different from C#/CLI. For example, the
delegate void EventHandler(object sender, EventArgs e);
In ACF it is like: typedef Delegate2<void, Object*, EventArgs*> EventHandler;
Here In C#, it is very easy to define and consume events, but in ACF, it's a little
complex since there is no compiler support. To define an event, use the Here is an example on how to use delegates and events in ACF: typedef Delegate2<void, Object*, EventArgs*> EventHandler;
typedef RefPtr<EventHandler> EventHandlerPtr;
class Button : public Object {
public:
ACF_DECLARE_EVENT(EventHandler, Click)
protected:
void OnClick(EventArgs* e) {
if (Click != null)
Click->Invoke(this, e);
}
};
class Form1 : public Object {
private:
RefPtr<Button> _button;
public:
Form1() {
this->_button = new Button();
EventHandlerPtr h1 = new EventHandler(this, Form1::ButtonClickHandler);
this->_button->add_Click(h1);
EventHandlerPtr h2 = new EventHandler(Form1::StaticButtonClickHandler);
this->_button->add_Click(h2);
}
private:
void ButtonClickHandler(Object* sender, EventArgs* e) {
std::cout << "Button clicked!" << std::endl;
}
static void StaticButtonClickHandler(Object* sender, EventArgs* e) {
std::cout << "Static: Button clicked!" << std::endl;
}
};
The 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);
}
};
In Strings and textThe StringPtr s = String::Format(L"Text: {0} {1}", s1, obj);
EncodingPtr enc = Encoding::get_Default();
RefPtr<Array<byte> > bytes = enc->GetBytes(s);
The int a = 10;
float b = 15.2;
const char* c = "hello";
StringPtr s = String::Format(L"{0}{1}{2}", str(a), str(b), str(c));
I/OThe For example, to read text from a file, you can write code as follows: StringPtr path = new String(L"C:\in.txt");
StreamReaderPtr reader = new StreamReader(path);
StringPtr text = reader->ReadToEnd();
ACF supports basic streams including ACF also supports simple file system management via ThreadingACF provides basic support for writing multithreaded applications. The following code shows how to create and start worker threads: static void WorkerThreadProc() {
...
}
class MyObject : public Object {
public:
void WorkerThreadProc() {
...
}
};
int main()
{
ThreadPtr thread1 = new Thread(WorkerThreadProc);
thread1->Start();
RefPtr<MyObject> obj = new MyObject();
ThreadStartPtr start = new ThreadStart(obj, MyObject::WorkerThreadProc);
ThreadPtr thread2 = new Thread(start);
thread2->Start();
...
}
To synchronize data access, use the History
ContactYour feedback and suggestions are key factors to make this framework better, please feel free to send them to Yingle Jia (yljia@msn.com). You can also visit his weblog at http://blogs.wwwcoder.com/yljia/. | ||||||||||||||||||||