Qt5 的 View Model 學習

筆記一下 Qt5 的 View Model 幾個關鍵

Standard widgets 把 data 存在 widget 裡.
例如:QListWidget, QTableWidget, QTreeWidget.

View classes 是使用外部的 data (the model)
例如:QListView, QTableView, QTreeView.

單一數據存取

1
2
3
4
5
// 存
Item->setData(itemValue, Qt::UserRole); // 單一存取

// 取
ItemValue itemValue = (ItemValue)(index.data(Qt::UserRole).toInt());

自定義結構數據存取

定義數據類型

1
2
3
4
5
6
struct ItemData {
QString name;
QString id;
};

Q_DECLARE_METATYPE(ItemData)

1
2
3
4
5
6
// 存
Item->setData(QVariant::fromValue(itemData), Qt::UserRole+1); // 整體存取

// 取得
QVariant variant = index.data(Qt::UserRole+1);
ItemData itemData = variant.value<ItemData>();

參考
Qt5 Model/View Tutorial
Qt5 Model/View Programming
Qt 學習之路 2(41):model/view 架構| 上篇的部份中文翻譯
QT中的View Model模型| 上上篇的完整中文翻譯
Qt Model/View编程介绍| 上上上篇的完整中文翻譯 排版比較好
QT开发(三十六)——Model/View框架
Qt-Model/View原理与编程方法
Qt之QListView使用

其它相關文章推薦
[Qt] 讀檔,讀取 txt 文字檔
[Qt] 寫檔,寫入 txt 文字檔
安裝 Qt 在 Windows 7 (使用MSVC)
Qt產生的exe發布方式
Qt 新增多國語言.ts翻譯檔案
Qt5的中文亂碼問題如何解決

怎麼查詢 OpenCV 的版本

本篇將介紹如何查詢/查看自己電腦裡已安裝的 OpenCV 版本,
以下將列出兩種 OpenCV 版本查詢方式:

方法一:從 opencv source code 原始碼查看版本

目前官網 opencv 最新的版號是 4.5.0-pre,
https://github.com/opencv/opencv/blob/master/modules/core/include/opencv2/core/version.hpp
寫這篇文章時官網 opencv 的版號是 4.1.0-dev,
https://github.com/opencv/opencv/blob/64168fc20aa8a914cb5529f90ffac309854563b1/modules/core/include/opencv2/core/version.hpp
可以查看自己本機電腦裡當初載好的 OpenCV 原始碼是那一版的,
已我手中剛載好的原始碼為例,版本是 4.1.0-dev

modules/core/include/opencv2/core/version.hpp
1
2
3
4
5
6
7
8
...
#define CV_VERSION_MAJOR 4
#define CV_VERSION_MINOR 1
#define CV_VERSION_REVISION 0
#define CV_VERSION_STATUS "-dev"
...
#define CV_VERSION CVAUX_STR(CV_VERSION_MAJOR) "." CVAUX_STR(CV_VERSION_MINOR) "." CVAUX_STR(CV_VERSION_REVISION) CV_VERSION_STATUS
...

方法二:用 pkg-config 指令查看版本

只要你的 Linux 安裝好 OpenCV 就可以透過 pkg-config 指令查詢 OpenCV 的版本,
這是我之前裝的 3.4.8

1
2
$ pkg-config --modversion opencv
3.4.8

方法三:查看已安裝到系統的 header 檔

這方式是查看已安裝到系統的 version.hpp header 檔,檔案內容基本上跟方法一是一樣的,檔案路徑如下:

/usr/local/include/opencv2/core/version.hpp
1
2
3
4
5
6
7
8
...
#define CV_VERSION_MAJOR 4
#define CV_VERSION_MINOR 1
#define CV_VERSION_REVISION 0
#define CV_VERSION_STATUS "-dev"
...
#define CV_VERSION CVAUX_STR(CV_VERSION_MAJOR) "." CVAUX_STR(CV_VERSION_MINOR) "." CVAUX_STR(CV_VERSION_REVISION) CV_VERSION_STATUS
...

方法四:用 opencv_version 指令查看版本

使用 opencv_version 指令查詢已安裝好的 OpenCV 版本,
這是我之前裝的 3.4.8

