0%

從最簡單的範例開始

我們現在有個demo.c,裡面的程式碼如下。

demo.c lang: c
1
2
3
4
5
6
7
#include <stdio.h>
int add2(int a, int b) { return a + b; }
int main()
{
printf("%d\n", add2(1, 1));
return 0;
}

所需要的CMakeLists.txt內容也很簡單。

1
2
3
PROJECT(CMakeDemo C)
cmake_minimum_required(VERSION 2.8)
ADD_EXECUTABLE(demo demo.c)

這樣還不如直接用gcc編譯來的快很多,不過事情總會越來越複雜。

向工業標準邁進一步

我們把執行檔放在bin,而把程式碼放到src下。
根目錄的CMakeLists.txt改成這樣。

1
2
3
4
PROJECT(CMakeDemo C)
cmake_minimum_required(VERSION 2.8)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
ADD_SUBDIRECTORY(src)

因為新增了最後一杭,所以在src底也要新增一個CMakeLists.txt。

1
ADD_EXECUTABLE(demo demo.c

這樣我們就能在bin底下看到demo了。

最近看了Mixin之後,抒發一下自己的感想。
學過Software Engineering的,都知道DRY。如何共用程式碼就變成一門學問了。

假設我們現在有IA, IB, IC三個interface,然後有三個Concrete Class CA, CB, CC實作這三個介面
然後D, E兩個Class需要同時支援這三個interface,該怎麼做。

在C++這種支援Multiple Inheritance特性的語言,大概會是這個樣子。

lang: cpp
1
2
class D : public CA, CB, CC {};
class E : public CA, CB, CC {};

如果CA, CB, CC的內容風馬牛不相及,這個解決方案不錯,不然的話,Diamond Problem是個很頭大的問題。

而Java等語言等只支持單一繼承,因此避掉了Diamond Problem,不過卻引來其他問題。
有兩種常見的問題,第一種是實作介面。

1
2
class D implements IA, IB, IC {};
class E implements IA, IB, IC {};

假設某個Class的實作方式需要修改,需要修改,那麼D跟E的內容都需要修改。如此一來就達不到DRY的精神了。
另一種是亂七八糟的繼承方式。

1
2
3
4
5
6
7
class PD1 extends CA {};												class PE1 extends CA {};
class PD2 extends CB {}; class PE2 extends CB {};
class PD3 extends CC {}; class PE3 extends CC {};
class PD4 extends PD1 {}; class PE4 extends PE1 {};
class PD5 extends PD2 {}; class PE5 extends PE2 {};
class PD6 extends PD3 {}; class PE6 extends PE3 {};
class D extends PD6 {}; class E extends Pe6 {}

雖然避掉的第一個問題,不過那眼花撩亂的繼承關係更麻煩了。

至於動態語言大行其道之後,Mixin提供另外一種思考模式。由於Duck Typing的支持,類與類之間沒有強烈的interface contract關係。以Ruby來說

lang: ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module IA 
end
module IB
end
module IC
end
class D
include IA
include IB
include IC
end
class E
include IA
include IB
include IC
end

mixin是一群method的集合,只要外在的class有滿足條件(Duck Typing),這個方案可以滿足大部分的需求。

StackOverFlow看到的一個範例。

lang: cpp
1
2
3
4
5
6
7
8
9
struct Functor {
template <typename T>
void function() {}
template <>
void function<int>() {}
};
Functor functor;
functor.function<char>();
functor.function<int>();

同樣的code,在gcc和clang編譯失敗,不過VC可以,同樣根據StackOvewflow的說法,這是VC的一個非標準Extension。

要模擬這個方案,可以靠function overloading來做。

lang: cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
struct DummyIdentity {
typedef T type;
};
struct Functor {
template <typename T>
void function() {
function(DummyIdentity<T>());
}
private:
template <typename T>
void function(DummyIdentity<T>) {}
void function(DummyIdentity<int>) {}
};

找了一下在Linux底下寫OpenGL程式的方法,覺得有必要繞過glut,直接跟XWindow打交道,才能拿到更大的控制權。
終於找到一個可以用的骨架。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <X11/Xlib.h>
#include <iostream>

int main()
{
Display* dpy;

dpy = XOpenDisplay(NULL);

std::cout << std::endl;

if (dpy == NULL)
{
std::cout << "Error: could not open display";
return 0;
}
else
{
std::cout << "Success!";
}

int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));

Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, whiteColor, blackColor);

XSelectInput(dpy, w, StructureNotifyMask);

XMapWindow(dpy, w);

GC gc = XCreateGC(dpy, w, 0, NULL);

XSetForeground(dpy, gc, whiteColor);

while(true)
{
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify)
{
break;
}
}

XDrawLine(dpy, w, gc, 10, 60, 10, 0);
XFlush(dpy);

