Windows 上 Multibyte 轉 Wide Char 的一些方法

#include <windows.h>
#include <cstdio>
#include <iostream>

#include <atlbase.h> 
#include <atlstr.h> 
#include <atlconv.h>
#include <string>
#include <codecvt>
#include <xlocbuf>
#include <cassert>


void PrintBytes(const wchar_t* wcsCaption, const wchar_t* wcs)
{
    static const int ROW_WIDTH = 16;

    size_t cbBuf = (wcslen(wcs) + 1) * sizeof(wchar_t);
    auto pBuf = (const BYTE*)wcs;
    wprintf_s(L"Buffer(%d) %s \n", cbBuf, wcsCaption);   
    for(size_t i = 0 ; i < cbBuf; i++)
    {
        if(i && !(i % ROW_WIDTH))
        {
            wprintf_s(L"\n");
        }
        wprintf_s(L"%02x  ", pBuf[i]);
    }
    wprintf_s(L"\n\n");
}


// Convert string in current code page/ACP (per system) to windows std::wstring in UCS2
namespace ConvFromMB 
{
    // Win32 API 
 std::wstring imp1(const char* s)
    {
        int cchRequired = ::MultiByteToWideChar(CP_ACP, 0, s, -1, nullptr, 0); // w/ null terminator
     std::wstring w(cchRequired, L'\0');                                    //  (because passed -1 as src length)
     int cchWritten = ::MultiByteToWideChar(CP_ACP, 0, s, -1, &w[0], cchRequired);
        return w;
    }

    // ATL tool, a MultiByteToWideChar wrapper
 std::wstring imp2(const char* s)
    {
        ATL::CA2W w (s, CP_ACP);
        return std::wstring(w);
    }

    // C Style conversion mbstowcs. Windows extends it as secure version and support different locale.
 //  - `mbstowcs_s` is identical to `_mbstowcs_s_l + Global locale`
 std::wstring imp3(const char* s)
    {
        _locale_t loc = ::_create_locale(LC_CTYPE, ".ACP");
        size_t cchRequired = 0;
        errno_t ret = ::_mbstowcs_s_l(&cchRequired, nullptr, 0, s, 0, loc); // w/ null terminator
     std::wstring w(cchRequired, L'\0');
        size_t cchActual = cchRequired;
        ret = ::_mbstowcs_s_l(&cchRequired, &w[0], cchActual, s, cchActual, loc);
        return w;
    }

    // Utilize printf + %hs, useful when print MB string to wide char fmt
 //  - `swprintf_s` is identical to `swprintf_s_l + Global locale`
 std::wstring imp4(const char* s)
    {
        _locale_t loc = ::_create_locale(LC_CTYPE, ".ACP");
        int cchRequired = _scwprintf_l(L"%hs", loc, s) + 1; // w/o null terminator
     std::wstring w(cchRequired, L'\0');
        int cchWritten = _swprintf_s_l(&w[0], cchRequired, L"%hs", loc, s);
        return w;
    }
}

void TestACPConv(const char* s)
{
    // 1
 PrintBytes(L"ACP/MultiByteToWideChar", ConvFromMB::imp1(s).c_str());
    PrintBytes(L"ACP/CA2W", ConvFromMB::imp2(s).c_str());
    PrintBytes(L"ACP/mbstowcs", ConvFromMB::imp3(s).c_str());
    PrintBytes(L"ACP/swprintf", ConvFromMB::imp4(s).c_str());
}

namespace ConvFromUTF8 
{
    // Win32 API
 std::wstring imp1(const char* s)
    {
        int cchRequired = ::MultiByteToWideChar(CP_UTF8, 0, s, -1, nullptr, 0); // w/ null terminator
     std::wstring w(cchRequired, L'\0');                                     // (because passed -1 as src length)
     int cchWritten = ::MultiByteToWideChar(CP_UTF8, 0, s, -1, &w[0], cchRequired);
        return w;
    }

    // ATL tool, a MultiByteToWideChar wrapper
 std::wstring imp2(const char* s)
    {
        ATL::CA2W wActual (s, CP_UTF8);
        return std::wstring(wActual);
    }