1
2
$ opencv_version
3.4.8

其它相關文章推薦
OpenCV FileStorage 用法與 YAML 檔案讀取寫入範例
如何看OpenCV當初編譯的編譯參設定
OpenCV trace VideoCapture 流程

C++ 取得系統當前時間

本篇 ShengYu 將介紹如何使用 C++ 取得系統當前時間,
取得系統當前時間有幾種,本篇介紹使用 C++11 的 chrono 的寫法來取得系統當前時間。

範例1: 取得系統當下時間並轉成字串

使用時,需要先宣告引用 chrono

1
2
3
4
5
6
7
8
9
10
11
12
#include <chrono>
...
std::string getCurrentSystemTime() {
auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
struct tm* ptm = localtime(&tt);
char date[60] = { 0 };
sprintf(date, "%d-%02d-%02d-%02d.%02d.%02d",
(int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
(int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec);

return std::string(date);
}

完整範例下載

使用 g++ 編譯 g++ main.cpp -std=c++11
輸出結果如下:

1
2019-05-17-20.56.58

範例2: 取得從 epoch 到現在經過多少微秒 milliseconds

1
2
unsigned long milliseconds_since_epoch =
std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);

或者

1
2
unsigned long milliseconds_since_epoch = 
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

參考
[1] c++ - Get time since epoch in milliseconds, preferably using C++11 chrono - Stack Overflow
https://stackoverflow.com/questions/16177295/get-time-since-epoch-in-milliseconds-preferably-using-c11-chrono

相關文章
C/C++ 新手入門教學懶人包
C++ 計算程式執行時間
std::deque 用法與範例
std::queue 用法與範例
std::vector 用法與範例
std::thread 怎麼實作的?
Python 取得系統當前時間

Linux rsync 同步最近修改的檔案/遠端同步資料檔案用法與範例

本篇介紹 Linux rsync 指令來同步最近修改的檔案到遠端機器去,
以下指令會先找出最近60天修改的檔案並同步到遠端機器上,

1
rsync `find . -name "*.pdf" -mtime -60` username@hostname:/tmp/

參考
https://www.linuxquestions.org/questions/linux-server-73/rsync-by-date-785692/

其它相關文章推薦
Linux 常用指令教學懶人包
Linux sed 字串取代用法與範例
Linux find 尋找檔案/尋找資料夾用法與範例
Linux cut 字串處理用法與範例
Linux tail 持續監看檔案輸出用法與範例
Linux grep/ack/ag 搜尋字串用法與範例
Linux tee 同時螢幕標準輸出和輸出到檔案用法與範例
Linux xargs 參數列表轉換用法與範例
Linux du 查詢硬碟剩餘空間/資料夾容量用法與範例
Linux wget 下載檔案用法與範例

C++ 設計模式 - 單例模式 Singleton Pattern

本篇介紹 c++ design pattern 的 singleton 單例模式,singleton 常被用來解決問題,許多時候整個系統只需要有一個的全域類別,這樣方便協調系統整體的行為。

一個類別在整個程式中只有一個實例,並且提供全域的存取,這就是單例模式 singleton pattern。

本文的目錄如下,

  • 什麼是單例?
  • 為什麼要用單例?
  • 餓漢模式 Eager Singleton
  • 懶漢模式 Lazy Singleton
  • 雙重鎖 Double-Checked Locking
  • Meyers Singleton (最簡單好用的懶漢模式,也是常見的實作方式)
  • 如何使用單例?
  • 誰也使用了單例模式 Singleton Pattern ?

那就開始本文吧!

什麼是單例?

單例是一種常用的軟體設計模式 design pattern,使用單例這個類別必須保證只能有一個實例 instance 存在。

為什麼要用單例?

有些物件只需要一個實例 instance,某段程式碼只能初始化一次,例如伺服器程式中通常會把設定檔資訊讀入到一個單例物件,之後使用單例 getInstance 然後就可以在任何地方(其他類別)獲得這些設定資訊,其他的類似應用情形還有使用者登入、資料庫連線、快取、與驅動程式溝通、執行緒池等等。

市面上實作 c++ singleton 有好幾種,這邊只提比較常用以及通用的方式,這邊就要開始講古了,
Singleton 分為兩種類型,分別為

  • 餓漢模式 Eager Singleton
  • 懶漢模式 Lazy Singleton

餓漢模式 Eager Singleton

這邊介紹的是餓漢模式,這版本會在進入 main 之前就將 Singleton 初始化好,原因是因為 static member variable 會被視為全域變數,

cpp-singleton-hungry.cpp
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
// g++ cpp-singleton-hungry.cpp -std=c++11 -pthread
#include <iostream>
#include <thread>
using namespace std;

class Singleton {
public:
static Singleton& getInstance() {
cout << "Singleton getInstance\n";
this_thread::sleep_for(chrono::seconds(1));
return sInstance;
}

private:
Singleton() {
cout << "Singleton constructor\n";
}

static Singleton sInstance;
};

Singleton Singleton::sInstance;

int main() {
cout << "main" << endl;
thread t1([]{
cout << "singleton addr: " << &Singleton::getInstance() << endl;
});
cout << "singleton addr: " << &Singleton::getInstance() << endl;
t1.join();
return 0;
}

輸出如下,這邊故意在 getInstance() 裡 sleep 1 秒來測試多執行緒環境下也是沒問題的,

1
2
3
4
5
6
Singleton constructor
main
Singleton getInstance
Singleton getInstance
singleton addr: 0x6052a9
singleton addr: 0x6052a9

缺點是如果該 Singleton 初始化很耗時的話會佔用到進入 main 前的時間,
優點是在進入 main 之前就將 Singleton 初始化好,所以不會有 thread-safe 執行緒安全問題,

需要注意的是在多個 Singleton 有相依關係的話會產生問題,例如 SingletonA 和 SingletonB 都採用了餓漢模式,SingletonA 初始化時需要 SingletonB,而這兩個 instance 又在不同的編譯單元(cpp檔),那麼這兩個的初始化是不固定順序的,如果 SingletonA 在 SingletonB 之前初始化就會出錯。解決方式避開多個 Singleton 有相依關係的設計,或者採用懶漢模式。

懶漢模式 Lazy Singleton

以下篇幅都要來介紹懶漢模式,懶漢模式意思為要用到時才初始化,

範例如下,這邊 getInstance() 初始化完 instance 後改成回傳 reference 而不是 pointer,因為你總不希望別人不小心把你的 instance 給 free 了吧!而別人是無法 free 一個 reference 的,所以回傳 refernce 是比較好的作法。

cpp-singleton-lazy.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...
class Singleton {
public:
static Singleton& getInstance() {
if (sInstance == nullptr) {
sInstance = new Singleton();
}
return *sInstance;
}

private:
Singleton() {}

static Singleton* sInstance;
};

Singleton* Singleton::sInstance = nullptr;
// ...

跟餓漢模式相比這種方法的好處是直到 getInstance() 被呼叫才會初始化,也稱為延遲初始化 (Lazy Initialization),這在一些初始化時消耗較大的情況有很大優勢。

而這個版本不是 thread-safe 的,如下列範例所示,假如現在有主執行緒和執行緒t1兩個執行緒都通過了 sInstance == nullptr 的判斷,那麼主執行緒和 t1 都會 new Singleton(),那麼就不是單例了。

cpp-singleton-lazy2.cpp
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
// g++ cpp-singleton-lazy2.cpp -std=c++11 -pthread
#include <iostream>
#include <thread>
using namespace std;

class Singleton {
public:
static Singleton& getInstance() {
cout << "Singleton getInstance\n";
if (sInstance == nullptr) {
this_thread::sleep_for(chrono::seconds(1));
sInstance = new Singleton();
}
return *sInstance;
}

private:
Singleton() {
cout << "Singleton constructor\n";
}

static Singleton* sInstance;
};

Singleton* Singleton::sInstance = nullptr;

int main() {
cout << "main" << endl;
thread t1([]{
cout << "singleton addr: " << &Singleton::getInstance() << endl;
});
cout << "singleton addr: " << &Singleton::getInstance() << endl;
t1.join();
return 0;
}

輸出如下,可以看出建構子初始化了兩次,印出來的記憶體位置也不一樣,

1
2
3
4
5
6
7
main
Singleton getInstance
Singleton getInstance
Singleton constructor
singleton addr: 0x138bda0
Singleton constructor
singleton addr: 0x7f3a040008c0

雙重檢測上鎖版 Double-Checked Locking

基於前一個懶漢模式例子,在多執行緒的情況下會有多次初始化實例的情形,所以很簡單的修改方式為加個鎖,就產生下面這樣的簡單上鎖版,

cpp-singleton-lock.cpp
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
// g++ cpp-singleton-lock.cpp -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

class Singleton {
public:
static Singleton& getInstance() {
cout << "Singleton getInstance\n";
lock_guard<std::mutex> lock(sMutex);
if (sInstance == nullptr) {
this_thread::sleep_for(chrono::seconds(1));
sInstance = new Singleton();
}
return *sInstance;
}

private:
Singleton() {
cout << "Singleton constructor\n";
}

static mutex sMutex;
static Singleton* sInstance;
};

mutex Singleton::sMutex;
Singleton* Singleton::sInstance = nullptr;

int main() {
cout << "main" << endl;
thread t1([]{
cout << "singleton addr: " << &Singleton::getInstance() << endl;
});
cout << "singleton addr: " << &Singleton::getInstance() << endl;
t1.join();
return 0;
}

輸出如下,即使在初始化前 sleep 1 秒也能順利的產生一個實體。

1
2
3
4
5
6
main
singleton addr: Singleton getInstance
singleton addr: Singleton getInstance
Singleton constructor
0x7ff47bc026f0
0x7ff47bc026f0

但是這樣的寫法在多執行緒的情況下很頻繁地呼叫 getInstance 會造成 race condition 問題,產生效能低落的情形,因此有了雙重檢測上鎖版 Double-Checked Locking 這個版本的改良。

由 Meyers 的 C++ and the Perils of Double-Checked Locking 這篇 paper 提出 Double-Checked Locking 版本,

cpp-singleton-lock2.cpp
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
// g++ cpp-singleton-lock2.cpp -std=c++11 -pthread
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

class Singleton {
public:
static Singleton& getInstance() {
cout << "Singleton getInstance\n";
if (sInstance == nullptr) {
lock_guard<std::mutex> lock(sMutex);
if (sInstance == nullptr) {
sInstance = new Singleton();
}
}
return *sInstance;
}

private:
Singleton() {
cout << "Singleton constructor\n";
}

static mutex sMutex;
static Singleton* sInstance;
};

mutex Singleton::sMutex;
Singleton* Singleton::sInstance = nullptr;

int main() {
cout << "main" << endl;
thread t1([]{
cout << "singleton addr: " << &Singleton::getInstance() << endl;
});
cout << "singleton addr: " << &Singleton::getInstance() << endl;
t1.join();
return 0;
}

這邊會產生一個問題是當有一個執行緒在執行 sInstance = new Singleton();,有另外一個執行緒在檢查第一個 sInstance == nullptr 時很有可能出現問題,

因為 sInstance = new Singleton(); 這語句會分成三個步驟,
Step 1: Allocate memory to hold a Singleton object. 記憶體配置
Step 2: Construct a Singleton object in the allocated memory. 在己經配置的記憶體上建構 Singleton 物件
Step 3: Make sInstance point to the allocated memory. 將 sInstance 指向配置的記憶體

1
2
3
4
5
6
7
8
9
10
11
static Singleton& getInstance() {
if (sInstance == nullptr) {
lock_guard<std::mutex> lock(sMutex);
if (sInstance == nullptr) {
sInstance // Step 3
= operator new(sizeof(Singleton)); // Step 1
new(sInstance) Singleton; // Step 2
}
}
return *sInstance;
}

這順序很可能會變成 1->3->2,導致在1->3時 sInstance 已經不是 null,另外一個執行緒執行 sInstance == nullptr 時判斷 sInstance 已經有指向到有效的物件,就直接拿來使用,結果就會發生災難~

更多詳細內容與解決方式請看 Meyers 的 C++ and the Perils of Double-Checked Locking 這篇 paper 內容,這邊就不多做介紹。

有鑒於上面的各種坑,個人比較建議接下來介紹的這種方式。

Meyers Singleton (最簡單好用的懶漢模式,也是常見的實作方式)

在 Scott Meyers 大神的《Effective C++》書中條款 4 提出的 local static object 實作方式,也是屬於懶漢模式,利用 local static object 在函式第一次被呼叫使用才初始化的特性,這個作法在 C++11 以後是保證 thread-safe 的,Visual Studio 從 Visual Studio 2015 開始支援,GCC 從 GCC 4.3 開始支援
引用 C++11 standard 的 §6.7.4:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

以下為 Meyers Singleton 的實作方式,

cpp-singleton-meyers.cpp
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
// g++ cpp-singleton-meyers.cpp -std=c++11 -pthread
#include <iostream>
#include <thread>
using namespace std;

class Singleton {
public:
static Singleton& getInstance() {
static Singleton sInstance;
return sInstance;
}

private:
Singleton() {}
};

int main() {
cout << "main" << endl;
thread t1([]{
cout << "singleton addr: " << &Singleton::getInstance() << endl;
});
cout << "singleton addr: " << &Singleton::getInstance() << endl;
t1.join();
return 0;
}

很簡單吧!

如何使用單例?

剛剛前幾節著重在介紹 C++ 的 singleton 幾種作法,這邊要介紹如何使用單例以及實際上在使用時應該需要注意的部份,
在這篇 stackoverflow 討論到 constructor 必須是 private 的,避免被別人呼叫建構實例化,同樣地,copy constructor 複製建構子與 assignment operator 賦值運算子 (operator=) 也是比照辦理,

假設我有一個 Setting class 提供全域的存取,以下為 c++ singleton 單例模式的範例(Meyers Singleton 版本),

cpp-singleton.cpp
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
// g++ cpp-singleton.cpp -std=c++11
#include <iostream>
using namespace std;

class Setting {
public:
static Setting& getInstance() {
static Setting instance;
return instance;
}

Setting(Setting const&) = delete;
// Setting s;
// Setting s2(s); // x

void operator=(Setting const&) = delete;
// Setting s2;
// s2 = s; // x

private:
Setting() {}
// Setting s; // x
};

int main() {
Setting &s = Setting::getInstance();
Setting &s2 = Setting::getInstance();
cout << "setting addr: " << &s << endl;
cout << "setting2 addr: " << &s2 << endl;

return 0;
}

輸出如下,

1
2
3
$ g++ singleton.cpp -std=c++11 && ./a.out 
setting addr: 0x10e6ff110
setting2 addr: 0x10e6ff110

誰也使用了單例模式 Singleton Pattern ?

誰也使用了 Singleton Pattern 單例模式? 我們來看看別人是怎麼使用的吧!
這邊舉個 OpenCV 內部的 VideoBackendRegistry class 為例,
使用 VideoBackendRegistry::getInstance() 來取得實例,所以在整個程式裡 VideoBackendRegistry 這個 class 就只會有一份實例,
https://github.com/opencv/opencv/blob/master/modules/videoio/src/videoio_registry.cpp

modules/videoio/src/videoio_registry.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class VideoBackendRegistry
{
protected:
VideoBackendRegistry()
{
// ...
}
// ...
public:
// ...
static VideoBackendRegistry& getInstance()
{
static VideoBackendRegistry g_instance;
return g_instance;
}
// ...
};

const std::vector<VideoBackendInfo> result = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByIndex();

在 OpenCV highgui 模組裡因應不同平台下有不同的實作方式,
在 Windows 平台下 window_w32.cpp 裡的 getInstance() 也是使用 static local 的方式,只是回傳的是 shared_ptr
https://github.com/opencv/opencv/blob/master/modules/highgui/src/window_w32.cpp

modules/highgui/src/window_w32.cpp
1
2
3
4
5
static std::shared_ptr<Win32BackendUI>& getInstance()
{
static std::shared_ptr<Win32BackendUI> g_instance = std::make_shared<Win32BackendUI>();
return g_instance;
}

AOSP (Android Open Source Project) 裡面 libutils 提供的 Singleton.h class 採用的是單一鎖方式,明顯地這種方式在多執行緒被頻繁呼叫的話會有 race condition 問題,2017 年在程式碼裡改成禁用,並建議改用 scoped static initialization 的方式,也就是本文提到的 Meyers Singleton 實作方式,
https://github.com/aosp-mirror/platform_system_core/blob/34a0e57a257f0081c672c9be0e87230762e677ca/libutils/include/utils/Singleton.h

libutils/include/utils/Singleton.h
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
// DO NOT USE: Please use scoped static initialization. For instance:
// MyClass& getInstance() {
// static MyClass gInstance(...);
// return gInstance;
// }
template <typename TYPE>
class ANDROID_API Singleton
{
public:
static TYPE& getInstance() {
Mutex::Autolock _l(sLock);
TYPE* instance = sInstance;
if (instance == nullptr) {
instance = new TYPE();
sInstance = instance;
}
return *instance;
}
// ...
protected:
~Singleton() { }
Singleton() { }

private:
// ...
static Mutex sLock;
static TYPE* sInstance;
};

知名的 xbmc 專案(現已改名為 Kodi)也是採用 Meyers Singleton 實作方式
xbmc CPlayerCoreFactory::GetInstance()
https://github.com/xbmc/xbmc/blob/f621a026f671ab0f1a91d411f2f452915242bddf/xbmc/cores/playercorefactory/PlayerCoreFactory.cpp

xbmc/cores/playercorefactory/PlayerCoreFactory.cpp
1
2
3
4
5
CPlayerCoreFactory& CPlayerCoreFactory::GetInstance()
{
static CPlayerCoreFactory sPlayerCoreFactory;
return sPlayerCoreFactory;
}

參考
單例模式 - wikipedia
https://zh.wikipedia.org/wiki/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F
[ Day 5 ] 初探設計模式 - 單例模式 (Singleton)
https://ithelp.ithome.com.tw/articles/10203092
Java單例模式——並非看起來那麼簡單
https://blog.csdn.net/goodlixueyong/article/details/51935526
Singleton class and correct way to access it in C++
https://codereview.stackexchange.com/questions/197486/singleton-class-and-correct-way-to-access-it-in-c
So Singletons are bad, then what?
https://softwareengineering.stackexchange.com/questions/40373/so-singletons-are-bad-then-what/40374#40374
It’s important to distinguish here between single instances and the Singleton design pattern.
C++ Singleton design pattern
https://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289
Singleton pattern in C++
https://stackoverflow.com/questions/2496918/singleton-pattern-in-c

其它相關文章推薦
C/C++ 新手入門教學懶人包

正規表達式 Regular Expression

本篇 ShengYu 介紹規則表達式 Regular Expression 來尋找字串或取代字串,學會後便能善用規則表達式來尋找字串跟取代字串。

範例1:目標字串(.+)

給定字串為:This is a book.
要搜尋字串為:This is 開頭,後面配上任意字元的多次組合,
正則表達式為:This is(.+)

.:表示任意字元,除了 \n 之外
+:表示多次批配

範例2:(.+)目標字串(.+)

給定字串為:My name is ShengYu.
要搜尋字串為:任意字元的多次組合配上 name,後面配上任意字元的多次組合,
正則表達式為:(.+)name(.+)

正規表示式的符號意義

表達式 說明
^ 行首
$ 行尾
\s 空白
. 匹配任何單個字元
[0-9]+ 整數
+ 配運算式一次或多次
[A-Za-z]+ 英文單字

其它範例

範例 說明
^\s 匹配行首的空格
\s$ 匹配行末的空格
^cat 行首以cat開頭
^\s[0-9]+\sfiles…+ 行首 空格 數字多個以上 空格 files

其它參考
NotePad++ 正則表示式替換 高階用法 - IT閱讀
https://www.itread01.com/content/1549641605.html
notepad 正則表示式 規則運算式 範例 筆記
http://procedurenotes.blogspot.com/2017/05/notepad.html
正規表示式 Regular Expression
http://ccckmit.wikidot.com/regularexpression

Python 寫藍芽 RFCOMM 通訊程式

以下範例 ShengYu 將會介紹如何使用 Python 來寫 RFCOMM 藍芽通訊程式,內容包含使用 bluetooth socket 建立一個連線,傳輸資料與斷線。

server 端程式

server 端的程式一開始 bind 在 port 1,
listen 指的是最多可以接受多少連線,
接下來 accept,這裡 accept 會 block 不會立即返回,
直到 client 連上才返回,之後接收 client 的資訊並印出來,然後關閉連線。

rfcomm-server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import bluetooth

server_sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )

