最近這種雜誌越來越多了,列出來備忘。
ack / ag - enhance grep for programmer
相信大家都有用過grep來找文字,不過這個ack是專為程式設計師開發的。
從官網下載並安裝
1 | $ curl http://beyondgrep.com/ack-2.12-single-file > ack |
ack有以下這些特性
- 預設就搜尋當前目錄
- 預設搜尋所有子目錄
- 忽略Metadata資料節,如
.svn
和.git
等 - 忽略Binary檔
- 印出找到Pattern的行號
- 能夠搜尋特定文件類型的檔案 (如Perl/C++)
- Highlight搜尋結果
- 支持Perl的Advance Regular Expression
幾個常用的功能
如何只搜尋eat
而忽略掉feature
和`eating
1 | $ ack -w eat |
當Pattern中有特殊字元的時候,需要當作Literal chracter被匹配
1 | $ ack -Q '$path/$' |
放棄搜尋某些目錄
1 | $ ack about --ignore-dir=downloads |
列出某些特定文件類型的檔案,列出擁有Pattern的檔案名稱
以Makefile為例,可能的檔名有 *.mk, makefile, Makefile
我們想知道這些檔案裡面哪些定義了CFLAGS
,就能夠這樣作
1 | $ ack --make -l CFLAG |
Highlight某些特定Pattern
1 | $ tail -f /var/log/syslog | ack --passthru 192.168.1.10 |
這裡的passthru是不管有沒有匹配到都會輸出。
Update
在網路上釉看到ag這套軟體。用途大同小異。多了一些ack沒有的功能。
可以參考Conquering the Command Line Chapter 2. Ack/Ag
git workflow
既然git是個自由度很高的version control system,對於該怎麼工作這件事,也沒有什麼固定答案,除了當SVN那樣子用法之外,還有其他答案,因此就有這篇文章的產生。
Simple Git Workflow
重點只有三個
- 當要開發New features時,就開一個New feature的Branch,且在上面開發
- 將New feature的Code合併回master
- 另外準備一個Branch,用作Deploy/Release用,在這邊發布的Code要經過Well test過的
A successful Git branching model
這是上面那種方式的無敵加強版
- Mainstream從master移到了develop,master只維持穩定的版本。
- feature分支一樣是開發New feature,完成之後會合併至develop
- Release branches: 準備要 release 的版本,只修 bugs。從 develop 分支出來,完成後 merge 回 master 和 develop
- Hotfix branches: 等不及 release 版本就必須馬上修 master 趕上線的情況。會從 master 分支出來,完成後 merge 回 master 和 develop
這套流程固然強大,不過太複雜了。
用git-flow可以幫助簡化一些情況。
可以參考Git flow 開發流程
Github flow
有鑑於上面那種方式太複雜,又衍生出來的新方式,也是GitHub自己在用的工作流程。感覺很像第一種方式,不過加上了Code review的能力。
master branch必須是deployable
每個功能fork出branch,push到remote repository
發送pull request
master developer會Review code且merge to master
這篇 Why Github Flow Better? 簡單介紹了git flow跟github flow的優缺點。
[Git Tutorials] (https://www.atlassian.com/git/workflows)簡介了大部分的Git flow。
Tools for multi-thread programming for linux
Helgrind 和 DRD
這兩個工具都是Valgrind的一部分,用途也相同,檢查Thread error,不過用的策略不同,可以交替使用檢茶室否有無隱藏的錯誤。
以下是從Binary hacks抄下的範例
1 | #include <pthread.h> |
裡面有兩個錯誤,一個是count在multi-thread的情況沒有保護,這種情況也可以用下面的thread-sanitizer
偵測出來。
另外一種情況就是lock的順序不同,導致Deadlock的情景。
編譯且執行
1 | $ gcc demo.c -o demo -lpthread |
輸出太長,列出感興趣的部份
==5172== Possible data race during write of size 4 at 0x600C90 by thread #3
==5172== Locks held: none
==5172== at 0x40065F: incr_count (in /home/hungming/a)
==5172== by 0x4C2DB38: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5172== by 0x4E3BE99: start_thread (pthread_create.c:308)
==5172==
==5172== This conflicts with a previous write of size 4 by thread #2
==5172== Locks held: none
==5172== at 0x40065F: incr_count (in /home/hungming/a)
==5172== by 0x4C2DB38: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5172== by 0x4E3BE99: start_thread (pthread_create.c:308)
上面這編列出可能有data-race的情形。
==5172== Thread #5: lock order “0x600CA0 before 0x600CC8” violated
==5172==
==5172== Observed (incorrect) order is: acquisition of lock at 0x600CC8
==5172== at 0x4C2DFCD: pthread_mutex_lock (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5172== by 0x4006FB: lock_m2_then_m1 (in /home/hungming/a)
==5172== by 0x4C2DB38: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5172== by 0x4E3BE99: start_thread (pthread_create.c:308)
==5172==
==5172== followed by a later acquisition of lock at 0x600CA0
==5172== at 0x4C2DFCD: pthread_mutex_lock (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5172== by 0x40070B: lock_m2_then_m1 (in /home/hungming/a)
==5172== by 0x4C2DB38: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5172== by 0x4E3BE99: start_thread (pthread_create.c:308)
這邊告訴我們lock的順序不對。
更多的使用方法可以參考
Helgrind使用說明
DRD使用說明
thread-sanitizer
thread-sanitizer現在已經是LLVM的一部分,在編譯LLVM的時候就會編譯完成,而GCC 4.8之後也支援thread-sanitizer。
這跟上面的不同是檢查data-race issue。
寫個sample code
1 | #include <pthread.h> |
這個範例很簡單,可以看出 Global 在不同Thread下操作可能出現問題。
編譯且執行,注意要加上-fsanitize=thread
1 | $ clang simple_race.c -fsanitize=thread -g |
同樣列出我們所關心的部份
WARNING: ThreadSanitizer: data race (pid=4441)
Location is global ‘Global’ of size 4 at 0x7f4d31e90ad8 (a+0x0000016caad8)
SUMMARY: ThreadSanitizer: data race ??:0 Thread2
有了Tool之後,從Log分西問題出在哪就便得很重要了。
git subtree
在網路上搜尋了一下,發現這功能真是好用。
原先的git submodule缺點不少,一堆批評的聲音。因此之後就有git subtree的誕生,,官方也建議用git subtree解決大部分的問題。
主要應用在兩個場景
引用另外一個Repository的管理
假設在我們的Repository當中,需要引用另一個Repository(如Application需要3rd party library)的內容時,盡其希望其Repository能夠跟著更新,一旦我們對其修改時,也能擁有修改和提交的權利。
第一步: 建立目前Repository跟Sub Repository的關聯性
1 | $ git remote -f add <name of sub_repo> <address of sub_repo> |
第一個指令加上-f
是建立關聯之後再度進行fetch作業。
而第二個指令加上--squash
是要把Sub Repository的history合併成一個。
例如
1 | $ git remote add -f ai https://github.com/aoxu/ai.git |
第二步: 更新Sub Directory
一旦Sub Repository有人更改之後,我們希望把修改的東西合併到我們 Repository中。
1 | $ git fetch <name of sub_repo> <branch> |
這就類似上面的情況了,一樣給的範例
1 | $ git fetch ai master |
第三步: 將修改推送到Remote Repository
Push只需要一個步驟
1 | $ git subtree push --prefix=<name of sub_directory> <name of sub_repo> <branch> |
同樣有個範例
1 | $ git subtree push --prefix=ai ai master |
如果不用git subtree的話,也有個Subtree merge strategy見仁見智了。
參考資料:
- Alternatives To Git Submodule: Git Subtree
- Managing Nested Libraries Using the GIT Subtree Merge Workflow
將Sub directory拆成令外一個Repository
這剛好跟上面那個使用奇境相反,使用方式如下
第一步: 先將sub directory的資料建立new branch
1 | $ git subtree split -P <sub_directory> -b <branch> |
第二步: 準備new repository並且啦取原先repository的branch資訊
1 | $ mkdir <new-repo> && cd <new-repo> |
第三步: 建立Remote Repository的關聯並推送到遠方
1 | $ git remote add origin <git@github.com:my-user/new-repo.git> |
參考資料:
Use case for smart pointer
看了書之後豁然開朗啊,之前還真是個半調子。總結一下使用情境。
從失敗的例子講起
1 | struct Obj { |
這段程式碼最大的問題就是,如果ObjFactory的instance沒被摧毀,所有拿到的Obj都不會被釋放。
用 weak_ptr 取代 shared_ptr
在ObjectFactory的部份不要保存shared_ptr,這樣會增加reference count,用weak_ptr取而代之。需要的話再promotion成shared_ptr。
1 | class ObjFactory { |
看起來沒什麼問題,不過譽到以下這種情況就爛了
1 | for (int i = 0; i < 3; i++) |
我們希望看到的是每次都能拿到一個新物件,結果發現只有第一次能成功。原因出在當Obj被摧毀的時候,沒有順便清理掉ObjFactory 當中的lookup_的資料,以致於下一次使用的時候,可以找到上一次殘留的屍體,promtion之後就是一個空的shared_ptr。
解決方法就是使用shared_ptr時,同時自訂一個destructor,除了釋放memory之外,也把map裡面的資料輕空。
1 | class ObjFactory { |
現在看起來正常了,不過街下來這個Case又會遇到問題。
1 | { |
由於在這個block之內,pFactory已經被釋放了,所以那個destructor的this一點都不可靠,因此要把裡面那個this
轉成一個shared_ptr。
enable_shared_from_this
enable_shared_from_this就是因此登場的,他可以把this指標所在的位置轉成一個shared_ptr。
而原先的測試部份也必須用shared_ptr管理了。
1 | class ObjFactory : public enable_shared_from_this<ObjFactory> { |
Misc
使用shared_ptr會延長ObjectFactory的LifeCycle,如果pFactory已經不在的話,跟本連清理的動作都不用作。
所以程式可以寫成
1 | class ObjFactory : public enable_shared_from_this<ObjFactory> { |
結論
有本好書真的很重要啊,以前我只會用最粗淺的shared_ptr,對於wear_ptr跟其他特性玩全部熟。有了範例之後至少有個基本認識。
至於程式寫得這麼複雜到令人髮指也是不太好,Garbage collection很大部份可以紓解Programmer的負擔。
yield semantics in C/C++
其他語言已經有yield的語意了,不過C/C++必須手動模擬。
看了Coroutines in C之後,做了一下實驗。
1 | int func() |
測試VC12、GCC和Clang之後,發現GCC要使用C99
模式編譯才會成功。
不過美增加一個狀態就得增加一個LABEL,也是蠻麻煩的一件事,後來看到一個作法更好,之前從沒想過能這樣用。
1 | int func() |
從沒想過switch/case的statement可以這樣用,開了眼界了。
C++11 range-based for loops for custom container
在C++11中,可以這樣用已經不是什麼新鮮事了。
1 | vector<int> vec; |
如果要在自己的container支援這特性的話,需滿足以下條件。
- Container必須擁有
begin
、end
函數,這兩個函數必須回傳一個 Iterator 。 - Iterator必須擁有
*
、++ (prefix版)
、!=
這三個operator function。
以下範例是從C++11 range-based for loops修改而來,加上自己的實驗。在gcc跟clang都能正常運作,不過到了VC12 Debug Mode就編譯失敗了。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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105#include <iostream>
#include <algorithm>
using namespace std;
class IntVector;
class ConstIter
{
public:
ConstIter(const IntVector* p_vec, int pos)
: _pos(pos)
, _p_vec(p_vec)
{ }
bool operator!= (const ConstIter& other) const
{
return _pos != other._pos;
}
int operator* () const;
const ConstIter& operator++ ()
{
++_pos;
return *this;
}
private:
int _pos;
const IntVector *_p_vec;
};
class Iter
{
public:
Iter(IntVector* p_vec, int pos) : _pos(pos) , _p_vec(p_vec) { }
bool operator!= (const Iter& other) const
{
return _pos != other._pos;
}
int& operator* ();
Iter& operator++ ()
{
++_pos;
return *this;
}
private:
int _pos;
IntVector *_p_vec;
};
class IntVector
{
public:
int get(int col) const { return _data[col]; }
int& get(int col) { return _data[col]; }
IntVector() {}
Iter begin()
{
return Iter(this, 0);
}
Iter end()
{
return Iter(this, 100);
}
ConstIter begin() const
{
return ConstIter(this, 0);
}
ConstIter end() const
{
return ConstIter(this, 100);
}
void set(int index, int val)
{
_data[index] = val;
}
private:
int _data[100];
};
int
ConstIter::operator* () const
{
return _p_vec->get(_pos);
}
int&
Iter::operator* ()
{
return _p_vec->get(_pos);
}
int main()
{
IntVector v;
for (int i = 0; i < 100; i++)
v.set(i, i);
transform(v.begin(), v.end(), v.begin(), [](int v) { return v * 2; });
for (int& i : v) { i *= 2; }
for (const int& i : v) { cout << i << endl; }
}
這是由於Checked Iterator這巷特性。
最快的解決方案是在前面加上1
2
3
4
5
6#ifndef _ITERATOR_DEBUG_LEVEL
#define _ITERATOR_DEBUG_LEVEL 0
#else
#undef _ITERATOR_DEBUG_LEVEL
#define _ITERATOR_DEBUG_LEVEL 0
#endif
Grand Central Dispatch (libdispatch)
在整理Concurrency programming資料的時候,發現這個部份被我遺漏了,寫點東西免得忘記。
libdispatch 是由蘋果開發的Concurrency framework,如今也可以在FreeBSD上使用。
從FreeBSD Wiki上找來的範例
1 | #include <dispatch/dispatch.h> |
看到那個 ^{ .... }
區塊的部份就類似於其他語言的Closure,C++11的lambda expression。
至於要編譯這段程式碼,就需要
1 | # clang -Wall -Werror -fblocks -L/usr/local/lib -I/usr/local/include -o test test.c -ldispatch |
Blocks是Clang的Extension,更多資訊可以參考Programming with C Blocks,GCC不支援,至於libdispatch需要在ports下事先安裝。編譯的時候要記得加上-fblockss
。
當然,也可以有無Blocks的版本。
1 | #include <dispatch/dispatch.h> |
編譯的時候就可以拿掉-fblocks
了
1 | # clang -Wall -Werror -I/usr/local/include -L/usr/local/lib -o test2 test2.c -ldispatch |
除了Cuncurrency之外,Closure的觀念也在很多程式語言開枝散葉了。
這個pdf有對libdispatch作個簡單的介紹。
在各語言下都有類似libdispatch這樣的Framework
- Java: Hawtdispatch
- Scala: Akka
- .Ne: TPL Dataflow
FreeBSD 10.0 Release
雖然大部分時間都在Windows/Linux底下打轉,FreeBSD被我晾在一旁(時間不夠用Orz)
不過趁著FreeBSD 10.0 Release。
把自己的9.1-Release升級到10.0-Releae,並把過程記錄下來。
1 | # freebsd-update upgrade -r 10.0-RELEASE |
安裝完10.0之後,發現gcc不存在於,全部用clang取代掉了。
另外就是使用 pkgng 來當做新的Package manager,用法就類似於 apt 或 yum。
1 | # pkg update |