追求寬字元字串和一般字元字串的轉換效率

在 Windows 之中,想將標準字元 (char) 字串轉換成寬字元 (wchar_t) 字串,通常會使用 MultiByteToWideChar() 這個 Win32 API。這個 API 的 prototype 如下:

int MultiByteToWideChar(
    UINT CodePage,              // [in] 說明輸入字串的編碼。本文假設都為 UTF-8。
    DWORD dwFlags,              // [in] 選項旗標。本文不討論。
    LPCSTR lpMultiByteStr,      // [in] 指向輸入字串的指標。
    int cchMultiByte,           // [in] 指定要處理輸入字串 char 個數。-1 表示處理到結尾的空字元。
    LPWSTR lpWideCharStr,       // [out; optional] 輸出的 buffer。
    int cchWideChar             // [in] 描述 lpWideCharStr 的 buffer 大小,單位為 sizeof(wchar_t)。
);

比較有趣的設計是:如果 lpWideCharStr 傳入的值是 nullptr,而 cchWideChar 是 0 的話,這個 API 的回傳值就會是「該字串完成轉換後所需要的 buffer 大小」。這是因為在不同的編碼下每個「文字」的大小可能不一樣;混用各種文字的字串,經過編碼轉換後,所需要的長度並沒有一定的公式可循,必須得真的轉換過一次,才能得知所需的長度 。

由於考慮到記憶體配置與安全性的問題,一般人在使用這個 API 時,會呼叫兩次:第一次取得所需的 buffer 大小、配置適當的記憶體後,再呼叫第二次,把轉換結果填入 buffer。把整個流程寫成函式的話,會長成這個樣子:

Read More

關於 Windows 中程式參數編碼的一些討論

標題實在下得很爛,但不知道怎麼下比較好。

這篇主要是回答 PTT 上 C_CPP 板,有 Windows programming 的初學者在問 C 程式的命令列為什麼不吃 Unicode 的問題。

這個問題其實是很大的。我的回答可能也不是很清楚。但好歹是我花時間寫的東西,就留在這邊記錄一下吧!


首先,我複議 lwecloud(註:另一位鄉民)的說法:都已經 2023 年,Windows 都是 NT-based 的了,不要再用 MBSC/ANSI 了。

你要做的事,不是把 compiler option 改成 MBSC,而是要想為什麼你在 Unicode 下編譯/執行有問題?

Read More

2024 亞洲門戶交流賽圖文雜記 (Day 3 & 4)

散策

昨天沒有開車去球場。回程是搭晚宴時才認識的新朋友的便車回來。
我們預約了今天要一起去吃美崎牛(我是一定會喝酒的),所以他邀我今早再坐他的車前往球場。
掙扎了一下,還是決定依自己的步調出門,慢慢走到球場。

經過昨天太陽的荼毒,今天不敢穿短袖出門了。
套了一件薄外套。寧可流點汗,也不要被曬傷啊!

住宅區一隅。總覺得那顆長在二樓高度的樹很有趣。配上斷垣殘壁,很有感覺。

Read More

2024 亞洲門戶交流賽圖文雜記 (Day 1)

出發

一大早六點就出門,出發去坐高鐵,然後換機捷到機場。
清晨的空氣特別冷冽,在機捷的月台上格外能感受到陣陣刺骨的冷風。
石垣島也是一樣的天氣嗎?

大約七點二時抵達機場報到櫃檯,結果發現場面比我想像的混亂不少....

主要應該是因為球員行李和球具太多,所以報到的進度有點緩慢。
我原本以為球員會有另外的通道、球具也會專案處理的說....看樣子是想太多了。

Read More

在 VS Code 中給 Rust 專案使用的 launch.json 範例

(更新:rust-analyzer 也有自動產生 launch.json 的功能。可以停留在 main.rs 上,按 F1,選取 "rust-analyzer: Generate launch configuration" 即可。)

留個筆記,不然每次都要靠 google....

{
    // 使用 IntelliSense 以得知可用的屬性。
    // 暫留以檢視現有屬性的描述。
    // 如需詳細資訊,請瀏覽: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type":"lldb",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/target/debug/${workspaceFolderBasename}",
            "args": [],
            "cwd": "${workspaceRoot}",
            "sourceLanguages": [
                "rust"
            ]
        }
    ]
}

然後記得要按 VS Code 自已的 debug/run 按鈕去執行,不要按 main() 上方的 "Run/Debug" inlay。後者不會吃 launch.json 中的 args