while(true)
{
XEvent e;
XNextEvent(dpy, &e);
if (e.type == DestroyNotify)
{
break;
}
}

std::cout << std::endl << std::endl;
return 0;
}

需要連結X11 library才能順利成功

1
$ g++ demo.cpp -o demo -lX11

不過奇怪的是當關閉視窗時,總會有以下的錯誤訊息出現

XIO: fatal IO error 11 (Resource temporarily unavailable) on X server “:0.0”
after 12 requests (10 known processed) with 0 events remaining.
Success

Google了一下也沒什麼正確的解法,就不管他。
程式的骨架跟Win32的架構很像,因此不會太難了解。

在CodeProject看到這篇文章之後,參考其他文章而發表的。

GCC nested function

這個Extension只支援GNU C,Clang或者VC++都不行。
這種技術有其名稱,叫做trampoline

這個Hack可模擬Javasciprt等語言使用Closure的特性

lang: c
1
2
3
4
5
6
7
8
typedef int (*func_t)(int);
static func_t f(int arg) {
int nested(int nested_arg) {
return (arg + nested_arg);
}
return &nested;
}

原文提供了更變態的使用方式,不過很難閱讀。

lang: c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(int argc, char *argv[]) {
void (*MySub1)() = ({void _() {
printf("Hello ");
} (void (*)())_;});

MySub1();

({void _() {
printf("World!\n");
} (void (*)())_;})();

return 0;
}

原理

在Linux常常遇到這種情況,記錄一下。
使用sudo執行指令的時候,常常會xxx is not in the sudoers file. This incident will be reported.
表示你的用戶沒有權限使用sudo,必須修改/etc/sudoers這個文件,修改方式如下。

  1. 進入Super user mode

    1
    $ su -
  2. 修改文件權限

    1
    $ chmod u+x  /etc/sudoers
  3. 修改/etc/sudoers內容,在root ALL=(ALL) ALL底下加上一行且存檔。

    1
    xxx ALL=(ALL) ALL

    這裡的xxx就是你的用戶名稱

  4. 回復文件權限

    1
    $ chmod u-x  /etc/sudoers

最近在看Dynamic programming language的時候,注意到了這個特性。
根據Wiki給的定義

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

一般OOP的觀念,就是定義一組Interface為公約數。不受Interface的約束,而動態語言是在執行時檢查是否滿足所需條件。

以下是Ruby的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Duck
def quack
puts "Quaaaaaack!"
end

def feathers
puts "The duck has white and gray feathers."
end
end

class Person
def quack
puts "The person imitates a duck."
end

def feathers
puts "The person takes a feather from the ground and shows it."
end
end

def in_the_forest duck
duck.quack
duck.feathers
end

def game
donald = Duck.new
john = Person.new
in_the_forest donald
in_the_forest john
end

game

動態語言的特性,就是在執行的時刻檢查類別的完整性,如果把上面Person的feathers拿掉,也必須在執行時才會發現錯誤。

如果是傳統競泰語言的話,如何顯示這種特性
C++有兩種方式。

一者是上面介紹的Interface contract

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IAnimal {
public:
virtual void Quack() = 0;
virtual void Feathers() = 0;
};

class Duck : public IAnimal {};
class Person : public IAnimal {};

void in_the_forest(IAnimal *pDuck)
{
pDuck->Quack();
pDuck->Feathers();
}

這種方式可以在Runtime的時候傳入某個滿足此Contract的物件,因此改變其行為。
不過缺點也是顯而易見的,一旦介面修改之後,所有有關的程式碼都要更著修改。所以一般的建議就是一旦介面固定之後就不要更改。

因此衍生了第二種方法,Template。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Duck {
public:
void Quack();
void Feathers();
};
class Person {
public:
void Quack();
void Feathers();
};
template <typename T>
void Program(T& duck)
{
duck.Quack();
duck.Feathers();
}

C++的Template會在C編譯時檢查所有約束條件,因此叫做Static Polymorphism,跟用Interface的Dynamic Polymorphism不同,Runtime沒有擴展性。

這次介紹另一個C++11重要的特性Variadic templates

從printf說起

相信每個寫程式的人,就算沒用過printf,也聽過printf的名字,printf的徒子徒孫大概跟Unix的子孫一樣多。而一般的printf使用方式就類似如此。

1
2
int my_printf(const char * format, ...);
my_printf("%d + %d = %d", a, b, a + b);

在設計一個通用函數的時候,無法知道後面參數有多少個,因此需要一個支持不定參數的機制。
從上面的程式碼看出,我們支援不定參數的語法就是...來表示。
而在C語言如何實做這樣的機制,可以參考MSDN上的範例

Marco也支援不定參數

在C99標準裡,Macro支持不定參數,不過Visual Studio至今不支援C99。
我們可以寫類似這樣的Macro

