0%

對最近的心得做個總結。
這篇Boost application performance using asynchronous I/O描述了幾種常用的IO Model。
首先要先解釋兩個很像,卻又部太相同的名詞

  • Synchronous Application發起I/O Operation,並且等待其完成 (如 read / write)
  • Non-Synchronous Application僅發起I/O Program Request,由Kernel通知Application完成
  • Blocking Application因為等待某事件,而不能繼續往下執行
  • Non-Blocking Application不受事件影響

Synchronous blocking I/O

最常見的I/O Model,就是在Event未結束前不會將控制權交還給Application。

1
2
int len = recv(s, .....);
// Do other things

在這種Model之下,為了要解決Blocking的問題,就得使用Multi-Porcess或是Multi-Thread的方式來做。

Read more »

隨著Java8推出,新增了一堆新特性。其中有一項就是C# 3.0有的Extension Method。紀錄一下。

問題

在OOP的世界裡,一旦介面推出之後,就不能在更改了。否則依賴於這介面開發的程式必須更著改動。如果Source Code都在自己手上還好。如果是3rd party或是已不在維護的Library,這就變成問題。

1
2
3
4
5
6
7
8
9
public interface MyInterface {
void myFunc();
}
public class Obj1 implements MyInterface {
public void myFunc() {}
}
public class Obj2 implements MyInterface {
public void myFunc() {}
}

如果如果現在Interface需要增加一個myFunc2的功能,該怎麼作。

Read more »

Docker跟Virtual Box這種Virtual Machine上的虛擬化不同,他是基於64 bit linux上,由cgroups/AUFS/LXC為基礎發展出來的Linux virtualization,目前也僅能在Linux上跑,相對於VM等級,更輕量化且更省資源。

安裝

由於Docker需要Linux kernel 3.8以上的,如果版本比較舊的要先更新

1
2
$ sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring
$ sudo shutdown -r now

Update: Ubuntu 14.04

由於Ubuntu 14.04直接收入Docker
所以直接安裝docker.io就好,然後以下的指令全部用docker.io取代docker

1
$ sudo apt-get install docker.io

將Docker的PGP Key跟Repository加入環境中

1
2
$ sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
$ sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ > /etc/apt/sources.list.d/docker.list"

跟新Source且安裝LXC-Docker

1
2
$ sudo apt-get update
$ sudo apt-get install lxc-docker
Read more »

開始玩虛擬化之後,紀錄如何用VirtualBox + Vagrant打造自己的開發環境。

Box

可以把Box想像成光碟,需要安裝的時候,就拿出光碟開始安裝環境。

1
2
3
$ vagrant box list # 列出目前所有的Box
$ vagrant box add {title} {url}
$ vagrant box remove {title}