port = 1
server_sock.bind(("", port))
server_sock.listen(1)

client_sock,address = server_sock.accept()
print "Accepted connection from ",address

data = client_sock.recv(1024)
print "received [%s]" % data

client_sock.close()
server_sock.close()

client 端程式

client 端的程式需將 server 端的 addr 填入 bd_addr 內,才能成功連上,
怎麼查詢藍芽 addr 請看這篇
如下範例, connect 嘗試連上 addr 為 “01:23:45:67:89:AB”, port 為 1 的裝置,
connect 連上 server 後,送出一個 hello!! 就斷線。

rfcomm-client.py
1
2
3
4
5
6
7
8
9
10
11
12
import bluetooth

bd_addr = "01:23:45:67:89:AB" # server 端的 addr

port = 1

sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
sock.connect((bd_addr, port))

sock.send("hello!!")

sock.close()

以上的兩個基本範例就能夠使兩個藍芽裝置進行溝通了!

ShengYu 將會在下一篇介紹 如何用Python的藍芽 L2CAP 協定來通訊

參考
https://people.csail.mit.edu/albert/bluez-intro/x232.html

std::thread 怎麼實作的?

本篇介紹一下一般各個作業系統的 C++ 編譯器是怎麼實作 std::thread 的,
在一番 trace code 的結果後,llvm 的 libc++ 實作是這樣的,
在 unix 平台下開 thread 就是轉換到 pthread 的 pthread_create。
在 windows 平台下開 thread 就是轉換到 win32 的 _beginthreadex。
gcc 的 libstdc++ 實作也是如此。

