2008年7月22日星期二

共享算術運算子

每當實作一些有關數學的類別 (如 Vector, Matrix, Point, Size 等...) 時,加減乘除等運算子都會時常出現。讓我們試試把共同的地方提煉成基類別:

template<int N>
class Tuple {
public:
Tuple operator+(const Tuple& rhs) const
{
Tuple result;
for(int i=0; i<N; ++i)
result.data[i] = data[i] + rhs.data[i];
return result;
}

float data[N];
};

class Vec3 : public Tuple<3> {
public:
Vec3(float x, float y, float z) {
data[0] = x; data[1] = y; data[2] = z;
}
};

int main() {
Vec3 v1(1, 2, 3);
Vec3 v2(4, 5, 6);
// Compilation error: v1 + v2 is returning Tuple but not Vec3
Vec3 v3 = v1 + v2;
return 0;
}

大家不用擔心那個回圈會為性能帶來負面影響,編譯器懂得把它優化 (我已在VC2008上證實了這一點)。
但由於運算子的返回型態出了問題,我們作出以下嘗試:

template<int N, class R>
class Tuple {
public:
R operator+(const Tuple& rhs) const
{
R result;
for(int i=0; i<N; ++i)
result.data[i] = data[i] + rhs.data[i];
return result;
}

float data[N];
};

class Vec3 : public Tuple<3, Vec3> {
public:
Vec3(float x, float y, float z) {
data[0] = x; data[1] = y; data[2] = z;
}
};

int main() {
Vec3 v1(1, 2, 3);
Vec3 v2(4, 5, 6);
Vec3 v3 = v1 + v2;
return 0;
}

非常好。不過還可以更好哩:

template<int N, class R, class U>
class Tuple : public U {
public:
R operator+(const Tuple& rhs) const
{
R result;
for(int i=0; i<N; ++i)
result.data[i] = data[i] + rhs.data[i];
return result;
}
};

struct _Vec3Union {
union {
struct { float x, y, z; };
float data[3];
};
};

class Vec3 : public Tuple<3, Vec3, _Vec3Union> {
public:
Vec3() {}
Vec3(float x_, float y_, float z_) {
x = x_; y = y_; z = z_;
}
};

struct _SizeUnion {
union {
struct { float width, height; };
float data[2];
};
};

class Size : public Tuple<2, Size, _SizeUnion> {
public:
Size() {}
Size(float w, float h) {
width = w; height = h;
}
};

int main() {
Vec3 v1(1, 2, 3);
Vec3 v2(4, 5, 6);
Vec3 v3 = v1 + v2;

Size s1(1, 2);
Size s2(2, 3);
Size s3 = s1 + s2;

return 0;
}

這可讓 Vec3 組成的 x, y 和 z 用方便的形式去存取。

儘管以上的提示未必有多大用途,還望它能加強大家對 C++ 的了解。

2008年7月14日星期一

尼采的神奇 Functor

尼采給我的題目...不如在此發表答案.


#include <iostream>

typedef int (*Functor)();

// Recursive type template that generate a
// compile-time list of Functor using inheritance
template<size_t N> struct Unit : public Unit<N-1> {
Unit() : mFunctor(&Unit<N>::fun) {}
static int fun() { return N; }
Functor mFunctor;
};

// Partial specialization to end the recursion
template<> struct Unit<0> {
Unit() : mFunctor(&Unit<0>::fun) {}
static int fun() { return 0; }
Functor mFunctor;
};

// Partial specialization to reduce recursive template complexity
template<> struct Unit<256> : public Unit<255> {
Unit() : mFunctor(&Unit::fun) {}
static int fun() { return 256; }
Functor mFunctor;
};

// Partial specialization to reduce recursive template complexity
template<> struct Unit<512> : public Unit<511> {
Unit() : mFunctor(&Unit::fun) {}
static int fun() { return 512; }
Functor mFunctor;
};

// A compile-time maximum count of Functor
static const size_t cMaxN = 768;

typedef Unit<cMaxN> List;
static const List cList;

// A function that return a Functor that return i.
// In other words, it selects from a list of compile-time functors
// base on the run-time parameter i.
Functor getFunctor(int i) {
{ // We have the assumption that Unit<> struct are packed tightly together in cList
typedef const char* byte_ptr;
byte_ptr functorAddres1 = byte_ptr(&(static_cast<const Unit<1>*>(&cList)->mFunctor));
byte_ptr functorAddres2 = byte_ptr(&(static_cast<const Unit<2>*>(&cList)->mFunctor));
(void)functorAddres1; (void)functorAddres2;
assert(functorAddres2 - functorAddres1 == sizeof(Functor));
}
assert(i <= cMaxN);
return *((Functor*)(&cList) + i);
}

int main() {
for(size_t i=0; i<=cMaxN; ++i) {
Functor f = getFunctor(i);
std::cout << f() << std::endl;
assert(f() == int(i));
}

return 0;
}


Win少, 我的答案正確嗎?

我認為最好的 Pdf 閱讀器



不知不覺 Adobe Acrobat Reader 已經出到第九代了.
在每一次更新的時候, 大家有沒有想過其實可能更有好的選擇呢?
我有一個好推薦, 它就是 Foxit Reader.

Foxit 的描繪品質和速度與 Acrobat 不分上下, 但記憶體用量和啟動時間就遠勝 Acrobat; 更不用安裝 , 細小可攜 (僅一個 5MB 的可執行檔).

以下的測試是基於網上的一篇文章:


Adobe Reader 9 Foxit Reader 2.2
啟動須時 (秒) 21 7
記憶體用量 74MB 25MB

縱然 Foxit 缺少某些(許多人都不常用的)功能, 它帶來的眾多好處立即使我把 100 多MB的 Acrobat Reader 從系統中撤除.

2008年7月2日星期三

程式語言效率大比拼

有天正在尋找腳本語言的時候無意間發現這個名為 The Computer Language Benchmarks Game 的網站.
那裡的 benchmark 數據覆蓋多達 76 種語言加編輯器的組合, 19 個試驗程式(大部分還有提供源始碼).
不過, 儘管那裡提供的資料準確無誤, 它所包含的參考價值仍然有限. 問題在於那些試驗程式都過於細小, 以及只有單一的功能. 在一個複雜的應用程式 (如 3D Game Engine), 有大量不同種類的資料需要處理, 因此記憶體的存取很容易成為瓶頸.
一個好例子就是 Java/.Net. 有數據顯示 Java/.Net 可以快過 C/C++, 不過當你走出試驗程式返回現實, 你會感到 Java/.Net 總是慢半拍的.

其實那網站的第一段就表明了:
Benchmarking programming languages?
How can we benchmark a programming language?
We can't - we benchmark programming language implementations.

How can we benchmark language implementations?
We can't - we measure particular programs!