    // standard c++ locale string conversion for utf8 <-> USC2/USC4
 std::wstring imp3(const char* s)
    {
        std::wstring_convert< std::codecvt_utf8<wchar_t> > cvt;
        return cvt.from_bytes(s);
    }
}

void TestUTF8Conv(const char* s)
{
    PrintBytes(L"UTF8/MultiByteToWideChar", ConvFromUTF8::imp1(s).c_str());
    PrintBytes(L"UTF8/CA2W", ConvFromUTF8::imp2(s).c_str());
    PrintBytes(L"UTF8/wstring_convert", ConvFromUTF8::imp3(s).c_str());
}



int main(int argc, char* argv[])
{
    const char* helloTW_UTF8 = "\xE4\xBD\xA0\xE5\xA5\xBD\xE5\x97\x8E"; // 你(0xE4BDA0)好(0xE5A5BD)嗎(0xE5978E)
 const char* helloTW_ACP = "你好嗎"; // if file is saved in codepage 950 Big5

    TestUTF8Conv(helloTW_UTF8);
    TestACPConv(helloTW_ACP);

    std::cin.ignore();
    return 0;
}

SOLID 筆記

SRP

The Single Responsibility Principle An active corollary to Conway’s law: The best structure for a software system is heavily influenced by the social structure of the organization that uses it so that each software module has one, and only one, reason to change.

  • 一個 class 只負責一件事。
  • Module 更容易模組化、更容易做單元測試。
  • (實作上的高聚合)

OCP

The Open-Closed Principle Bertrand Meyer made this principle famous in the 1980s. The gist is that for software systems to be easy to change, they must be designed to allow the behavior of those systems to be changed by adding new code, rather than changing existing code.

  • 新增新的行為,不用改 code (Close),只需要加 code 就好 (Open)。
  • (寫 base class/interface 的方法)

LSP

The Liskov Substitution Principle Barbara Liskov’s famous definition of subtypes, from 1988. In short, this principle says that to build software systems from interchangeable parts, those parts must adhere to a contract that allows those parts to be substituted one for another.

  • 就算是要新增功能,也要忠於原有 interface 的約定。
  • 就算 subclass 的實作再怎麼有變化,你也只是在"實作 interface"。
  • (寫 sub class/implemntation 的方法)

ISP

The Interface Segregation Principle This principle advises software designers to avoid depending on things that they don’t use.

  • Interface 的範圍越小越精確越好。
  • (介面上的高聚合)

DIP

The Dependency Inversion Principle The code that implements high-level policy should not depend on the code that implements low-level details. Rather, details should depend on policies.

  • Module 都應該只(向上)相依於介面就好,而不是(向下)相依於實作。
  • 上層要更改 policy (傳入不同實作),也不用改到 Module。
  • 差不多就是封裝。

引言內文來自: Clean Architecture: A Craftsman's Guide to Software Structure and Design

理解看看 compose compose compose / (.) . (.)

- (.) :: (b -> c) -> (a -> b) -> (a -> c)
- fmap :: (a -> b) -> f a -> f b
- instance Functor ((->) r) where
   fmap = (.)
- (.).(.) :: (b -> c) -> (a1 -> a2 -> b) -> (a1 -> a2 -> c)
- fmap . fmap :: (a -> b) -> f (f1 a) -> f (f1 b)

f: 你好,我們是 a1 -> a2 -> b 。

b->c: 我 b -> c 想要改一下你們的 return type 啦。

f: 好啊,請由 fmap 大門進來。我們這裡都是 (-> f1 b) 的 functor,也就是 (-> a2 -> b) 的部門。

b->c: 幹 怎麼辦,不是 -> b ,而是 (-> a2 -> b) 啊

f1: 你傻啦,你都進了 f 的 fmap大門,你再進來一下我 f1 的 fmap 大門不就好了。

b->c: 對吼,對我來說,你 f1 記錄的是 -> a2 的 context 啦,我直接再走一次 fmap,bypass 就好啦。

f1: 對啦。你看,現在你套完我們 f1 的 fmap,f 部門的都變成 (-> f1 c) 或是 (-> a2 -> c) 啦