簡單介紹這兩種 c++ 庫差異:

  • libc++ (LLVM C++ standard library with C++ 11 support)
  • libstdc++ (GNU C++ standard library)

以下是 llvm 下 libc++ 怎麼實作的 code snippet,

1. std::thread::thread 的實作

建構子 thread 轉換成內部的 __libcpp_thread_create

libcxx/include/threadlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template <class _Fp, class ..._Args,
class
>
thread::thread(_Fp&& __f, _Args&&... __args)
{
typedef unique_ptr<__thread_struct> _TSPtr;
_TSPtr __tsp(new __thread_struct);
typedef tuple<_TSPtr, typename decay<_Fp>::type, typename decay<_Args>::type...> _Gp;
_VSTD::unique_ptr<_Gp> __p(
new _Gp(std::move(__tsp),
__decay_copy(_VSTD::forward<_Fp>(__f)),
__decay_copy(_VSTD::forward<_Args>(__args))...));
int __ec = __libcpp_thread_create(&__t_, &__thread_proxy<_Gp>, __p.get());
if (__ec == 0)
__p.release();
else
__throw_system_error(__ec, "thread constructor failed");
}

1-1. std::thread::thread 在 unix 平台的實作內容

__libcpp_thread_create 在 unix 平台是呼叫 pthread_create

