0%

不定期更新

C Language

C++ Language

未分類

Stackoverflow看到這串討論很有意思。
以下的Code,雖然時間複雜度都一樣O(n),不過排序過後的速度遠遠超過沒排序,將sort註解掉之後可以看出很明顯的差異。

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
#include <algorithm>
#include <ctime>
#include <iostream>

int main()
{
// Generate data
const unsigned arraySize = 32768;
int data[arraySize];

for (unsigned c = 0; c < arraySize; ++c)
data[c] = std::rand() % 256;

// !!! With this, the next loop runs faster
//std::sort(data, data + arraySize);

// Test
clock_t start = clock();
long long sum = 0;

for (unsigned i = 0; i < 100000; ++i)
{
// Primary loop
for (unsigned c = 0; c < arraySize; ++c)
{
if (data[c] >= 128)
sum += data[c];
}
}

double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

std::cout << elapsedTime << std::endl;
std::cout << "sum = " << sum << std::endl;
}
Read more »

寫起來,免得忘記。

在程式Crash前直接呼叫gdb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void dump(int signo)
{
char buf[1024];
char cmd[1024];
FILE *fh;

snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());
if (!(fh = fopen(buf, "r")))
exit(0);
if (!fgets(buf, sizeof(buf), fh))
exit(0);
fclose(fh);
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());
system(cmd);

exit(0);
}
signal(SIGSEGV, &dump);
Read more »

對最近的心得做個總結。
這篇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的發生。