2009年11月3日 星期二

Ubuntu 9.10 + VirtualBox Guest Additions

Ubuntu 9.10 釋出了!不過根據過去的經驗,剛剛發行的 Ubuntu 在前一二週都會有一些地雷,所以我目前還不敢把我日常使用(寫作業、文章)的筆電升到 Ubuntu 9.10。我大概要到期中考之後才會想要升級吧!

不過在那之前,我會想要先用 VirtualBox 來玩玩看。然而在使用 VirtualBox 跑 Ubuntu 9.10 的時候,我必需忍受 800x600 的小螢幕感覺很痛苦。我忽然想到 Windows XP 當 VirtualBox 的 Guest 的時候可以安裝 VirutalBox Guest Additions 讓 Guest 的解析度可以隨 VirtualBox 的視窗大小做修改,我就在想 Ubuntu 9.10 可不可以?答案當然是可以的!(不然我寫這篇幹麼? XD )不過有一些麻煩的事要做。
  1. 從 VirtualBox 選單選擇「裝置 > 客戶端額外功能」,你會發現有一片光碟被掛載到你的 Ubuntu 9.10。
  2. 從 Ubuntu 的「應用程式 > 附屬應用程式 > 終端機」開啟終端機
  3. sudo aptitude install patch
    (安裝 patch 這個程式,可以讓我們依修正檔修補程式碼)
  4. cd /media/cdrom
  5. ./VBoxLinuxAdditionals-x86.run --target ~/vbox
  6. cd ~/vbox
  7. wget http://w.csie.org/~b97073/W/ubuntu910-vbox.patch
  8. patch -p1 < ubuntu910-vbox.patch
  9. sudo ./install.sh
  10. 重新開機
如果以上都沒有錯誤,重新開機之後就應該可以享受「任意」的螢幕大小了!

參考資料:
Compilation of VirtualBox add-ins for Ubuntu 9.10
Reply of "Ubuntu 9.04 alpha6 and Virtualbox video driver for X"

2009年9月23日 星期三

Design Pattern 與 Double Dispatching

為了寫 Design Pattern 作業,所以上網查了一下。不過我覺得以這個作業而言,Double Dispatch 好像也沒有什麼用,如果不偷用 RTTI(不管是用 instanceof、getClass 或自己加上一個回傳型別的 Virtual Function),好像就沒有辦法確認 object 的真正的 class。

--

在談 Double Dispatch 之前我們必需先從 Polymorphism 說起。Polymorphism 有兩種:
  1. Subtype Polymorphism: 前者就是指繼承類別之間的多型。例如:人和狗同時繼承動物類別,二者都會進食,不過二者的吃飯方式不同。我們會在動物宣告一個 Virtual Method,人和狗類別會各自 Override 自己的吃飯方式,利用 Virtual Method Invocation,在執行期喚起正確的函式。 (Method Overriding)
  2. Type Polymorphism (Ad-hoc Polymorphism): 有一個同名的函式,編譯器會依據傳入參數的不同,選擇不同的函式,例如:同樣是 + operator,你把數字加數字與字串加字串就會有不同的結果。(Function Overloading)
值得一提的是 Virtual Method Invocation 的效率略低於一般的函式呼叫,所以 C++ 要求希望被 Virtual Invoke 的函式應該要額外加上 virtual 關鍵字。而 Function Overloading 因為是在編譯期決定呼叫的函式,所以不會影響效率。

我們先看一下什麼是 Type Polymorphism (Function Overloading):

class A;
class B;

class A {
public:
    void interact(A const &a) { cout << "class A vs A" << endl; }
    void interact(B const &b) { cout << "class A vs B" << endl; }
};

class B : public A {
public:
    void interact(A const &a) { cout << "class B vs A" << endl; }
    void interact(B const &b) { cout << "class B vs B" << endl; }
};

int main() {
    A a;
    B b;

    a.interact(a); // Invoke A::interact(A const&)
    a.interact(b); // Invoke A::interact(B const&)
    b.interact(a); // Invoke B::interact(A const&)
    b.interact(b); // Invoke B::interact(B const&)
    return 0;
}

我們可以注意到,透過 Function Overloading,我們可以在不同的時候喚起不同的函式。例如:a.interact(b) 就會喚起 void A::interact(B const &) 這個函式。

如果因為某些原因我必需使用類別 A 的 Reference 來指稱其子類別的 Instance,我把上面的程式改成這樣呢?

int main() {
    A &aa = a;
    A &ba = b;

    a.interact(aa); // Invoke A::interact(A const&)
    a.interact(ba); // Invoke A::interact(A const&)
}

我們就會發現 Type Polymorphism 完全幫不了我們!為什麼?因為類別 A 的 Reference 所指向的物件的 Type 是可以隨 Runtime 的不同而有所變動,所以一般來說編譯器只能假定這個 Reference 只會是類別 A 的實例。從而只能喚起 A::interact(A const&) 函式。那我們該怎麼做呢?