libcxx/include/__threading_supportlink
1
2
3
4
5
6
7
typedef pthread_t __libcpp_thread_t;
...
int __libcpp_thread_create(__libcpp_thread_t *__t, void *(*__func)(void *),
void *__arg)
{
return pthread_create(__t, 0, __func, __arg);
}

1-2. std::thread::thread 在 windows 平台的實作內容

__libcpp_thread_create 在 windows 平台是呼叫 _beginthreadex

libcxx/src/support/win32/thread_win32.cpplink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __libcpp_thread_create(__libcpp_thread_t *__t, void *(*__func)(void *),
void *__arg)
{
auto *__data = new __libcpp_beginthreadex_thunk_data;
__data->__func = __func;
__data->__arg = __arg;

*__t = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0,
__libcpp_beginthreadex_thunk,
__data, 0, nullptr));

if (*__t)
return 0;
return GetLastError();
}

2. std::thread::join 的實作

join 轉換成內部的 __libcpp_thread_join

libcxx/src/thread.cpplink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void
thread::join()
{
int ec = EINVAL;
if (!__libcpp_thread_isnull(&__t_))
{
ec = __libcpp_thread_join(&__t_);
if (ec == 0)
__t_ = _LIBCPP_NULL_THREAD;
}

if (ec)
__throw_system_error(ec, "thread::join failed");
}

