1 | template <typename T, std::size_t N> |
Use gdb to debuuging golang program
好久沒寫文章了,把之前看到的資料記一記。馬有失蹄,人有錯手,在怎麼宣告不用Debugger的Programming language,可能也需要gdb搭配使用,這邊紀錄一下如何用gdb來幫助golan除錯。
範例程式
1 | package main |
編譯時間入debuu info
1 | $ go build -gcflags "-N -l" test.go |
積著就可以照
1 | $ gdb test |
之後就能夠像之前一樣使用gdb了
Reference
– Debugging Go Code with GDB
– [Debugging Go (golang) programs with gdb ]http://thornydev.blogspot.tw/2014/01/debugging-go-golang-programs-with-gdb.html)
– Introduction to Go Debugging with GDB
– Using the gdb debugger with Go
Thought about macros
Macro這東西很早就有了,不過各有巧妙不同。
在Assembler的年代就已經有Macro了
1 | COPY macro Dest, Source |
而在C語言的時候,Macro的被使用了更頻繁了
1 | #define min(a, b) ((a < b) ? a : b) |
C語言的Macro也很簡單,就只是作文字的代換而已。Side Effect除外,出錯的話也很難Debug,因此其他語言打算因此作改進。
Define group of functions
1 | #define CK_PR_FENCE(T, I) \ |
在這情況下 Templateˊ無能為力.
Rust
Macro overload
1 | macro_rules! sayHello { |
Pattern Matching
1 | macro_rules! min { |
Recursive Macro
1 | macro_rules! write_html { |
Nim
Reference
– Macros - Rust Documentation
– A Quick Intro to Rust Macros
– Module macros - Nim
– Nim Manual
Build freebsd kernel
雖然這些東西網路上都找得到,不過為了節省時間,把相關步驟一次記下來。
UsersFetchingSource
有些時刻/usr/src
是空的,所以需要手動獲取程式碼
1 | $ pkg install svnup |
The Configuration File
修改自己需要的Configuration File
1 | $ cd /usr/src/sys/amd64/conf |
Building and Installing a Custom Kernel
1 | $ cd /usr/src |
Reference:
Install Hadoop 2.7 on Ubuntu 14.04
Create Hadoop Account
1 | $ sudo useradd -m hadoop -s /bin/bash |
Setup ssh environemtn
1 | $ sudo apt-get install openssh-server |
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 | $ sudo tar -zxvf ./hadoop-2.7.0.tar.gz -C /usr/local |
單機Hadoop建立
執行測試程, ˊ執行時記得不能有output`目錄存在
1 | $ sudo mkdir input |
偽分布式Hadoop建立
修改/usr/local/hadoop/etc/hadoop/core-site.xml
1 | <configuration> |
修改/usr/local/hadoop/etc/hadoop/hdfs-site.xml
1 | <configuration> |
###請動Hadoop
進行Namenode格式化
1 | $ bin/hadoop namenode -format |
啟動Guardian process
1 | s bin/start-dfs.sh |
可以連到 http://localhost:50070 來觀察adoop狀態
俄式偽分布式Hadoop
1 | $ bin/hdfs dfs -mkdir -p /user/hadoop |
Reference
How to pass exception between threads in C++11
長知識了,直接看Code比較快
1 | int calc(std::exception_ptr& eptr) |
如果要在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 | void calc(std::promise<int> && p) |
The story about return value and error handling
這故事說起來還真不簡單,在寫Code的時候,往往與這兩樣東西牽扯不清。
例如以下這段Code
1 | FILE *fp = fopen( "input.txt", "r"); |
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 | int _errno; |
這作法不難懂,不過有幾個很重大的缺點
– 需要對Return Value定義一個特蘇的值,代表這個值是無效的,而這個Return Value有可能跟值域的某個數值碰撞。例如上例的f(-50)
,你不知道他是錯誤還是有效值。
– global variable表示任何人都有機會更動到,因此在Code的Maintain跟Protect上難很多
– Error很容易被忽略,因為沒有強制性,可能在實做內部,或是外部使用,都有可能因為Code的修改而跳過Error Code處理
因此有了改良版的方法
Return Error and Value simultaneously
上面方法的改良版
1 | int g(int x, int *retvalue) { |
這邊的寫法類似於COM的作法,把return value放在最後一個參數,而errorcode當作return值傳回。
也可以像golang那樣傳回多個回傳質的方法,大同小異
1 | f, err := os.Open("filename.ext") |
這方是解決了上述錢兩個問題,不過還是無法解決第三個問題
Exception-Based solution
越來越多程式語言都加入當標準配備了。
1 | int g(int x) { |
比起上述兩種,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 | enum Result { |
座個事情很簡單,如果Result是有效的值就繼續往下做,不然就直接往後傳。
Enhance Monad solution for Railway Oriented Programming
網路上有很多Monad的介紹,於是東施校憑寫了一個山寨版的。關鍵在於所謂的bind
函數,這裡就不多介紹了。
1 | trait Monad { |
Reference
– C++ Exceptions: Pros and Cons
– http://fsharpforfunandprofit.com/rop/
Type traits in C++11
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提了一種用法這段Code能動,不過會多出複製物件的成本1
2
3
4
5template <class T1, class T2>
inline pair<T1,T2> make_pair(T1 x, T2 y)
{
return pair<T1,T2>(x, y);
}如果我們改用Refernece的傳法重寫的話1
std::pair<std::string, int> p = make_pair("foo", 0);
以上麵這個範例的話,T1會被推導成1
2
3
4
5template <class T1, class T2>
inline pair<T1,T2> make_pair(T1&& x, T2&& y)
{
return pair<T1,T2>(x, y);
}const char[4]
,而不是const char *
。因此我們需要Decay
正確的寫法類似這樣1
2
3
4
5
6
7
8template <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範例
為了decay推導出primitive type,將變化包裝起來不讓外界知道。1
2
3
4
5
6
7
8template<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);
}
std::result_of
看了Stackoverflow,覺得這東西可以完全用decltype去代。有興趣的話可以參考CppReference的用法。
std::enable_if
SFINAE
Some thought about C/C++
前一陣子在做C++轉C Code的動作,真是麻煩的苦工。 在看到Modern C++ in embedded systems – Part 1: Myth and Reality還真是心有慼慼焉啊。C++能做的,C當然也可以,不過程式可讀性差很多,以下列出幾點。
Function overload
1 | void Dosomething(int v); |
這樣的Code在C++可行,在C會編譯失敗,因此要自行加上suffix錯開
1 | void DosomethingWithInt(int v); |
雖然效果是一樣的,不過人的記憶力是有限的,同時兩個淚名稱不同,功能相似的函式,有事沒事要分心注意,會殺死一堆腦細胞。
在C11之後,終於有了Generic Selections
可用了。
1 | define Dosomething(X) _Generic((X), \ |
這下看起來好多了,不過如果每增加一種寒士,就要修改一次Dosomething
的定義。還是很麻煩啊。這也無法解決所有問題。
Constructor & Destructor
在C++當中是
1 | struct Obj { |
同樣的Code在C與研究寫得支離破碎了
1 | void init_Obj(Obj *this, int v) |
由於C不會主動去呼叫Destructor,因此要將計算的值存起來,所以要主動呼叫Destructor。
由於我們不能簡單的把Constructor和Destructor定義成init
和uninit
就好,原因同上面的Function overload,只好加上自行的suffix避開這個問題。
Member function
雖然這兩樣寫差異無幾
1 | Obj *obj; |
萬一有個cstruct 也有Dosomething的函式怎麼辦
1 | Obj2 *obj2; |
又回到老問題了,不能function overload就要手動繞過很多眉角。
Continuation, Calll/cc and CPS
又是一個很雜的題目。
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,則它就是可以被刪除的。