Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

What's New in ACF 0.3

0.00/5 (No votes)
23 Jun 2004 1  
This article introduces what's new in ACF (Another C++ Framework) version 0.3.

Introduction

ACF (Another C++ Framework) is a C++ framework designed to bring the power and RAD of the .NET framework to standard C++. The overall vision of version 0.3 is to deliver a more robust and ready to use Corlib.

1. Type system

Basic RTTI support

ACF 0.3 supports basic RTTI through Type, Object::GetType(), and macro TYPEOF(T). The implementation is based on standard C++ RTTI, namely type_info and typeid. Currently, there is no plan to support advanced reflection functionalities in the .NET framework (e.g., calling a method at runtime).

The following sample code shows how to use GetType and TYPEOF.

ObjectPtr obj = str(L"hello");
bool equals = (obj->GetType() == TYPEOF(String));

Following is the equivalent code in C#:

object obj = "hello";
bool equals = (obj.GetType() == typeof(string));

Value types boxing/unboxing unification

In ACF 0.3, all value types are boxed to type Boxed<T> (e.g.: Boxed<Int32>, Boxed<DateTime>) through template specialization for consistency purpose. For example, the following code checks whether an object is int, and if so, reads its value:

ObjectPtr obj = box(10);
...
Boxed<Int32>* intObj = dynamic_cast<Boxed<Int32>*>(obj.Pointer);
if (intObj != null) {
    int n = intObj->Value;
}

Of course, the example can also be written as follows, if try/catch is preferred and you don't need to change the value of the object:

ObjectPtr obj = box(10);
...
try {
    int n = unbox<int>(obj); // actually calls unbox<Int32>(obj)

}
catch (...) {
}

2. Base types parsing and formatting

Base types parsing and formatting are updated in ACF 0.3 (however, currently there is no support for cultures). For example, the following code shows how to format integers using various formats:

Int32 n = 123456789;
Console::WriteLine(n.ToString(L"C")); // $123,456,789.00

Console::WriteLine(n.ToString(L"E")); // 1.234568E+008

Console::WriteLine(n.ToString(L"P")); // 12,345,678,900.00%

Console::WriteLine(n.ToString(L"N")); // 123,456,789.00

Console::WriteLine(n.ToString(L"F")); // 123456789.00

The following function formats file sizes as shown in Windows Explorer (e.g., "3,012 KB"):

StringPtr FormatFileSize(int64 size) {
    Int64 kb = size / 1024;
    if (size % 1024 != 0)
        kb++;
    return kb.ToString(L"N0") + str(L" KB");
}

3. Arrays and collections

Multi-dimensional arrays

ACF 0.3 supports multi-dimensional arrays. The array class definition is as follows:

template <typename T, int R> // R: array rank, default to 1

class Array : ... {
    Array(int (&lengths)[R]);
    T GetValue(int (&indices)[R]);
    void SetValue(InArgType value, int (&indices)[R]);
    ...
};

// specialization for 1D arrays

template <typename T>
class Array<T, 1> : ... {
    Array(int length);
    T GetValue(int index);
    void SetValue(InArgType value, int index);

    static int BinarySearch(Array<T, 1>* array, InArgType value, 
        IComparer<T>* comparer = null);
    static void Sort(Array<T, 1>* array, 
               IComparer<T>* comparer = null);
    ...
};

// specialization for 2D arrays

template <typename T>
class Array<T, 2> : ... {
    Array(int length1, int length2);
    T GetValue(int index1, int index2);
    void SetValue(InArgType value, int index1, int index2);
    ...
};

...

As you can see, the implementation uses template partial specialization for various array ranks (especially for single dimensional arrays). This is because certain methods, such as BinarySearch, work only on single dimensional arrays. In C#, if you call BinarySearch on a two dimensional array, it compiles OK but will throw a RankException at runtime. In ACF, this will be a compile-time error.

The following sample code shows how to use three dimensional arrays:

RefPtr<Array<int, 3> > array3D = new Array<int, 3>(10, 20, 30);
array3D->SetValue(100, 0, 0, 0);

Following is the equivalent code in C#:

int[,,,] array3D = new int[10, 20, 30];
array3D[0, 0, 0] = 100; // or array3D.SetValue(100, 0, 0, 0);

Comparing and sorting (esp. case insensitive)

ACF 0.3 supports comparing interfaces and classes like IComparer<T>, Comparer<T>, and StringComparer (which are introduced in .NET 2.0). For example, the following sample code sorts a string array using case insensitive invariant culture:

RefPtr<Array<StringPtr> > array = 
  Array<StringPtr>::Build(str(L"hello"), str(L"world"));
Array<StringPtr>::Sort(array, 
  StringComparer::get_InvariantCultureIgnoreCase());

Following is the equivalent code in C#:

string[] array = new string[] { "hello", "world" };
Array.Sort(array, StringComparer.InvariantCultureIgnoreCase);

The following sample code shows how to use case insensitive hash table:

RefPtr<Dictionary<StringPtr, int> > dict = 
  new Dictionary<StringPtr, int>(StringComparer::get_InvariantCultureIgnoreCase());
dict->Item[str(L"Test")] = 10;
int n = dict->Item[str(L"test")];

Following is the equivalent code in C#:

Dictionary<string, int> dict = 
    new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
dict["Test"] = 10;
int n = dict["test"];

4. Registry support

ACF 0.3 provides support for accessing Windows registry (under namespace Acf::Microsoft::Win32). The RegViewer sample application shows how to use the registry classes.

For example, the following code shows how to respond to TVN_ITEMEXPANDING notification and expand the tree view node (exception handling code omitted):

void CRegViewerDlg::OnTvnItemexpandingTree1(NMHDR *pNMHDR, LRESULT *pResult) {
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    *pResult = 0;

    if (pNMTreeView->action == TVE_EXPAND && 
        (pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE) == 0) {
        RegistryKey* parentKey = 
          reinterpret_cast<RegistryKey*>(pNMTreeView->itemNew.lParam);

        // Get the subkey names and sort them

        RefPtr<Array<StringPtr> > subKeyNames = parentKey->GetSubKeyNames();
        Array<StringPtr>::Sort(subKeyNames, 
            StringComparer::get_InvariantCultureIgnoreCase());

        // Add the subkeys to the tree view

        FOREACH (StringPtr, keyName, subKeyNames) {
            RegistryKeyPtr subKey = parentKey->OpenSubKey(keyName);
            InsertTreeNode(subKey, keyName, 
                             pNMTreeView->itemNew.hItem, TVI_LAST);
        }
    }
}

The following code adds keys to the tree view:

HTREEITEM CRegViewerDlg::InsertTreeNode(RegistryKey* key, String* name, 
                               HTREEITEM hParent, HTREEITEM hInsertAfter) {
    key->AddRef(); // we'll release it in TVN_DELETEITEM


    CW2T nameT(name->GetCString());

    TVINSERTSTRUCT tvi;
    memset(&tvi, 0, sizeof(tvi));
    tvi.hParent = hParent;
    tvi.hInsertAfter = hInsertAfter;
    tvi.item.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM;
    tvi.item.pszText = nameT;
    tvi.item.lParam = reinterpret_cast<LPARAM>(key);
    tvi.item.cChildren = key->SubKeyCount;

    return this->_treeCtrl.InsertItem(&tvi);
}

As you can see, we should call AddRef when adding the key to the tree view to comply with our reference counting rules. We should release it in the TVN_DELETEITEM notification:

void CRegViewerDlg::OnTvnDeleteitemTree1(NMHDR* pNMHDR, LRESULT* pResult) {
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    *pResult = 0;

    RegistryKey* key = 
      reinterpret_cast<RegistryKey*>(pNMTreeView->itemOld.lParam);
    key->Release();
}

5. Other minor updates and clean-up

For example, Directory::GetLogicalDrives.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here