2-1. std::thread::join 在 unix 平台的實作內容

__libcpp_thread_join 在 unix 平台是呼叫 pthread_join

libcxx/include/__threading_supportlink
1
2
3
4
int __libcpp_thread_join(__libcpp_thread_t *__t)
{
return pthread_join(*__t, 0);
}

2-2. std::thread::join 在 windows 平台的實作內容

__libcpp_thread_join 在 windows 平台是呼叫 WaitForSingleObjectExCloseHandle

libcxx/src/support/win32/thread_win32.cpplink
1
2
3
4
5
6
7
8
int __libcpp_thread_join(__libcpp_thread_t *__t)
{
if (WaitForSingleObjectEx(*__t, INFINITE, FALSE) == WAIT_FAILED)
return GetLastError();
if (!CloseHandle(*__t))
return GetLastError();
return 0;
}

總結

在一番 trace code 的結果後,std::thread 在 llvm 的 libc++ 實作是這樣的,
在 unix 下
std::thread::thread 轉換成 pthread_create
std::thread::join 轉換成 pthread_join
在 windows 下
std::thread::thread 轉換成 _beginthreadex
std::thread::join 轉換成 WaitForSingleObjectExCloseHandle

線上 trace code 工具

介紹一下 Woboq 這個線上 trace code 網頁,可以點擊關鍵字跳至定義處,trace code 起來很方便,減少很多搜尋的時間。
thread::thread()
__libcpp_thread_create()