1
#define dprintf(enable, ...) dprintf_impl(__FILE__, __LINE__, enable, __VA_ARGS__)

在參數列的最後面寫 …,然後就可以用 VA_ARGS 代表 … 所傳入的參數。

如何讓Template支援不定參數

從C++98談起

在實作Command Design Pattern的時候,常常需要把外部函數的參數原封不動的傳遞至內部函數,解決方法大概就像這樣。

1
2
3
4
5
6
7
8
9
10
11
12
#define P1 typename T1
#define A1 T1 &&v1
#define V1 v1
#define P2 typename T1, typename T2
#define A2 T1 &&v1, T2 &&v2
#define V2 v1, v2
#define P3 typename T1, typename T2, typename T3
#define A3 T1 &&v1, T2 &&v2, T3 &&v3
#define V3 v1, v2, v3
template <P1> void outer(A1) { inner(V1); }
template <P2> void outer(A2) { inner(V2); }
template <P3> void outer(A3) { inner(V3); }

這方案的缺點大概有以下幾點

  • 可代入的參數數量有限 (雖然可以手動擴充)
  • 程式碼難以維護,核心的程式碼不多,但是重複的程式碼很多
  • 編譯速度緩慢
  • 極度依賴Preprocessor
    加上C++11的新特性之後,問題變得更複雜了。

C++11時期

來個最簡單的範例

1
2
3
4
5
6
7
8
9
template <typename... Args>
class VariadicTemplate {};
template <typename T, typename ...Args>
class VariadicTemplate1 {};

VariadicTemplate<> a; // VariadicTemplate1接受無任何型別
VariadicTemplate<int> b;
VariadicTemplate1<> c; // VariadicTemplate1至少需要一個型別,編譯器會報錯
VariadicTemplate1<double, int, string> d;

在Args左邊出現...時,表示Args是一個Template type parameter pack,如上面的最後一行,T就是double,而Args就是int, string,除了類別之外,非類別的template paramter也可以這樣使用。如下

1
2
3
template <unsigned ...dims>
class Array {};
Array<3, 4, 5> arr;

而Function template也可以像Class Template一樣使用不定參數

1
2
template <typename ...Args>
void func(Args ...args);

這裡的Args不是Type,args也不是一個value,所以以下的程式碼會出問題

1
2
3
typedef Args MyList;
MyList var;
auto copy = args;

而sizeof也跟著Variadic templates而新增新特性,sizeof...可以印出Args到底有多少個參數

1
2
3
4
5
6
template <typename... Args>
struct VariadicTemplate {
static const unsigned short int size = sizeof...(Args);
};
cout << VariadicTemplate<>::size << endl; // 0
cout << VariadicTemplate<int, int, int>::size << endl; // 3

如何解決之前的問題

用新的特性同時解決perfect forwarding跟Variadic templates

1
2
template <typename ...Ts>
void outer(Ts&& ...args) { inner(std::forward<Ts>(args)...); }

如何抽取單一個型別與參數

透過Template specification來實作

1
2
3
4
5
6
7
8
template <typename T>
void print(T &&v);
template <typename T, typename ...Args>
void print(T &&v, Args ...args)
{
print(std::forward<T>(v));
print(std::forward<Args>(args)...);
}

還有其他未介紹到的特性,基於所知有限<無法完全說明,可以參考Variadic Templates (Revision 3) DraftVariadic Templates are Funadic

前一陣子都在忙C++ Grandmaster的比賽,沒什麼時間紀錄一些東西,最近對Functional Programming感到興趣,找Haskell當作的入門的開始。做些紀錄。

安裝

以Ubuntu為例,先安裝Haskell。

1
$ apt-get install ghc

接著就能用ghci進入直譯器了,用Ctrl+D退出ghci。

Script Programming

有兩種方式可以達成

在ghci當中載入

首先我們先來寫一個test.hs文件

lang: bash
1
2
3
$ cat > test.hs << EOF
add x y = x + y
EOF

在ghci中載入test.hs

lang: bash
1
2
3
4
5
6
$ghci
Prelude> :l test
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> add 3 4
7

當成一般的 Script 使用

這個方案是要加上 #!/usr/bin/runghc 且Script當中需要有main存在
以下是個範例

lang: bash
1
2
3
4
5
6
7
$ cat > hello.hs << EOF
> #!/usr/bin/runghc
> main=putStrLn "Hello, Haskell from elf"
> EOF
$ chmod +x hello.hs
$ ./hello.hs
Hello, Haskell

編譯成執行檔

同樣的,這個方案也需要有main存在

lang: bash
1
$ ghc hello.hs -o hello

其他有關Haskell的教學

網路上可以找到不少,先以這幾個當入門教材

Haskell入門的5個步驟

Haskell在线教程

Tutorials - Haskell