0%

Create Hadoop Account

1
2
3
$ sudo useradd -m hadoop -s /bin/bash
$ sudo passwd hadoop
$ sudo adduser hadoop sudo

Setup ssh environemtn

1
2
3
4
$ sudo apt-get install openssh-server
$ cd ~/.ssh/
$ ssh-keygen -t rsa
$ cat id_rsa.pub >> authorized_keys

Instal Java

1
$ sudo apt-get install openjdk-7-jre openjdk-7-jdk

修改~/.bashrc押入

export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

1
$ source ~/.bashrc

Install Hadoop

1
2
3
$ sudo tar -zxvf ./hadoop-2.7.0.tar.gz -C /usr/local
$ sudo mv ./hadoop-2.7.0/ ./hadoop
$ sudo chown -R hadoop:hadoop ./hadoop

單機Hadoop建立

執行測試程, ˊ執行時記得不能有output`目錄存在

1
2
3
4
$ sudo mkdir input
$ sudo cp README.txt input
$ bin/hadoop jar share/hadoop/mapreduce/sources/hadoop-mapreduce-examples-2.7.0-sources.jar org.apache.hadoop.examples.WordCount input output
$ cat output/*

偽分布式Hadoop建立

修改/usr/local/hadoop/etc/hadoop/core-site.xml

1
2
3
4
5
6
7
8
9
10
11
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>file:/usr/local/hadoop/tmp</value>
<description>Abase for other temporary directories.</description>
</property>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>

修改/usr/local/hadoop/etc/hadoop/hdfs-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/name</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/data</value>
</property>
</configuration>

###請動Hadoop
進行Namenode格式化

1
$ bin/hadoop  namenode -format

啟動Guardian process

1
s bin/start-dfs.sh

可以連到 http://localhost:50070 來觀察adoop狀態

俄式偽分布式Hadoop

1
2
3
4
$ bin/hdfs dfs -mkdir -p /user/hadoop
$ bin/hdfs dfs -put README.txt input
$ bin/hadoop jar share/hadoop/mapreduce/sources/hadoop-mapreduce-examples-2.7.0-sources.jar org.apache.hadoop.examples.WordCount input output
$ bin/hdfs dfs -cat output/*

Reference

長知識了,直接看Code比較快

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
int calc(std::exception_ptr& eptr)
{
try {
std::string s;
s.at(0); // throw out of range exception
} catch (...) {
eptr = std::current_exception();
}
return 0;
}
int main()
{
std::exception_ptr eptr;
auto f = std::async(std::launch::async, std::bind(calc, std::ref(eptr)));
try {
int i = f.get();
if (eptr) {
std::rethrow_exception(eptr);
}
std::cout << i << std::endl;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}ion_ptr eptr;
auto f = std::async(std::launch::async, std::bind(calc, std::ref(eptr)));
try {
int i = f.get();
if (eptr) {
std::rethrow_exception(eptr);
}
std::cout << i << std::endl;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
return 0;
}

如果要在Thread傳輸exception的食,記得紀錄一個exception_ptr,當exception發生食,用current_exception()捕捉在當今thread發生的excetipn。然後其他Thread發現這個eptr存在的話,透過rethrow_exception處理這個exception。
不過這樣作一點都不直覺啊

Better Solution

搭配同時加入C++11的thread和future,可以寫出更加優雅的Code,promise不僅解決了exception的問題,同時也提供了Thread return value的問題>

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
void calc(std::promise<int> && p)
{
try {
std::string s;
s.at(0); // throw out of range exception
p.set_value(0);
} catch (...) {
p.set_exception(std::current_exception());
}
}
int main()
{
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread thd(calc, std::move(p));
try {
int i = f.get();
std::cout << i << std::endl;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
thd.join();
return 0;
}

‵``

這故事說起來還真不簡單,在寫Code的時候,往往與這兩樣東西牽扯不清。
例如以下這段Code

1
2
3
FILE *fp = fopen( "input.txt", "r");
if (fp == NULL)
printf (" err %d \n", errno);

fp代表著回傳的正常值,errno表示萬一錯誤的時候,可以供判斷的錯誤依據。
不過就算是return value跟error code,也有很長故事可以說。
$ f(x)=\frac{100}{\frac{100}{x}+1} $當我們的範例,來討論各種處理return value跟error handleing

Global state

在設計API的時候,就完全沒考慮過Error這件事,如同上面的errno那樣,我們可以仿照使用一個global variable。
可能的作法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int _errno;
const int invalid_value = -1;
int g(int x) {
if (x == 0) {
_errno = -1;
return invalid_value;
}
return 100 / x + 1;
}
int f(int x)
{
int v1 = g(x);
if (v1 == invalid_value)
return v1;
if (v1 == 0) {
_errno = -2;
return invalid_value;
}
return 100 / v1;
}

這作法不難懂,不過有幾個很重大的缺點
– 需要對Return Value定義一個特蘇的值,代表這個值是無效的,而這個Return Value有可能跟值域的某個數值碰撞。例如上例的f(-50),你不知道他是錯誤還是有效值。
– global variable表示任何人都有機會更動到,因此在Code的Maintain跟Protect上難很多
– Error很容易被忽略,因為沒有強制性,可能在實做內部,或是外部使用,都有可能因為Code的修改而跳過Error Code處理

因此有了改良版的方法

Return Error and Value simultaneously

上面方法的改良版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int g(int x, int *retvalue) {
if (x == 0)
return -1;
*retvalue = 100 / x + 1;
return 0;
}
int f(int x, int *retvalue)
{
int err = g(x, retvalue);
if (err != 0)
return err;
if (*retvalue == 0)
return -2;
*retvalue = 100 / (*retvalue);
return 0;
}

這邊的寫法類似於COM的作法,把return value放在最後一個參數,而errorcode當作return值傳回。
也可以像golang那樣傳回多個回傳質的方法,大同小異

1
2
3
4
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}

這方是解決了上述錢兩個問題,不過還是無法解決第三個問題

Exception-Based solution

越來越多程式語言都加入當標準配備了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int g(int x) {
if (x == 0)
throw std::exception("cannot be zero in g");
return 100 / x + 1;
}
int f(int x)
{
int v1 = g(x);
if (v1 == 0)
throw std::exception("cannot be zero in f");
return 100 / v1;
}
try {
f(0);
}
catch (std::exception e) {
}

比起上述兩種,exception的優點
– 不需要特異定義一堆內部的Error Code Value,並且Code的可讀性比上面兩者都強。可以專住在Logic上的Code。
而缺點是:
– 效能問題,雖然這問題越來越不重要,不過使用Exception的方案通常比Error code慢。
– Boundary issue,當跨越兩個Shared library的程式碼互動食,這方案完全派不上用場。還是要走回上面的老路。

Railway Oriented Programming

錢幾個作法都是遇到問題就往上拋,而ROP反其道而行,將錯誤往後傳,最後統一處理。

這例子改用ustㄉ做示範範例 因為C++诶有ML系Language Pattern Matching和Abstract Data Type,要模擬這個不如直接換個語言寫寫看當練習。

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
enum Result {
Value(i32),
Error(&'static str)
}
fn div(x: Result, y: Result) -> Result {
match y {
Result::Value(0) => Result::Error("Cannot divide 0"),
Result::Value(y_) => {
match x {
Result::Value(x_) => Result::Value(x_ / y_),
_ => x
}
},
_ => y
}
}
fn add1(x: Result) -> Result {
match x {
Result::Value(v) => Result::Value(v + 1),
_ => x
}
}
fn f(x: i32) -> Result {
return div(Result::Value(100), add1(div(Result::Value(100), Result::Value(x))));
}
fn main() {
let value = f(25);
match value {
Result::Value(v) => println!("The result value is {}", v),
Result::Error(str) => println!("Error happen, {}", str)
}
}

座個事情很簡單,如果Result是有效的值就繼續往下做,不然就直接往後傳。

Enhance Monad solution for Railway Oriented Programming

網路上有很多Monad的介紹,於是東施校憑寫了一個山寨版的。關鍵在於所謂的bind函數,這裡就不多介紹了。

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
trait Monad {
fn unit(x: i32) -> Self;
fn bind(&self, f: fn(i32) -> Self) -> Self;
}
enum Result {
Value(i32),
Error(&'static str)
}
impl Monad for Result {
fn unit(x: i32) -> Result {
Result::Value(x)
}
fn bind(&self, f: fn(i32) -> Self) -> Self {
match *self {
Result::Value(x) => f(x),
Result::Error(str) => Result::Error(str)
}
}
}
fn div100(x: i32) -> Result {
match x {
0 => Result::Error("Cannot divide 0"),
_ => Result::Value(100 / x)
}
}
fn add1(x: i32) -> Result {
Result::Value(x + 1)
}
fn f(x: i32) -> Result {
Result::unit(x).bind(div100).bind(add1).bind(div100)
}
~

Reference

C++ Exceptions: Pros and Cons
http://fsharpforfunandprofit.com/rop/

std::decay

如名稱所說,將所獲取到的類型作退化的動作。
先移除類型T的Reference,得累類型U

  • 如果 is_array::value 為 treu,修改類型type為remove_extent::type *
  • 如果 is_function::value 為 true,修改類型type為add_pointer::type
  • 否則,修改類型type為remove_cv::type
    不過decay可以用來作些什麼?

    將參數以值的方式傳回

    N2609提了一種用法
    1
    2
    3
    4
    5
    template <class T1, class T2> 
    inline pair<T1,T2> make_pair(T1 x, T2 y)
    {
    return pair<T1,T2>(x, y);
    }
    這段Code能動,不過會多出複製物件的成本
    1
    std::pair<std::string, int> p = make_pair("foo", 0);
    如果我們改用Refernece的傳法重寫的話
    1
    2
    3
    4
    5
    template <class T1, class T2> 
    inline pair<T1,T2> make_pair(T1&& x, T2&& y)
    {
    return pair<T1,T2>(x, y);
    }
    以上麵這個範例的話,T1會被推導成const char[4],而不是const char *。因此我們需要Decay
    正確的寫法類似這樣
    1
    2
    3
    4
    5
    6
    7
    8
    template <class T1, class T2> 
    inline pair< typename decay<T1>::type, typename decay<T2>::type >
    make_pair(T1&& x, T2&& y)
    {
    return pair< typename decay<T1>::type,
    typename decay<T2>::type >(std::forward<T1>(x),
    std::forward<T2>(y));
    }

    之前的make_resource範例

    1
    2
    3
    4
    5
    6
    7
    8
    template<typename Creator, typename Destructor, typename... Arguments>
    auto make_resource(Creator c, Destructor d, Arguments&&... args)
    {
    auto r = c(std::forward<Arguments>(args)...);
    if (!r) { throw std::runtime_error{ "Unable to create resource" }; }
    typedef typename std::decay<decltype(*r)>::type ResourceType;
    return std::unique_ptr<ResourceType, void(*)(ResourceType*)>(r, d);
    }
    為了decay推導出primitive type,將變化包裝起來不讓外界知道。

std::result_of

看了Stackoverflow,覺得這東西可以完全用decltype去代。有興趣的話可以參考CppReference的用法。

std::enable_if

SFINAE

前一陣子在做C++轉C Code的動作,真是麻煩的苦工。 在看到Modern C++ in embedded systems – Part 1: Myth and Reality還真是心有慼慼焉啊。C++能做的,C當然也可以,不過程式可讀性差很多,以下列出幾點。

Function overload

1
2
3
4
void Dosomething(int v);
void Dosomething(float v);
Dosomething(1);
Dosomething(3.14f);

這樣的Code在C++可行,在C會編譯失敗,因此要自行加上suffix錯開

1
2
3
4
void DosomethingWithInt(int v);
void DosomethingWithFloat(float v);
DosomethingWithInt(1);
DosomethingWithFloat(3.14f);

雖然效果是一樣的,不過人的記憶力是有限的,同時兩個淚名稱不同,功能相似的函式,有事沒事要分心注意,會殺死一堆腦細胞。
在C11之後,終於有了Generic Selections可用了。

1
2
3
4
5
define Dosomething(X) _Generic((X),  \
int: DosomethingWithInt, \
float: DosomethingWithFloat)(X)
Dosomething(1);
Dosomething(3.14f);

這下看起來好多了,不過如果每增加一種寒士,就要修改一次Dosomething的定義。還是很麻煩啊。這也無法解決所有問題。

Constructor & Destructor

在C++當中是

1
2
3
4
5
6
7
8
9
10
struct Obj {
Obj(int v);
~Obj();
};
int Doanotherthing(Obj *v);
nt Dosomething()
{
Obj local(10);
return Doanotherthing(&local);
}

同樣的Code在C與研究寫得支離破碎了

1
2
3
4
5
6
7
8
9
10
void init_Obj(Obj *this, int v) 
void uninit_Obj(Obj *this);
int Dosomething()
{
Obj local;
init_Obj(&local, 10);
int result = Doanotherthing(&local);
uninit_Obj(&local);
return result;
}

由於C不會主動去呼叫Destructor,因此要將計算的值存起來,所以要主動呼叫Destructor。
由於我們不能簡單的把Constructor和Destructor定義成inituninit就好,原因同上面的Function overload,只好加上自行的suffix避開這個問題。

Member function

雖然這兩樣寫差異無幾

1
2
3
Obj *obj;
obj->Dosomething(); // member function
Dosomething(obj); // pass object to function

萬一有個cstruct 也有Dosomething的函式怎麼辦

1
2
3
Obj2 *obj2;
obj2->Dosomething(); // member function
DosomethingWithObj2(obj2); // pass object to function

又回到老問題了,不能function overload就要手動繞過很多眉角。

又是一個很雜的題目。

Continuation

看了很多定義之後,覺得這定義是最好的。

所謂continuation,其實本來是一個函數調用機制。

我們熟悉的函數調用方法都是使用堆棧,采用Activation record或者叫Stack frame來記錄從最頂層函數到當前函數的所有context。一個frame/record就是一個函數的局部上下文信息,包括所有的局部變量的值和SP, PC指針的值(通過靜態分析,某些局部變量的信息是不必保存的,特殊的如尾調用的情況則不需要任何stack frame。不過,邏輯上,我們認爲所有信息都被保存了)。函數
的調用前往往伴隨著一些push來保存context信息,函數退出時則是取消當前的record/frame,恢複上一個調用者的record/frame。

如果有用過C/C++/Java等開發過,對於Stack Frame隊上面這段話應該不陌生。

Continuation則是另一種函數調用方式。它不采用堆棧來保存上下文,而是把這些信息保存在continuation record中。這些continuation record和堆棧的activation record的區別在於,它不采用後入先出的線性方式,所有record被組成一棵樹(或者圖),從一個函數調用另一個函數就等於給當前節點生成一個子節點,然後把系統寄存器移動到這個子節點。一個函數的退出等於從當前節點退回到父節點。 

這些節點的刪除是由garbage collection來管理。如果沒有引用這個record,則它就是可以被刪除的。 

Read more »

From callback to (Future -> Functor -> Monad) 這篇寫得很好,雖然是用Javascript實做,不過還是值得一看。目前我的能力還不足以使用C++重寫一次,先用殘破的Javascipt來練習吧。

Callback

1
2
3
4
fs.readFile('...', function (err, data) {
if (err) throw err;
....
});

這就是continuation-passing style。最大的問題就是nested callback之後,整個程式碼雜亂無章,難以維護。因此想了很多方法來改善流程。

Future

現在各大語言都有Future跟Promise了,Javascript的版本可以參考上面那篇文章的實做。

這邊是參考How to JIT - an introductionHello, JIT World: The Joy of Simple JITs 的感想。

Sample Code

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

// Allocates RWX memory of given size and returns a pointer to it. On failure,
// prints out the error and returns NULL.
void* alloc_executable_memory(size_t size) {
void* ptr = mmap(0, size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == (void*)-1) {
perror("mmap");
return NULL;
}
return ptr;
}

void emit_code_into_memory(unsigned char* m) {
unsigned char code[] = {
0x48, 0x89, 0xf8, // mov %rdi, %rax
0x48, 0x83, 0xc0, 0x04, // add $4, %rax
0xc3 // ret
};
memcpy(m, code, sizeof(code));
}

const size_t SIZE = 1024;
typedef long (*JittedFunc)(long);

// Allocates RWX memory directly.
void run_from_rwx() {
void* m = alloc_executable_memory(SIZE);
emit_code_into_memory(m);

JittedFunc func = m;
int result = func(2);
printf("result = %d\n", result);
}
Read more »

雖然原來名稱叫10 C99 tricks,不過有些跟C99沒關係,不過還是有參考價值。

Ternary operator without middle operand (gnu extension)

1
2
3
4
5
// Instead of
x = x ? x : 10;

// We can use the shorter form:
x = x ?: 10;

個人覺得, 這個樣子更不容易看出程式在寫什麼

Unamed struct for compound type

根據實驗之後,這也不需要C99,C89就能正常運作了
在一般的情形之下,這樣子Compilier會提出警告

1
2
3
struct {
float x, y, z;
};

Clang發出這樣的警告

1
2
3
4
demo.c:5:1: warning: declaration does not declare anything [-Wmissing-declarations]
struct {
^~~~~~
1 warning generated.

但是如果再union / struct當中這樣使用的話就沒有問題,可以依照自己喜歡的方式使用

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef union {
struct { float x, y, z; };
struct { vec2_t xy; };
struct { float x_; vec2_t yz; };
float v[3];
} vec3_t;
#define VEC3(x, y, z) { {x, y, z} }

vec3_t vec = VEC3(1, 2, 3);
// We can access the attributes in different ways.
float x = vec.x;
vec2_t xy = vec.xy;
float z = vec.v[2];

IS_DEFINED macro

無法理解為什麼要設計的這麼複雜..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// As used in the linux kernel.
// A macro that expands to 1 if a preprocessor value
// was defined to 1, and 0 if it was not defined or
// defined to an other value.

#define IS_DEFINED(macro) IS_DEFINED_(macro)
#define MACROTEST_1 ,
#define IS_DEFINED_(value) IS_DEFINED__(MACROTEST_##value)
#define IS_DEFINED__(comma) IS_DEFINED___(comma 1, 0)
#define IS_DEFINED___(_, v, ...) v

// Can be used in preprocessor macros:
#if IS_DEFINED(SOMETHING)
...
#endif

// Or even directly in the code.
// Same effect but looks better.
if (IS_DEFINED(SOMETHING)) {
...
}

Convenience macro for Debuging

其實這個方法不限於OpenGL,像GetLastErrorerrno都可以用類似的方法來確認狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Not really special, but so useful I thought
// I'll put it here. Can also be used with other
// libraries (OpenAL, OpenSLES, ...)
#ifdef DEBUG
# define GL(line) do { \
line; \
assert(glGetError() == GL_NO_ERROR); \
} while(0)
#else
# define GL(line) line
#endif

// Put GL around all your opengl calls:
GL(glClear(GL_COLORS_MASK));
GL(pos_loc = glGetAttribLocation(prog, "pos"));

Array size macro

這個也不世新玩意了

1
2
3
4
5
6
7
8
9
10
11
// Is there any C project that does not use it?
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

// Can be used like this:
int a[] = {0, 4, 5, 6};
int n = ARRAY_SIZE(a); // n = 4

// Warning: does not work with array arguments to functions:
int func(int a[]) {
int nb = ARRAY_SIZE(a); // Would not work!
}

Safe-type macro (uses a gnu extension)

當然這也不是只用於min, max, 還可以用於swap等…弱化版的Template。

1
2
3
4
5
#define min(a, b) ({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})

Passing pointer to unnamed variables to function.

這是一般的寫法

1
2
3
4
void func(const int *arg);
// Instead of using a local variable.
int tmp[] = {10, 20, 30};
func(tmp);

不過可以寫成這樣

1
2
// We can write.
func( (const int[]){10, 20, 30} );

更進一步

1
2
3
// Can be useful with a helper macro.
#define VEC(...) ((const int[]){__VA_ARGS__})
func(VEC(10, 20, 30));

如果搭配上designated initializers威力更大

1
2
3
4
5
6
typedef struct {
float x, y;
} vec2_t;
void func(const vec2_t *arg);
#define make_struct(T, ...) (&(const T){ __VA_ARGS__})
func(make_struct(vec2_t, .y = 9999, .x = 20));

Named initializer, with default values

designated initializers跟Variadic Macros的組合技

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// I use this one all the time when writing
// video game. One of the reason why I
// don't like to use C++.

// Let say we have this struct
struct obj {
const char *name;
float pos[2];
float color[4];
};

// We can write a macro like this one
#define OBJ(_name, ...) \
(struct obj) { \
.name = _name, \
.color = {1, 1, 1, 1}, \
__VA_ARGS__ \
};

// Now we can use the macro to create new objects.
// This one with color defaulted to {1, 1, 1, 1}.
struct obj o1 = OBJ("o1", .pos = {0, 10});
// This one with pos defaulted to {0, 0}.
struct obj o2 = OBJ("o2", .color = {1, 0, 0, 1});

X macros

即使在C++當中,這也是個非常重要的技巧之一,利用Macro來進行Code Generation。
X Macro分成兩部分,一個是彼此相相關連的List,另外一個是巨集,對這個List進行展開動作,而這點Template無能為力。
例如

1
2
3
4
5
6
7
8
9
10
11
#define COLORS \
X(Cred, "red") \
X(Cblue, "blue") \
X(Cgreen, "green")

#define X(a, b) a,
enum Color { COLORS };
#undef X
#define X(a, b) b,
static char *ColorStrings[] = { COLORS };
#undef X

當要新增一種顏色的時候,只需要在COLORS那邊修改,減少了維護和犯錯的可能性。
如果需要更進一步的學習,可以參考

  • X Macro
  • The X Macro
  • The New C: X Macros
  • Reduce C-language coding errors with X macros Part 1 Part 2 Part 3
  • Real-world use of X-Macros

    State machine helper using LINE

    Generator的簡易實現,將S__LINE__來當做State之一。
    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
    // This is a great trick.
    // Instead of:

    int iter(int state) {
    switch (state) {
    case 0:
    printf("step 0\n");
    return 1;
    case 1:
    printf("step 1\n");
    return 2;
    case 2:
    printf("step 2\n");
    return 3;
    case 3:
    return -1;
    }
    }

    // We can define:
    #define START switch(state) { case 0:
    #define END return -1; }
    #define YIELD return __LINE__; case __LINE__:;

    // And now the function can be written
    int iter(int state) {
    START
    printf("step 0\n");
    YIELD
    printf("step 1\n");
    YIELD
    printf("step 2\n");
    END
    }