相關主題
C/C++ 新手入門教學懶人包
std::condition_variable 怎麼實作的?
std::mutex 怎麼實作的?
C++11 在各平台下如何實現?

在 Windows 下編譯安裝 Boost 1.70.0

本篇 ShengYu 將介紹如何在 Windows 下編譯安裝 Boost 1.70.0,
這版 boost 1.70.0 釋出日期是 2019/04/12,開始動手編譯原始碼安裝吧!

以下為我的系統環境:
作業系統:Windows 10
編譯器:Visual Studio 2015 (Version 14)
使用版本:boost 1.70.0 下載處 / 7z / tar.gz

使用指令下載

1
2
3
wget https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz
tar xvf boost_1_70_0.tar.gz
cd boost_1_70_0

Read More

開源專案ScreenCapture-螢幕截圖自動存檔小工具

介紹

ScreenCapture 是一個支援 win7/win10 螢幕截圖並且自動存檔小工具,讓你可以在短時間擷取多張圖片。

故事起源

這是大學時期寫的小工具了,直到工作後才想說拿出來翻修更新一下,也順便開源一下,在一些情況下使用時真的很好用。

特色

  • 截圖時立馬自動存檔,根據當下的時間作為檔案名稱,這樣可以節省時間,可以短時間內按截圖快捷鍵擷取多次截圖。
  • 支援內建的 PrintScreen key,即使 ScreenCapture 沒有在前景執行,照樣可以按下 PrintScreen key 就自動完成了存檔動作。

以上兩大特色功能為我最想要用到的功能,也是很常遇到的狀況。
如果你對我的工具有興趣的話可以透過下面網址瞭解或下載:
專案首頁:https://shengyu7697.github.io/ScreenCapture/

Demo

如果你覺得這工具很讚,可以幫我按個星星,也可以透過贊助我讓我更有動力開發新功能唷!

其它相關文章推薦
開源專案CursorJail-滑鼠鎖定工具
開源專案-2048
開源專案-tetris俄羅斯方塊
開源專案-數獨sudoku
開源專案-金庸群俠傳
開源專案-仙劍奇俠傳