b->c: 真的NE,從 f 外面看,就已經是 a1 -> a2 -> c,神神的。

(.).(.) 的 fmap . fmap,可以想成連下兩層 a1->r 的 functor 的 fmap。第一次 bypass 掉 吃 a1 的 context,第二次 bypass 掉 吃 a2 的 context,就可以偷修 return type 啦。

Inspired by: http://adit.io/posts/2013-07-22-lenses-in-pictures.html

試著講講看甚麼是 Monad

1.

FP 想要 purely function,不喜歡 side effect。但在現實中就是會有 side effect 的存在。FP 建立了一種會員制的 Club,任何有遇到 side effect 的計算,沒關係。我們讓當中 pure 的成分,提升到 Club。而其他有 side effect 的成份,先讓圍事讓他們在 Club 外面排隊等。至少我們在 Club 內部,可以宣稱全部是 pure 的。

這種會產生 side effect 而要建立 Club 機制的計算,我們先把它標記成 C++ 語言的 function template 形式
 

template<typename InputT, typename ReturnT>
Club<ReturnT> func1(InputT arg);

 
我們把登記會員這個動作叫做 lift,提升資格的意思。另一種函式形式的叫法是叫 pure(x),代表把普通人 x 標記 成 Club 內的 pure x

2.

當你想繼續算下去時,發現其他寫好的計算過程,其實不知道所謂的 Club 機制,而是很單純處理一般資料。像是下面:

template<typename InputT, typename ReturnT>
ReturnT func2(InputT arg);

那麼你從 func1 算出來的 Club<ReturnT>func2 是吃 InputT 而不是 Club<InputT> 的,那要怎麼套用 func2 呢?這個時候有些 Club 比較厲害,有 警衛室 可以 代收這種計算過程,幫你跟裡面的人講,大家把自己代一代,再把結果代為傳出來。

template<typename InputT, typename ReturnT>
Club<ReturnT> GuardOffice(Club<InputT> clubedData, std::function<ReturnT(InputT)> func2);

有這樣設立警衛室的 Club 很棒,我們會叫這種代收運算過程的 Club 為 Functor。而警衛室的功能呢,用一個比較廣義的詞,叫 fmap ,代表我幫你 計算過程,來去 map (把自己代一代) Club 裡面的會員。 

3.

有了警衛室的功能後,你會想, 那這樣我不就可以請警衛幫我代收是一堆計算過程,像是 

template<typename InputT, typename ReturnT> 
Club<ReturnT> func3(InputT arg);
template<typename InputT, typename ReturnT> 
Club<ReturnT> func4(InputT arg);

InputData arg;
Club<Club<Club<InputDataT>>> result = GuardOffice(GuardOffice(GuardOffice(func1(arg), func2), func3), func4)

沒錯!

但是你會發現這些 func3func4,一定要是無 Club 機制的計算過程。否則因為 警衛室 只是單純幫你代為套用,使得上面的結果會變成: 

decltype(result) ==> Club<Club<Club<InputDataT>>>;

Club 中又有 Club,之中又再有 Club。

當初建立會員制的目的,只是想要簡單一層的 Club 來幫我們隔離 side effect。並不需要這麼多層結構來複雜化,我們並不 care。於是在競爭激烈的社會下,更棒的 Club 推出 高等警衛室

不管是在第一層 Club 外面排隊的 side effect ,還是在第二層 Club 外面排隊的 side effect。我們通通都把他趕到最外面那層。我們只想要 Club 內部是純 pure,其他的我們不管。

使得

template<typename InputT, typename ReturnT> 
Club<ReturnT> func3(InputT arg);

template<typename InputT, typename ReturnT> 
Club<ReturnT> func4(InputT arg);

using SideEffectFuncT = decltype(func3);