此時我們會想聯想的 Subtype Polymorphism (Method Overriding) 的 Late Binding。首先我們要為上述的函式加上 virtual 關鍵字,使其可以動態地決定要喚起哪個函式。我分別 class A, class B 的 interact 函式的前面都加上了 virtual 關鍵字(雖然一日 Virtual Method 終身 Virtual Method,所以加在 class A 裡面的 Method 就可以了,不過為了方便閱讀,我是建議在 class B 裡面的 Method 也加上 virtual)

class A {
public:
    virtual void interact(A const &a) { cout << "class A vs A" << endl; }
    virtual void interact(B const &b) { cout << "class A vs B" << endl; }
};

class B : public A {
public:
    virtual void interact(A const &a) { cout << "class B vs A" << endl; }
    virtual void interact(B const &b) { cout << "class B vs B" << endl; }
};

這下問題解決了嗎?我們看看:

int main() {
    aa.interact(aa); // Invoke A::interact(A const&)
    aa.interact(ba); // Invoke A::interact(A const&)
}

很顯然地,雖然 interact 是 Virtual Function,但是因為只有 this 會透過 Late Binding 而被延遲到執行期,編譯器還是會在編譯期施行 Method Overloading Resolution 從眾多 Overloaded Method 當中挑選特定版本生成程式碼,讓我們的程式於執行期呼叫 Virtual Method(進而依據 this 所屬類別進行 Dynamic Dispatch),所以這裡 Subtype Polymorphism 似乎幫不上忙。

不過,事實上 Subtype Polymorphism 是可以幫上忙的!只是我們需要一個小技巧!這就是所謂的 Double Dispatching,我們可以再寫一個函式叫作 apply,它固定傳入一個類別 A 的 Reference,不過我們會在函式的本體上動手腳!

class A {
public:
    virtual void apply(A &obj) { obj.interact(*this); }
};

class B : public A {
public:
    virtual void apply(A &obj) { obj.interact(*this); }
};

int main() {
    aa.apply(aa); // Finally invoke A::interact(A const&)
    ba.apply(aa); // Finally invoke A::interact(B const&)

    aa.apply(ba); // Finally invoke B::interact(A const&)
    ba.apply(ba); // Finally invoke B::interact(B const&)
}

對,這裡我用了 *this,*this 的型別是什麼呢?對!就是擁有 apply 函式的類別。注意:我們必需要在每個類別加上相同的 apply 函式。因為每個 apply 函式中的 *this 型別是不一樣的。當我們用 object1.apply(object2) 呼叫時,apply 會幫我們找出 object1 的確切類別,再使用 object2.interact((THISCLASS &)object1) 來呼叫 interact 函式,因為 object1 的型別已經確定,所以第二次呼叫 interact 就是找出 object2 的確切類別,喚起正確的函式。

這大概是 Double Dispatch 的內容。

--

不過壞消息是:這個作業中我們不能碰原有的 class 也就不能為原有的 class 加上 virtual method,所以這樣寫似乎沒有什麼用。

2009年9月16日 星期三

C++ 與 instanceof

C++ 本身是沒有提供 instanceof 關鍵字,不過我們可以利用簡單地實作出該語意:

template <class Class, typename T>
inline bool
instanceof(T const &object)
{
    return dynamic_cast<class const *>(&object);
}

不過我發現如果要用 dynamic_cast,我們的 class 至少要有一個 virtual function,因為 C++ 的 RTTI 實作是依賴 vtable 的。

以下是整個範例程式:

#include <iostream>
#include <cstdlib>

using namespace std;



template <class Class, typename T>
inline bool
instanceof(T const &object)
{
    return dynamic_cast<Class const *>(&object);
}



class A
{
public:
    virtual ~A() {}
};

class B : public A { };
class C : public A { };
class D : public B, public C { };

class E
{
public:
    virtual ~E() {}
};



int
main()
{
    A a;
    B b;
    C c;
    D d;
    E e;

    A& aa = a;
    A& ab = b;
    A& ac = c;
    A& ad = *static_cast<B *>(&d);

#define CHECK(CLASS, REF) \
    do \
    { \
        if (instanceof<CLASS>(REF)) \
        { \
            cout << #REF " is an instance of " #CLASS << endl; \
        } \
        else \
        { \
            cout << #REF " is NOT an instance of " #CLASS << endl; \
        } \
    } \
    while (0);

    CHECK(A, aa);
    CHECK(A, ab);
    CHECK(A, ac);
    CHECK(A, ad);
    CHECK(A, e);

    CHECK(B, aa);
    CHECK(B, ab);
    CHECK(B, ac);
    CHECK(B, ad);
    CHECK(B, e);

    CHECK(C, aa);
    CHECK(C, ab);
    CHECK(C, ac);
    CHECK(C, ad);
    CHECK(C, e);

    CHECK(D, aa);
    CHECK(D, ab);
    CHECK(D, ac);
    CHECK(D, ad);
    CHECK(D, e);


    return EXIT_SUCCESS;
}