參考來源:Setting up a generic launch.json for Rust in VSCode

[中譯] 軟體開發預估的現實

來源:https://twitter.com/svpino/status/1696187613739208878?s=20

自古至今,軟體開發預估一直都是我們用來欺騙自己的謊言之一。

我們都知道這些預估一定不準,但都假裝這些數字多少有些參考價值。等到情況真的爆炸、一發不可收拾的時候,我們才會因此覺得很生氣。

我在大學生的時候,花了很長的時間專注於研究軟體開發預估的方法。

畢業之後,我寫了很多關於這個主題的文章。

接著,我開始在一家公司上班,花了數年的時間研究如何才能讓軟體開發預估更加準確。公司利用我建立的工具產出的軟體,創造了數百萬美元的營業額。

關於這個主題的必讀書目,我沒有漏讀任何一本。Steve McConnel 的著作《軟體預估 (Software Estimation)》我簡直倒背如流。

而我學到最重要的教訓就是:

我們無法預估軟體開發所需的時間或成本。不管是評估者是誰、或是評估者有沒有經驗,都一樣。

要能預估出值得參考的數字,基本上就是一部科幻小說。

而最精采的就是:

他們會要求你給出預估的數字。他們會說:「我們知道這只是預估,難免和現實有出入啦!」,然後保證到時候不會要你負責。

但他們就是會。沒有例外。

這樣的情況有兩種解決方案。第一個方案,我推薦給那些沒有選擇的人:

  1. 把「快速 (quick)」、「單純 (simple)」、「直覺 (straightforward)」、「簡單 (easy)」,以及所有類似的單字從你的字彙中移除。絕對不要用。不能讓別人用這些字眼形容你的工作內容。

  2. 絕對不要主動提供預估的數字。你所說的一切都會變成用來質疑你的證據。

  3. 如果被逼要預估,只預估你知道今天能完成的工作。數字一定要是一個範圍,例如:「這件事要我需要 2 到 4 小時。」

  4. 如果事情你今天做不完,就要用「天」或「週」當成評估的單位。例如:「我應該可以在本週結束之前完成這個功能。」不要用「小時」當單位去評估未來的工作。

但我們都知道,老闆一定會強迫你給出預估的時程。此時應該這麼做:

  1. 先在心裡預估你覺得完成這個任務要花多少時間。

  2. 把這個數字乘以 3。這就是你的預估範圍的下限。

  3. 把下限乘以 2。這就是預估範圍的上限。

舉例來說:如果你覺得某件事情大約一個工作天可以完成,你就要說「大約需要 3 到 6 天」。

然後有趣的是:

你還是沒有辦法準確地在 3 到 6 天中完成。用這個方法估出來的數字,跟其他方法一樣,都是個屁。

所以,這個問題的真正解決方案就是:

去一家完全不管什麼軟體開發預估的公司上班。

[Rust] Unit-Like Struct 在 Embedded Rust 中的應用

在處理嵌入式系統時常會需要控制 GPIO。所謂的 GPIO(General-Purpose I/O),就是 IC 的某些 pin 腳,可以透過軔體設定為「input(偵測外部的訊號是高電位還是低電位)」或是「output(對外輸出高電位或低電位)」狀態。

理論上來說,當 GPIO 設定為 input 狀態時,叫它「輸出高電位」是沒有意義的。反之,設定為 output 時,去讀取 pin 的電位狀態可能也是沒意義的。因此在設計存取 GPIO 的 API 時,最好能有適當的防呆機制。

Read More

[翻譯] Eric 的 BSTR 完全攻略

(原文出處:https://ericlippert.com/2003/09/12/erics-complete-guide-to-bstr-semantics/


如果你曾經用 C 或 C++ 寫過任何使用到 COM 物件的程式,你一定看過類似的程式碼:

STDMETHODIMP CFoo:Bar(BSTR bstrABC)
{ ... }

這個 BSTR 到底是三小?它和 WCHAR* 又有什麼區別?

像 C 或 C++ 這種低階語言,你有絕對的自由可以決定:究竟要用什麼方式實作某種概念。Unicode 字串就是個絕佳範例。用 C++ 來表示長度為 n 個字元的 Unicode 字串的標準方法是一個指向 2 * (n + 1) bytes 記憶體空間的指標。這塊空間中的前 2 * n bytes 是用來表示 UTF-16 編碼字元的無號短整數 (unsigned short integers),最後 2 個 bytes 的內容則是 0,用來表示字串的結尾。