template<typename InputT, typename ReturnT>
Club<ReturnT> PrimeGuardOffice(Club<InputT>, SideEffectFuncT func);`

升等之後,statement 改成

InputData arg;
Club<ReturnT> result = PrimeGuardOffice(PrimeGuardOffice(GuardOffice (func1(arg), func2), func3), func4)

可以給你好棒棒的 Club<ReturnT> 結果。 

這種推出高等警衛室功能 Club ,我們稱他為 Monad高等警衛室 的功能我會會叫 flatMap,意思表示,在將 function 套用 (/map)在內部會員後,並把他產生出來的 side effect 也趕到外面,跟外面在排隊的 side effect 一起排成只有一個隊伍 (/flat)。
 

4.

有了 Monad Club ,不管是一般的 func2 ,或是有 side effect 的 func3func4,都能以 purely function 的方式,(Composite)串聯起來。那些如何隔離 side effect 的機制,都交由 高等警衛室一般警衛室 來去來處理。

有時候會擔心我們用的 Club,經由 警衛室 轉換過後 Club 內做運算,與在 Club 外做結果,是不是等價不變的

我們會定下 對應關係要符合我們預期 的規則。剛好跟群論裡的談到 Monad 性質一樣,所以才會以他的名字命名。

Python - random 模組用法筆記

從特定區間生成亂數

  • random.random() -> 在 0 <= x < 1.0 範圍中,生成一個 float。 (不含 1.0)
  • random.urandom(0, 100) -> 在 0 <= x <= 100 範圍中,生成一個 float。
  • random.randint(100, 200) -> 從 (100,101,102,...,200) 範圍中生成一個 int。

從序列中選出

給定一個序列 xs = [1.0, 1, 2, "Hello"]

  • random.choice(xs) -> 從 xs 中挑選一個
  • random.choice(range(0, 100, 3)) -> 從 (0,3,6,...,99) 中挑選出一個
  • random.randrange(0, 100, 3) -> 從 (0,3,6,...,99) 中挑選出一個 > 沒有實際的 range object 會被產生。
  • random.randrange(10) -> 從 (0,1,2,...,9) 中挑選出一個
  • random.sample(xs, k)
    • xs 中挑選 k 個且不會被重複挑中。
    • 直接回傳新的序列,不會影響傳入的 xs

序列操作

random.shuffle(xs) -> 將序列亂數排序。會改變 xs 狀態。

狀態初始化

random.seed(seed=None)

  • 預設為 None,會取 system time 當種子。
  • 同樣的種子保證同樣的亂數生成順序。

狀態存檔

  • random.setstate(state)
  • random.getstate()

獨立生成器

random.Random()

  • 全部的 methods 其實由背景的 random.Random() 的全域物件所提供。
  • 如果想要不想共享狀態,可以定義一個自有的 random.Random(),當成獨立的生成器。

加密安全的使用

以上所有亂數生成 methods 請用在加密安全上。
請改用 os.urandom() 或是 random.SystemRandom 這兩種 API,內部會呼叫由系統提供更高安全性的亂數產生器。去得到無法重現的亂數序列,來提高安全層級。

Reference: https://docs.python.org/3/library/random.html

Python -  看到一個 grouper 的寫法

在 Stack Overflow 看到 What is the most “pythonic” way to iterate over a list in chunks? 看到這段用法,實在驚為天人,覺得真是優雅(又看不太懂XD)。知道他是融合了 zip, iterator, list repeatparameter unpack 這四種用法,而且也是 lazily 取值的方式在效能上也不浪費。

import itertools
def grouper(iterable, n):
    return itertools.zip_longest(*[iter(iterable)] * n)
簡單範例
grouper([0,1,2,3,4,5,6,7,8], 3) 
 ->
[[0,1,2],[3,4,5],[6,7,8]]

以下就簡單記錄一下,為了弄懂這段 Code ,回去復習一下 zip, iterator 的筆記。

zip_longest 是幹麻的

def zip_longest(*iterable, fillvalue=None) 

將多個陣列像拉鍊一樣,平行地黏上,使得各陣列同位置的元素被包在一個個小小的 tuple裡。

  • 長度是當中陣列最長的長度,內部 tuple 大小則是看有傳入陣列有幾個。
  • 由於是取最長的陣列長度當輸出長度,所以可能會有空隙,這時候可傳入自定義的 fillvalue 來填滿剩下的空間。
  • 參數是 *iterable,代表則是你可以傳任意數量的陣列進去。
  • 回傳一個 iterator 所以可以 lazily 地取值。
zip_longest([1,2,3,4,5], [155,166,177,188],[2016,2017,2018])
  -> 
(
 (1,155,2016), 
 (2,166,2017), 
 (3,177,2018), 
 (4,188,None), 
 (5,None,None)
)
zip_longest(abcde, ABC, fillevalue=X)
  ->
(
 ("a","A"),
 ("b","B"),
 ("c","C"),
 ("d","X"),
 ("e","X")
)

zip_longest 與內建 zip 的不同

zip 只取最短長度

若是其中一個陣列取完了,整個過程也就結束,當然也就沒有 padding 的需求。

zip([1,2,3,4,5], [155,166,177,188],[2016,2017,2018])
  -> 
((1,155,2016), (2,166,2017), (3,177,2018))

zip(abcde, ABC)
->
(("a","A"),("b","B"),("c","C"))

iter 是幹麻的?

對陣列套用 iter() ,會得到一個針對此陣列,記錄初始位置的 iterator。

隨著你對它做 next(),會得到它的當前值,並且此 iterator 會改變它狀態,將位置移置下一個。直到 iterator 取值到終點時,它會拋出 StopIteration 作為結束的訊號。

一般來說 iterator 的操作比較少直接用到,通常會被包裝在 for loop 裡面,由 python 來幫你包裝好整個容器的尋訪。

  • 對容器呼叫 __iter__() 取得 iterator。 (如果有支援的話)
  • 使用 __next__() 做取值與尋訪。
  • 處理 StopIteration exception 的終止訊號
bs = [0,1,2,3,4]
for loop expression
for x in bs:
    print(x)
等價於
iter1 = bs.__iter__()
try:
    while True:
        x = next(iter1)
        print(x)
except StopIteration:
    pass 

Using-an-iterator-to-print-integers

  • 同一個 iterator 在不同地方操作是會彼此影響的。
  • 不同的 iterator,彼此位置的資訊是獨立的。

Iterator

前面的星號 - Argument unpacking

用來將陣列解開,並把所有元素當參數傳入 function。

Argument unpacking

準備一個 list 與接收三個參數的 function
bs = [1, 2, 3]
def add3(a, b, c)
    return a + b + c    
手動傳入
hello(bs[0], bs[1], bs[2])
等價於
hello(*bs)

後面的星號 — list element repeat creation

[x] * n

會重複 [] 裡的元素 x 一共 n 次,再放在 [] 裡。

等價於
[x, x, x, , (n)x]

複製是以 reference 的方式,所以每個 x 都會指向同一個物件。

[iter(iterable)] * n
等價於
iter1 = iter(iterable])
[iter1, iter1, iter1, , (n) iter1]

Create List of Single Item Repeated n Times in Python

所以是怎麼組合的?

zip_longest(*[iter(iterable)] * n)

以下面兩個參數為例子

  • iterable = [0,1,2,3,4,5,6,7,8,9]
  • n = 3
1
zip_longest(* [iter([0,1,2,3,4,5,6,7,8,9])] * 3)
2
iter1 = iter([0,1,2,3,4,5,6,7,8,9])
zip_longest(* [iter1] * 3)
3
zip_longest(* [iter1, iter1, iter1])
4
zip_longest(iter1, iter1, iter1)

接著 zip_longest 依序會從左到右拿起同一個 iter1 ,填好 第一個 3-tuple

5
zip_longest(iter1, iter1, iter1) 
 -> 
(
 (0, 1, 2), 
 ...

第二個 3-tuple

6
zip_longest(iter1, iter1, iter1) 
 -> 
(
 (0, 1, 2), 
 (3, 4, 5),
 ...
7
zip_longest(iter1, iter1, iter1) 
 -> 
(
 (0, 1, 2), 
 (3, 4, 5), 
 (6, 7, 8), 
 ...

一直到 iter1 填到最後一個 9 結束,產生 最後一個 3-tuple,剩下兩個欄位就填 fillevalue 的預設值: None

final
zip_longest(iter1, iter1, iter1) 
 -> 
(
 (0, 1, 2), 
 (3, 4, 5), 
 (6, 7, 8), 
 (9, None, None)
)

Tada!

一個利用 iterator 做 round-robin 的方式,依照你給定的 n 值,將每 n 個元素包裝成一個個的 n-tuple ,進而達到 group 的目的,就完成啦。

這段 code 似乎也被收錄在官方文件的 itertools 各種 receipt ,當作是延伸用方法。也可以點下面連結去看一下其他的神應用。

Itertools Recipes

python groupby 筆記

itertool::groupby(iterable[, keyFunc=None]) 
  1. groupby, 將你的一維陣列中每個元素,依照特定的條件再各自分成一組一組的。(相鄰同 key 的元素會自成一坨)
  2. iterable, 能夠尋訪並且吐出 element 的變數
  3. keyFunc, 將 element 代入能夠得到 key 的 function。
    • def key1(ele): return ele % 3 -> 相鄰同餘 3 的元素自成一坨
    • def key2(ele): return ele[1] -> 相鄰並且第二欄位相同的元素自成一坨
    • def key3(ele): return ele.zone -> 相鄰並且欄位zone相同的元素自成一坨

Python 3 Doc

https://docs.python.org/3/library/itertools.html#itertools.groupby

Make an iterator that returns consecutive keys and groups from the iterable. The key is a function computing a key value for each element. If not specified or is None, key defaults to an identity function and returns the element unchanged. Generally, the iterable needs to already be sorted on the same key function.

行為

分類方式會線性尋訪,依序代入 element 到 keyFunc 取 key。

  • key 與上一個 element 的 key 一樣: 被加入原來 group
  • 若是不一樣: 被加入到新建的 group

當 keyFunc 沒給的預設行為

預設 keyFunc 會是 lambda x: x 自身當一個 key。故預設行為會是將 iterable 中相同 element 的圈一組。

一定要sort 好

因為它是只記錄上一次的 key 以及是線性尋訪,所以你若是要依 key 來分類的話,請一定要先 sort 好。否則會東一塊西一塊。

輸出

一個含有每個 group 2-tuple 的 iter。
group 2-tuple 中的 2nd 又是一個 group 內部的 subiter。

[(key1, iter1), (key2, iter2), (key3, iter3), ...]

例子

例子 1
groupby("hello")
  • 預設 keyFunc -> 自身即是 key -> 一樣字母的一國
  • 四國, 各是 h, e, l, o 這四個 key
  • 每一國內部還能再去取得實際字母
output
as = []
for k, g in itertools.groupby("hello"):
    as.append( [k, list(g)] )
    
print(as)
[
    ["h", ["h"]], 
    ["e", ["e"]], 
    ["l", ["l", "l"]], 
    ["o", ["o"]]
]
例子 2
groupby("ABBBCCCCAAA") # 沒 sort 的
output
[
    ["A", ["A"]], 
    ["B", ["B","B","B"]], 
    ["C", ["C","C","C","C"]], 
    ["A", ["A","A","A","A",]]
]
例子 3:
groupby(range(10), lambda x: x // 3)

0到9的數列,將商數相同的分一組

output
[
    [0, [0,1,2]],     
    [1, [3,4,5]],     
    # 3, 4, 5 這 3個數字透過 keyFunc

    # 都會得到一樣的商數 1

    # 所以被分在同一組

    [2, [6,7,8]],
    [3, [9]
]
例子 4:
groupby(
    (
        ("Dog", "land"),
        ("Cat", "land"),
        ("Eagle", "bird"),
        ("Chicken Attack", "bird"),
        ("Whale", "sea"),    
    ),
    lambda x: x[1]
)

將 element 2-tuple 的第二數值當 key
所以 Dog 跟 Cat 會因為傳入的 ("Dog", "land") 與 ("Cat", "land")
進 lambda x: x[1] 得到相同的 "land" 而分在同一組

0到9的數列,將商數相同的分一組

output
[
    ["land", ["Dog", "Cat"],    
    ["bird", ["Eagle", "Chicken Attack"],
    ["sea", ["Whale"],
]

WTL/ATL Window 關閉時所收到的 4 種 Message

1. WM_CLOSE -> OnClose

情境: 外部的關閉請求(當右上角的 X 按下,或是 ALT+F4 按下。)

  • 處理第一線的關閉請求,決定是否要執行關閉。
  • 通常會將 Handler 取名叫 OnClose,但是沒有硬性規定。
  • DefWinProc 的 default 行為即是關閉視窗,並送出 WM_DESTROY

2. WM_DESTROY -> OnDestory

情境: 視窗準備要被關閉。

  • 你沒辦法阻止即將發生的關閉行為,你只能做關閉前的資源清理。
  • 通常會將 Handler 取名叫 OnDestory,但是沒有硬性規定。

3.1 WM_NC_DESTROY

情境: 視窗已經被關閉。

3.2 OnFinalMessage

  • ATL 在做 Message Routing 時,提供一個 virtual OnFinalMessage。它在收到 WM_NC_DESTROY 時,有再特別處理。保證最後一則 Message 都有被處理之後,才呼叫 OnFinalMessage
  • 做一些關閉之後的資源清理。比較常做的是做 delete this,將被 new 的自己給自我了結。

WM_DESTROYWM_NC_DESTROY 是成對的訊息,分別表示關閉前後的時間點。做關閉時,Parent 與 Child 收到的關閉訊息以 Stack-like 的方式收到。

Parent - WM_DESTROY
    Child - WM_DESTROY
           ...
    Child - WM_NC_DESTROY
    ...
Parent - WM_NC_DESTROY

4. WM_QUIT

情境: 一切視窗皆已關閉。

終止 Message Loop。此 Message 會使得 GetMessage() 得到 0,而離開 Loop。
沒有 Loop 來派送 Message,所以基本上也不會有所謂的 Handler 來處理。

Reference

WTL/ATL CWindoesImp 組成筆記

CWindoesImp

template <class T, class TBase /* = CWindow */, class TWinTraits /* = CControlWinTraits */>
class ATL_NO_VTABLE CWindowImpl :

任何需要為特定 Control 填寫自己的 message handler callback,就需要繼承此 template。
並要求 Client 提供下列三件事

  • 將自身 class, 傳入當第一個 template T 參數(CRTP)
  • DECLARE_WND_CLASS, or DECLARE_WND_CLASS_EX
  • BEGIN_MSG_MAP(YOUR_CLASS) 與 END_MSG_MAP() 包起來的 Message Handler Map
  • (optional) Window Style 的 Traits
Predefined Traits
typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0>                  CControlWinTraits;
typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE>        CFrameWinTraits;
typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_MDICHILD>  CMDIChildWinTraits;

typedef CWinTraits<0, 0> CNullTraits;

繼承關係

CWindowImplRoot<TBase, Traits> 
     ^
CWindowImplBaseT<TBase, Traits>  
     ^
CWindowImpl<T, TBase, Traits>  
    ^
class YouWinImp : CWindowImpl<YouWinImp, CWindow, YourWinStyleTraits> 

CWindow

  • HWND Wrapper Class
  • 只有帶一個 data member HWND, cheap to copy
  • 包裝大部份圍繞著 HWND 的 window API

CWindowImpl

自帶 Handler Window 下游結構中,負責 window 註冊的相關職責。

  • 在 Create 裡做 Register,得到 atom 後,再 Chain 給 CWindowImplBaseT::Create。
  • 只留下 Window 註冊需要的名字, Style, 與背景顏色,以 Macro 的形式讓 Client 調整
    • DECLARE_WND_CLASS(NAME)
    • DECLARE_WND_CLASS_EX(NAME, STYLE, BKG_COLOR)
  • 並在 Window Create Function 提供大部份的 default 參數,降低使用難度。
template <class T, class TBase /* = CWindow */, class TWinTraits /* = CControlWinTraits */>
class ATL_NO_VTABLE CWindowImpl :
    public CWindowImplBaseT< TBase, TWinTraits >;

CWindowImplBaseT

自帶 Handler Window 中間結構中,負責 procedure 調換/重組相關職責。

  • 以 thunk 手法去重新改造 message procedure 的架構
    • 試著 subclass WNDCLASSEX::WNDPROC 導到 CWindowImplRoot 架好的 ProcessWindowMessage
    • Thunk 相關的 Setup 動作埋進 StartWindowProc
    • 接著在 Create 做實際的 CreateWindow
template <class TBase = CWindow, class TWinTraits = CControlWinTraits>
class ATL_NO_VTABLE CWindowImplBaseT : 
    public CWindowImplRoot< TBase >;

Consists of:

  - WNDPROC m_pfnSuperWindowProc
  - static DWORD GetWndStyle(_In_ DWORD dwStyle);
  - static DWORD GetWndExStyle(_In_ DWORD dwExStyle);
  - static LRESULT CALLBACK StartWindowProc();
  - static LRESULT CALLBACK WindowProc();
  - HWND Create();
  - BOOL DestroyWindow();
  - BOOL SubclassWindow(_In_ HWND hWnd);
  - HWND UnsubclassWindow(_In_ BOOL bForce = FALSE;
  - LRESULT DefWindowProc();
  - virtual void OnFinalMessage(;

CWindowImplRoot:

自帶 Handler Window 最上層結構中,負責 Messge Routing 的架構。

  • 讓 Client 得以依 BEGIN_MSG_MAP / END_MSG_MAP Marco 對中,放置自訂的 Message Handler Map。
template <class TBase /* = CWindow */>
class ATL_NO_VTABLE CWindowImplRoot : 
    public TBase, 
    public CMessageMap;

Consist of

- BEGIN_MSG_MAP/END_MSG_MAP -(Extract)-> ProcessWindowMessage
- LRESULT ForwardNotifications();
- LRESULT ReflectNotifications();
- static _Success_(return != FALSE) BOOL DefaultReflectionHandler);

Example:

不需要自帶 Message Handler 的 Window Example:

CTabCtrl<CWindow>
    - 只是單純要為額外的 HWND 訊息(TabCtrl相關),而擴充。且沒有需要自己的 Message callback
    ex:
        int HitTest(TC_HITTESTINFO* pHitTestInfo) const
        {
            ATLASSERT(::IsWindow(m_hWnd));
            return (int)::SendMessage(m_hWnd, TCM_HITTEST, 0, (LPARAM)pHitTestInfo);
        }

想自己寫 Message Handler 的 WIndow Example:

CTabCtrlImp: CWindowImp<CTabCtrlImp, CWindow, ATL::CControlWinTraits>
                        ^            ^        ^-- 使用 Control Style
                        CRTP         |---最基本的 Window Wrapper  
{
    DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE) <-- 背景顏色用 COLOR_APPWORKSPACE
                         ^     ^-- 無特定 style
                         |-由 ATL 幫忙 Gen
    ...
}

C++11 印出現在時間 使用 chrono 2

前章做了一堆事想要把 now_as_string 抽離出來,變得更 Generic, 好用, C++風格,還為了能跟 std::put_time 一樣,做出一個能與 std::ostream 重載的 operator<< ,所以定義了 now_as_obj 之累的實作型別。到頭來才發現我其實只是在做一個,帶有現在時間 std::tmstd::put_time wrapper 罷了。

為了不要重作無謂的 wrapper,應該抽離做我所缺的東西。我想要的是 將現在時間以字串表示 的服務。

[Func2] =  [Func1] => tm(now) => [put_time] => ostream => [sstream::str()] => string
Func1
std::tm now_tm()
{
  auto now = std::chrono::system_clock::now();                  // as chrono time_point
  auto now_time_t = std::chrono::system_clock::to_time_t(now);  // as UNIX-timestamp integer
  std::tm now_tm_ = {};                                          // as struct with date and time
  localtime_s(&now_tm_, &now_time_t);                            // use thread-safe localtime
  return now_tm_;
}
Func2
template<typename CharT>
std::basic_string<CharT> now_string(const CharT* fmt)
{
  std::basic_ostringstream<CharT> ss; 
  ss << std::put_time(&now_tm(), fmt);
  return ss.str();
}
Usage
std::cout << std::put_time(&now_tm(), "%F %T") << std::endl;
std::wstring str = now_string(L"%F %T");