從網路下載的Box會存在`~.vagrant.d\boxes’下。

Create a virtual machine

有兩種方式,從Box衍生出來,或是寫個VagrantFile衍生。

從Box開始

1
2
$ vagrant init {title}
$ vagrant init {boxname}

後者會將box存到Box List中。
兩者都會產生一個VagrantFile檔案。

Read more »

看了這篇 C11 - Generic Selections
第一直覺感覺和C++ Template很像,不過也不太一樣。
這是C11的Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
void funci(int x)
{
printf("func value = %d\n", x);
}
void funcc(char c)
{
printf("func char = %c\n", c);
}
void funcdef(double v)
{
printf("Def func's value = %lf\n", v);
}
#define func(X) _Generic((X), int: funci, char: funcc, default: funcdef)(X)
int main()
{
func(1);
func('a');
func(1.3);
return 0;
}
Read more »

在電腦裡面,有很多Resource的存在,除了最常見的Memory之外,還有FILEHANDLE等以指標為形,卻有自己的Destroy function的資源。而這種資源該怎麼管理比較好,就是一門學問了。

C語言

翻閱很多Open source的Project之後,發現大部分的作法都類似這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef void(*freefunc)(void *);
struct Object {
void *obj_;
freefunc func_;
};
void custom_alloc(Object *obj, void *obj_, void(*freefunc)(void *))
{
obj->obj_ = obj_;
obj->func_ = freefunc;
}
void custom_free(Object *obj)
{
if (obj->obj_)
{
if (obj->func_)
obj->func_(obj->obj_);
else
free(obj->obj_);
}
}
custom_alloc(&obj, malloc(10), NULL);
custom_free(&obj);
custom_alloc(&obj2, fopen("log.txt", "rb"), (freefunc)(fclose));
custom_free(&obj2);

由於任何指標型態都能跟void 互轉,所以這種作法行得通。
不過由於C語言的Type system很弱,一旦轉成void
之後,無法檢查支援先的Type,如果上面的fclose換成free,編譯器不會警告你任何東西,只有在Runtime才會知道這邊會Core dump。

C++98

由於auto_ptr只能管理Memory之類的Resource,所以我們必須要自行打造輪子。
可以學習C++11中的unique_ptr帶入Destroy function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <typename T>
class CustomResource {
T* obj_;
typedef void (*freeFunc)(T *);
freeFunc func_;
public:
CustomResource(T* obj) : obj_(obj), func_(NULL) {}
CustomResource(T* obj, freeFunc func) : obj_(obj), func_(func) {}
~CustomResource() {
if (func_)
func_(obj_);
else
delete obj_;
}
};
CustomResource<int> m(new int);
CustomResource<FILE> obj(fopen("log.txt", "w"), (void (*)(FILE *))fclose);

雖然這樣子可以盡量避免掉呵叫錯誤Destory function的問題,不過如果硬要cast function prototype也是無法避免。
測試了一下C++11對這種錯誤也沒辦法

1
2
unique_ptr<FILE, int(*)(FILE *)>(fopen("log.txt", "w"), free); // Compile error
unique_ptr<FILE, void (*)(void *)>(fopen("log.txt", "w"), free); // Compile success, but the result is incorrect

另外一個發法是透過Template跟template specialization來解決

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <typename T>
class CustomResource {
T* obj_;
public:
CustomResource(T* obj) : obj_(obj) {}
~CustomResource() {
delete obj_;
}
};
template <>
class CustomResource<FILE> {
FILE* obj_;
public:
CustomResource(FILE* obj) : obj_(obj) {}
~CustomResource() {
if (obj_)
fclose(obj_);
}
};
CustomResource<int> m(new int);
CustomResource<FILE> obj(fopen("log.txt", "w"));

這樣雖然可以解決,不過美增加一種Resource,就會增加一大堆類似的程式碼,因此,C++中罕見的Template template parameter就可以在此發揮所長。
可以參考Writing a Smart Handle class using template template parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <typename T>
struct destory {
static void free(T * obj)
{
delete obj;
}
};
template <>
struct destory<FILE> {
static void free(FILE * obj)
{
fclose(obj);
}
};
template <typename T, typename Destroy = destory<T>>
class CustomResource {
T* obj_;
public:
CustomResource(T* obj) : obj_(obj) {}
~CustomResource() {
if (obj_) Destroy::free(obj_);
}
};

不過就算把fclose換成free也不會有任何警告,這問題還真是難解。

C++11/C++14

在之前提過了,可以透過unique_ptr跟custom destructor來達成同樣的目的。

1
unique_ptr<FILE, int (*)(FILE *)> obj(fopen("log.txt", "w"), fclose);

C++14之後,新增了make_unique這個函數,我們可以仿造這方式,寫出一個make_resource的函數。
參考這一篇C++14 and SDL2: Managing Resources寫來的。

1
2
3
4
5
6
7
8
9
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);
}
auto obj = make_resource(fopen, (void (*)(FILE *))fclose, "log.txt", "w");

這段程式碼的auto跟decltype已經有個抵了,部過decay還看不懂。有時間在研究吧

以往在C++98寫的Code,䅰習慣用Raw Pointer來操作Object,在C++11/14之後,有了更安全的面貌。少了new/delete之後,接觸到Memory Leak的機會變少了。

Insert Element into container

1
2
3
deque<Object *> objs;
for (int i = 0; i < 3; i++)
objs.push_back(new Object());

現在可以這麼寫

1
2
3
deque<unique_ptr<Object>> objs;
for (int i = 0; i < 3; i++)
objs.push_back(make_unique<Object>());

C++11有make_shared部過沒有make_unique,到C++14才加入,部過我們可以打造個一模一樣的版本。

1
2
3
4
5
6
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args ) {
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
for (int i = 0; i < 3; i++)
objs.push_back(make_unique<Object>(i));

Erase element from Container

以往的寫法大概就像這樣

1
2
3
4
5
6
deque<Object*>::iterator  it = std::find_if(objs.begin(), objs.end(), [](const Object *obj) -> bool { ... });

if (it != objs.end()) {
delete *it;
objs.erase(it);
}

最常望記得就是那個delete,然後就造成Memory leak。
新的寫法就像這樣

1
2
3
4
auto it = std::find_if(objs.begin(), objs.end(), [](const unique_ptr<Object> &obj) -> bool { ...});

if (it != objs.end())
objs.erase(it);

被指向的Object resource會在某個時間點被歸還。

Get element from container

這邊可分成兩部份,單純瀏覽整個Container中的Elemeent,不對Container作任何動作。
或是當作資料結構,會改變Container本身的狀態。
先從第一種來說明,以往可能這麼作

1
2
3
4
5
for (auto it = objs.begin(); it != objs.end(); ++it)
{
Object *obj = *it;
// Do something
}

用了unique_ptr之後,只能這麼做了

1
2
3
4
5
for (auto it = objs.begin(); it != objs.end(); ++it)
{
Object *obj = (*it).get();
// Do something
}

如果要直接操作Container,像輕空Buffer queue的情形時,以前的作法

1
2
3
4
5
6
7
8
while (!objs.empty())
{
cout << "Move from vector" << endl;
Object *pObj = objs.front();
objs.pop_front();
// Do something
delete pObj;
}

不過由於unique_ptr不像auto_ptr擁有copy semantics,因此上面的程式碼要改成

1
2
3
4
5
6
7
while (!objs.empty())
{
cout << "Move from vector" << endl;
unique_ptr<Object> s = std::move(objs.front());
objs.pop_front();
// Do something
}

可以看到,跟Raw Pointer操作相差無幾,不過利用RAII技術減少Memory leak的發生。

這種檔案格式最近才流行起來,7zup可以直接解壓縮。
在Linux底下可以這麼作

1
2
$ xz *.tar.xz
$ tar xvf *.tar

chrono原先是來自於boost,現在進入C++11 Standard了。
鑽簡單的範例就是用來作高精密度的Timer使用。原先在Windows有QueryPerformanceCounter的函數,可以精密到nanoseconds,在Liunx/BSD很難找到類似的解法,不過C++11把這納入標準了,不必花太多功夫。
以下是一個範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
void f()
{
this_thread::sleep_for(chrono::seconds(1));
}

int main()
{
auto t1 = chrono::high_resolution_clock::now();
f();
auto t2 = chrono::high_resolution_clock::now();
std::cout << "f() took "
<< chrono::duration_cast<chrono::seconds>(t2 - t1).count()
<< " seconds\n"
<< chrono::duration_cast<chrono::milliseconds>(t2 - t1).count()
<< " milliseconds\n"
<< chrono::duration_cast<chrono::nanoseconds>(t2 - t1).count()
<< " nanoseconds\n";
return 0;
}

更多的使用方法可以參考

最近遇到在GitHub frok出來的Branch改爛之後,想要捨棄掉自己的修改,重新跟上Upstream的狀態。
沒想到已經有人解答了
Clean up a fork and restart it from the upstream
GitHub page, section “What should I do if I’m in a bad situation”
就是以下四個指令的組合技

1
2
3
4
$ git fetch upstream
$ git checkout master
$ git reset --hard upstream/master
$ git push origin master --force