看看以下程式有什麼不一樣
1 |
|
和
1 | template <typename T> |
差異就在於auto和decltype的用途不太一樣,auto會去掉reference,而decltype(auto)不會
C++11’s solution
C++11也可以達到decltype(auto)的方式,不過寫法比較繁瑣
1 | template <typename T> |
看看以下程式有什麼不一樣
1 | #include <iostream> |
和
1 | template <typename T> |
差異就在於auto和decltype的用途不太一樣,auto會去掉reference,而decltype(auto)不會
C++11也可以達到decltype(auto)的方式,不過寫法比較繁瑣
1 | template <typename T> |
cgroup是linux用來限制program使用Computer resource的一種方法
也是Docker的基礎
首先先安裝cgroup
1 | $ sudo apt install cgroup-bin |
不用看也知道他絕對吃滿cpu resource
所以該怎麼限制,例如只讓他吃20%的CPU
先建立cgroup的群組
1 | $ cd /sys/fs/cgroup/cpu # 管理CPU資源的地方 |
接著把我們程式限制的規則加入群組 以下動作需要root權限,sudo無法執行
1 | $ echo 20000 > calm/cpu.cfs_quota_us # 預設值是100000,20000正好是20% |
接著我們就能看到程式CPU使用率就只剩20%了
也可以用自定義規則的方式
1 | $ sudo cgcreate -g cpu:calm # 一樣是建立 cpu calm |
如果要執行程式的話
1 | $ sudo cgexec -g cpu:calm python busy.py |
跟上面有同樣的效果
之前大概有寫過C++11 error_code的文章,不過覺得不夠清楚
這篇當作補充
為了示範,僅定義幾個錯誤
1 | #include <system_error> |
定義Error的Domain,需要繼承自std::error_category
,範例如下
1 | class MyErrCategory : std::error_category { |
定義完Error Class和Category Class之後,就可以寫出make_error_code
1 | std::error_code make_error_code(MyErrC e) |
不過就算這樣還是有點麻煩,我們希望寫出這樣的程式碼
1 | std:error_code err = MyErrC::FileNotFound; |
因此需要一個helper structure
1 | namespace std |
因此上面那行程式碼就能通過編譯了
自從C++11有了auto和decltype之後,整個coding style有了很大的改變
現在我們有一個字串統計的map,該怎麼走訪這個map才好
1 | map<string, int> wordCount; |
沒別招了
1 | for (map<string, int>::iterator it = wordCount.begin(); |
雖然可以用typename簡化map<string, int>::iterator
不過大同小異,看起來也不怎麼美觀
auto被賦予了新生命,於是可以寫出這樣的程式碼
1 | for (auto &p : wordCount) { |
C++17引進了structure binding進一步簡化
1 | for (auto &[word, count] : wordCount) { |
不管從哪個角度看,切割字串都不是件簡單的工作
即便是簡單的字串格式
先來個C語言處理方式
1 | #include <stdio.h> |
C++的版本
1 | #include <sstream> |
看起來相差無幾,也沒需要動用到其他武器的地方
不過如果情況更複雜該怎麼辦
例如輸入的字串是Tom: 42
如果什麼都不改的話,name會得到Tom:
,不是我們想要的
如果改C語言的版本Parse string的字串
1 | sscanf(str, "%s: %d", name, &value); |
結果也不是我們想要的
看看C++的版本,不改的話結果一樣
如果改成這樣
1 | int value; |
很顯然地也不對
如果不動用重型武器,這是我唯一想得出來的方法
1 | #include <stdio.h> |
雖然解決了問題,不過程式碼支離破碎,維護起來也是麻煩透頂
萬一字串變成 Tom: 42, 123
該怎麼辦
這世上不缺聰明人,想到了優雅到爆炸的作法
在C++內嵌DSL Parser解決問題,而使用的就是Boost Spirit
不過要說缺點的話,出錯Debug很麻煩
1 | #include <string> |
就如同上面說的如果要修改字串成Tom: 42, 123
我們可以將Parser寫成
1 | bool r = qi::phrase_parse(iter, end, |
高明的不得了
當要Parse的字串越來越複雜,就可以自己定義Grammer和Rule了
1 | #include <string> |
經過測試,如果省略後面Skipper
的模板定義,整個Grammer不會正常運作
– Home of The Boost.Spirit Library
– Parsing with Spirit Qi
– spirit.Qi in boost
最近都在猛K機器學習, 有點荒廢了寫作
終於有時間可以寫點東西, 常常有在網路上看程式碼的需求,
於是就有新的工具誕生了
1 | $ sudo apt install clang llvm libclang-dev |
1 | $ git clone https://github.com/basiliscos/cpp-bredis |
值得注意的是, 這邊用cmake的out of build不起作用
所以就只能在root directory放在一起
之後就可以直接用Browser來看程式碼了
忽略其他 3rd-party的部分, 編譯一個極簡版的caffe2
為了方便, 把步驟記錄起來
– CMake
– Python3
– Numpy
1 | git clone --recursive https://github.com/caffe2/caffe2.git |
接著進入Visual studio native tools command prompt進行編譯過程
1 | scripts\build_host_protoc.bat |
warning都可以忽略, 可以產生可用的protoc
1 | scripts\build_windows.bat |
這樣就能產生caffe2 library了, 也可以打開caffe.sln來看
其實最麻煩的反而是’Dependecny`那塊, 需要手動安裝
目前的Caffe2只支援CPU Mode
雖然Concept還沒正式列入C++ Standard中,不過提早經歷過總是好事..
Concept像Interface,定義一個靜態Concept該有些什麼東西
雖然可以用Interface完成,不過有時候我們不需要Runtime Polymorphism
常見的做法是用template來做
1 | #include <iostream> |
這方法最大的問題就是error message很難看懂, 常常跟Template打交道就有感覺了
1 | a.cpp: In instantiation of ‘void print(T) [with T = int]’: |
如果用Concept表達的話會是這個樣子
1 | #include <stdio.h> |
1 | a.cpp: In function ‘int main()’: |
面的Error告訴我們123不如的類型是int, 而int不符合 Printable這個Concept的要求
Concept只是Template的一層Wrapper而已,所以這樣的程式碼也是能通過的
1 | emplate <class T> |
明明Printable中沒定義empty的觀念,不過程式還是編譯通過,這個時候只能多加留意了
雖然C++11之後沒對Pointer做任何加強,不過也沒縮減他的能力
Modern C++ 不鼓勵直接使用Raw Pointer,用了一堆Toolkit做取代方案
來分析一下Raw Pointer有哪些問題以及怎麼做比較好
Raw Pointer最大的問題是語義不夠強
拿以下兩個例子來說
1 | int* produce(); |
dosomething
傳回的指標需要釋放嘛?這問題除了查看文件或是看Sourece Code外別無他法。因此很容易誤用
同樣的問題,consume
參數的Pointer需要在函數中釋放嘛?假設consume釋放了記憶體,不過caller傳進來的的參數不是透過allocated拿到的(stack array or something),然後城市就掛掉了
從這兩個範例來看,你不能從函數宣告知道指標該怎麼處理
其他的Memory Leak等問題就不詳述了,以下是我對Raw Pointer和Modern C++的一些見解
Reference不是什麼新東西,C++98就有了,不過這也是有效減少Pointer issue的方式之一
Reference和Pointer的差異就不詳述了,上面兩個範例可以用Reference表示
1 | int produce(); |
這樣一看,對於原先的版本,關於記憶體該誰釋放這點就很清楚了
萬一原先的函數是要回傳一個array,而非單一元素,同樣在函數宣告無法很好的表達出來
不過用vector就知道我需要回傳一個vector
1 | std::vector<int> produce(); |
假設我們要處理的是一個char array
1 | char* produce(); |
如果用std::string可以更好的表達語義
1 | std::string produce(); |
由於頻繁的Memory allcation/deallcation會造成不小的開銷
假設你餵給consume的參數是一個const char pointer,會做以下的事情
1 | std::string& produce(); |
1 | std::string& obj = produce(); // (O) |
string_view
列入STL,類似的實作已經出現在各大Library了1 | std::string_view produce(); |
1 | void doSomething() { |
1 | void doSomething() { |
這是另外一個跟指標有關, 不過跟上面不太相同的問題
假設我們現在有個需求
1 | int* findArray(int *arr, int size, int v) |
std::optional
之後,語義有所提昇1 | std::optional<int&> findArray(std::vector<int> &arr, int v) |
雖然Raw Pointer威力強大,無所不能,但未了減少失控。做些房物措施無可厚非
用些Modern C++的技巧可以少犯不少錯誤,如果真的需要最佳化的時候,在把這些拿掉蛻化成Raw Pointer也不遲
先講究不傷身體,在講究效果..
前陣子沉迷於大唐雙龍傳,所以耍廢了一陣子,來紀錄一下新學到的觀念
簡單直接的作法,定義error,然後把ReturnCode當作ErrorCode回傳
1 | enum errors |
不過這邊有個小問題,當兩個不同的Componet有相同的ErrorCode怎麼辦,假設LibA和LibB都有NOTFOUND的定義
通常的作法就是改名,然後用類似LibA_NOTFOUND
和LibB_NOTFOUND
來繞過
當然是用Exception來表示Error
1 | int openFile(const char *filename) { |
遮方式當然也有缺點,需要考慮Runtime overhead,有些Coding Style不鼓勵用Exception(例如Google)
C++11從Boost引進了error_code的觀念
1 | int openFile(const char *filename, std::error_code &ec) { |
這邊作法類似於C語言的作法,不過不同的是引進了Category的觀念
於是可以自定義一個Category和自己的Error_code,解決了Conflict的問題
不過如果需要的話,當然也可以當Exception丟出去
1 | int fd = openFile(filePath, ec); |
當你需要時才把Exception丟出
如果要對Error做條件處理該怎麼做,野引進了error_condtion的觀念
1 | int fd = openFile(filePath, ec); |
從Stackoverflow看到如何自定義ErrorCode和Category的方式,記錄下來
1 | #include <iostream> |