2017年6月24日 星期六

在Visual Studio Code寫C/C++ (1) - 第一個執行檔HelloWorld

2017/06/24 10:28~12:25

Visual Studio Code (VSCode) 是個單純的編輯器,但有著非常多得外掛,安裝之後你可以用它來開發Java, PHP, JavaScript 和 Golane等等許多程式語言,也有很多很棒的編輯器工具可以使用。

自從我換從Windows跳槽到Mac之後,一直找不到像Notepad++好用的編輯器,最近因為覺得用XCode開發C/C++程式很不友善,而且XCode只有Mac能用,於事一直在找免費且跨平台的IDE,找到了VSCode,意外發現Notepad++好用的功能他都有,包含我最愛用的編輯器功能『利用關鍵字過濾出此檔案中包含關鍵字的所有行數文字』,既然有這個功能,就沒有理由不用它了啊~!

這篇網誌會用VSCode, C/C++ plugin 還有CMake完成一個helloWorld的執行檔 。


1. 建立專案







2. 設定專案為C/C++
新增兩個檔案:

  • CMakeLists.txt
  • hello-vscode.cpp







CMakeLists.txt內容如下:
cmake_minimum_required(VERSION 3.0)
project(hello-vscode)
set(SOURCE hello-vscode.cpp)
add_executable(${PROJECT_NAME} ${SOURCE})
hello-vscode.cpp內容如下:
#include <iostream>
int main(int argc, const char * argv[]) {
    std::cout << "hello Visual Studio Code! :)" << '\n'; 
    return 0;
}


3. 產生c_cpp_properties.json
  • 在編輯器按下F1,會出現Command列表
  • 輸入"C/Cpp: Edit Configurations"
  • 按下Enter


c_cpp_properties.json內容如下:
{
    "configurations": [
        {
            "name": "Mac",
            "includePath": [
                "${workspaceRoot}",
                "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
                "/usr/local/include",
                "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/include",
                "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
                "/usr/include"
            ],
            "defines": [],
            "browse": {
                "path": [
                    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
                    "/usr/local/include",
                    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/include",
                    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
                    "/usr/include"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        },
        {
            "name": "Linux",
            "includePath": [
                "${workspaceRoot}",
                "/usr/include",
                "/usr/local/include"
            ],
            "defines": [],
            "browse": {
                "path": [
                    "/usr/include",
                    "/usr/local/include"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        },
        {
            "name": "Win32",
            "includePath": [
                "${workspaceRoot}",
                "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE"
            ],
            "browse": {
                "path": [
                    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        }
    ]
}

4. 產生CMake和Make的Tasks設定檔tasks.json
  • 按下F1打開Command表
  • 輸入"Tasks: Configure Task Runner",
  • 選擇Task為"Others"



修改tasks.json內容如下:
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "0.1.0",
    "command": "sh",
    "isShellCommand": true,
    "args": ["-c"],
    "showOutput": "always",
    "suppressTaskName": true,
    "options": {
        "cwd": "${workspaceRoot}/build"
    },
    "tasks": [
        {
            "taskName": "cmake",
            "args": ["cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Debug .."]     
        },
        {
            "taskName": "make",
            "args": ["make -j 8"],
            "isBuildCommand": true            
        }
    ]
}
其中cwd代表Current Working Directory,${workspaceRoot}/build則代表會把產生出來的檔案放在此目錄中。但目前沒有build的目錄,我們必須手動建立build資料夾。





5. 執行CMake

  • 案F1打開Command列表
  • 輸入"Run Task",選擇"Tasks: Run Task"
  • 選擇CMake



6. 產生執行檔
  • 案F1打開Command列表
  • 輸入"Run Build Task"
  • 進入build資料夾
  • 在Terminal中輸入"./hello-vscode"執行程式





7. 使用LLDB Debug
  • 點選debug按鈕
  • 點選齒輪
  • 選擇"C++ (GDB/LLDB)",產生launch.json
  • 將launch.json中的program改為"${workspaceRoot}/build/hello-vscode"
  • 將launch.json中的cwd改為"${workspaceRoot}"
  • 在hello-vscode.cpp中設定斷點
  • 點選Start Debugging開始debug






滑鼠點選行號左邊,即可產生程式斷點 



點選紅色框框處或按下快捷鍵F5開始除錯



最後launch.json內容會長這樣
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(lldb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceRoot}/build/hello-vscode",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceRoot}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "lldb"
        }
    ]
}

。。。


終於完成在VSCode中的第一個C++程式啦!


2016年5月29日 星期日

從定義看重構

2016/5/29 14:06~14:45


圖片取自 <泰迪軟體-軟體重構入門實作班>投影片

重構就是『在不改變程式外在行為的前提之下改變城市內部結構以提升設計品質

Teddy從定義切入引伸出了六個議題:

1. 如何確認程式外在行為沒有被改變?


在正常的軟體開發過程中,通常會先規劃好設計規格(或類別),才開始動手寫程式,然後經過手動或自動化測試驗證功能是否符合規格。
程式會依時間或需求而變得越來越大坨,測試項目也會跟著越來越多,假設目前的程式已開發了100個功能,當你重構一小塊功能,通常要重新測試這100個功能,以確保自己修改的程式沒有問題。這時候如果沒有自動化測試的話通常開發人員也會開始不敢改程式,更別說整理程式碼了。

2. 如何定義哪些是外在行為?


一個使用者使用計算機類別的類別的public "add" 方法,對使用者來說,add的行為就是外在行為,計算機使用者來說是一個黑箱,我不知道它怎麼做,但我知道它會說。

3. 為什麼要以不改變程式外在行為當作前提?


這個問題很有趣,為什麼不能再新增功能的同時順便重構?
不知道你有沒有這樣的體驗,在新增功能的過程中,發現某一塊的程式碼寫了100行達成一個功能,但你發現其實寫10行就可以達到一樣的功能,所以你修改這一塊程式碼,改完之後才繼續做原本要新增的功能,好不容易把功能新增完,很興奮地按下"執行",再來你的情緒就跟著程式一起崩潰了,是剛剛重構錯誤,還是新增的功能錯誤?

4. 怎麼改變程式內部結構以提升品質?


藉由重構裡面所定義的壞味道與技巧,加上自己的反覆練行。想知道怎麼改可以讀Martin Flower的重構或者上泰迪軟體-軟體重構入門實作班

5. 那些內部結構可以改?


以Java來說,程式碼的結構從小到依序為
變數(Variable) → 條件式(Statement) → 方法(Method) → 類別(Class) → 包裹 (Package)
重構裡面所談的重構技巧也是有結構上的差別,例如調整類別的繼承關係、改變方法的介面、將1個100行的方法切成5個20行的方法等等。

6. 品質提升的目的為何?


讓軟體變軟


Teddy總是能把死的定義講得很活真的有很猛

2015年11月5日 星期四

Android StackTrace

2015/11/05 15:10~15:31

在C語言中,Log時可以使用 __func__  __LINE__ 取得目前程式所執行的函式和行數,在Android中的StackTraceElement物件也提供了類似的功能,它記錄了Call stack的許多資訊,包括執行檔案名稱、程式碼行號、方法(或函數)名稱、類別名稱。

程式範例:

package com.example.getstacktrace;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         trace("test");
    }

    private void trace(String msg) {
         StackTraceElement[] stack = Thread.currentThread().getStackTrace();        
         for(int i = 0; i < stack.length; i++) {
             StackTraceElement s = stack[i];
             Log.d("Brian", "i=" + i);
             Log.d("Brian", "ClassName="+s.getClassName());
             Log.d("Brian", "FileName="+s.getFileName());
             Log.d("Brian", "MethodName="+s.getMethodName());
             Log.d("Brian""LineName="+s.getLineNumber());      
             i ++;
         }
         Log.d("Brian ", msg);
    }

}

輸出結果:
11-05 15:25:49.748: D/Brian(6662): i=0
11-05 15:25:49.748: D/Brian(6662): ClassName=dalvik.system.VMStack
11-05 15:25:49.748: D/Brian(6662): FileName=VMStack.java
11-05 15:25:49.748: D/Brian(6662): MethodName=getThreadStackTrace
11-05 15:25:49.748: D/Brian(6662): LineNumber=-2
11-05 15:25:49.748: D/Brian(6662): i=1
11-05 15:25:49.748: D/Brian(6662): ClassName=java.lang.Thread
11-05 15:25:49.748: D/Brian(6662): FileName=Thread.java
11-05 15:25:49.748: D/Brian(6662): MethodName=getStackTrace
11-05 15:25:49.748: D/Brian(6662): LineNumber=599
11-05 15:25:49.748: D/Brian(6662): i=2
11-05 15:25:49.748: D/Brian(6662): ClassName=com.example.getstacktrace.MainActivity
11-05 15:25:49.748: D/Brian(6662): FileName=MainActivity.java
11-05 15:25:49.748: D/Brian(6662): MethodName=trace
11-05 15:25:49.748: D/Brian(6662): LineNumber=16
11-05 15:25:49.748: D/Brian(6662): i=3
11-05 15:25:49.748: D/Brian(6662): ClassName=com.example.getstacktrace.MainActivity
11-05 15:25:49.748: D/Brian(6662): FileName=MainActivity.java
11-05 15:25:49.748: D/Brian(6662): MethodName=onCreate
11-05 15:25:49.748: D/Brian(6662): LineNumber=12
11-05 15:25:49.748: D/Brian(6662): i=4
11-05 15:25:49.748: D/Brian(6662): ClassName=android.app.Activity
11-05 15:25:49.748: D/Brian(6662): FileName=Activity.java
11-05 15:25:49.748: D/Brian(6662): MethodName=performCreate
11-05 15:25:49.748: D/Brian(6662): LineNumber=5165
11-05 15:25:49.748: D/Brian(6662): i=5
11-05 15:25:49.748: D/Brian(6662): ClassName=android.app.Instrumentation
11-05 15:25:49.748: D/Brian(6662): FileName=Instrumentation.java
11-05 15:25:49.748: D/Brian(6662): MethodName=callActivityOnCreate
11-05 15:25:49.748: D/Brian(6662): LineNumber=1103
11-05 15:25:49.758: D/Brian(6662): i=6
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=performLaunchActivity
11-05 15:25:49.758: D/Brian(6662): LineNumber=2416
11-05 15:25:49.758: D/Brian(6662): i=7
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=handleLaunchActivity
11-05 15:25:49.758: D/Brian(6662): LineNumber=2521
11-05 15:25:49.758: D/Brian(6662): i=8
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=access$600
11-05 15:25:49.758: D/Brian(6662): LineNumber=162
11-05 15:25:49.758: D/Brian(6662): i=9
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread$H
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=handleMessage
11-05 15:25:49.758: D/Brian(6662): LineNumber=1370
11-05 15:25:49.758: D/Brian(6662): i=10
11-05 15:25:49.758: D/Brian(6662): ClassName=android.os.Handler
11-05 15:25:49.758: D/Brian(6662): FileName=Handler.java
11-05 15:25:49.758: D/Brian(6662): MethodName=dispatchMessage
11-05 15:25:49.758: D/Brian(6662): LineNumber=99
11-05 15:25:49.758: D/Brian(6662): i=11
11-05 15:25:49.758: D/Brian(6662): ClassName=android.os.Looper
11-05 15:25:49.758: D/Brian(6662): FileName=Looper.java
11-05 15:25:49.758: D/Brian(6662): MethodName=loop
11-05 15:25:49.758: D/Brian(6662): LineNumber=158
11-05 15:25:49.758: D/Brian(6662): i=12
11-05 15:25:49.758: D/Brian(6662): ClassName=android.app.ActivityThread
11-05 15:25:49.758: D/Brian(6662): FileName=ActivityThread.java
11-05 15:25:49.758: D/Brian(6662): MethodName=main
11-05 15:25:49.758: D/Brian(6662): LineNumber=5777
11-05 15:25:49.758: D/Brian(6662): i=13
11-05 15:25:49.758: D/Brian(6662): ClassName=java.lang.reflect.Method
11-05 15:25:49.758: D/Brian(6662): FileName=Method.java
11-05 15:25:49.758: D/Brian(6662): MethodName=invokeNative
11-05 15:25:49.758: D/Brian(6662): LineNumber=-2
11-05 15:25:49.758: D/Brian(6662): i=14
11-05 15:25:49.758: D/Brian(6662): ClassName=java.lang.reflect.Method
11-05 15:25:49.758: D/Brian(6662): FileName=Method.java
11-05 15:25:49.758: D/Brian(6662): MethodName=invoke
11-05 15:25:49.758: D/Brian(6662): LineNumber=511
11-05 15:25:49.758: D/Brian(6662): i=15
11-05 15:25:49.758: D/Brian(6662): ClassName=com.android.internal.os.ZygoteInit$MethodAndArgsCaller
11-05 15:25:49.758: D/Brian(6662): FileName=ZygoteInit.java
11-05 15:25:49.758: D/Brian(6662): MethodName=run
11-05 15:25:49.758: D/Brian(6662): LineNumber=1083
11-05 15:25:49.758: D/Brian(6662): i=16
11-05 15:25:49.758: D/Brian(6662): ClassName=com.android.internal.os.ZygoteInit
11-05 15:25:49.758: D/Brian(6662): FileName=ZygoteInit.java
11-05 15:25:49.758: D/Brian(6662): MethodName=main
11-05 15:25:49.758: D/Brian(6662): LineNumber=850
11-05 15:25:49.758: D/Brian(6662): i=17
11-05 15:25:49.758: D/Brian(6662): ClassName=dalvik.system.NativeStart
11-05 15:25:49.758: D/Brian(6662): FileName=NativeStart.java
11-05 15:25:49.758: D/Brian(6662): MethodName=main
11-05 15:25:49.758: D/Brian(6662): LineNumber=-2
11-05 15:25:49.758: D/Brian(6662): test



挑自己需要的StackTraceElement包成Method就很好Debug了!

2015年10月26日 星期一

印出環境變數

2015/10/26 11:38~11:54


在Windows中,很多時候需要用命令提示字元觀看環境變數,在Powershell中,這時可以使用下面的命令

$env:Path

輸出結果:

C:\Windows\Microsoft.NET\Framework\v4.0.30319;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\ProgramFiles\TortoiseSVN\bin;C:\HashiCorp\Vagrant\bin;C:\Program Files\SourceGear\Common\DiffMerge\;C:\Program Files (x86)\Skype\Phone\


在Windows的環境變數中,每一個路徑使用分號隔開,直接印出來很難閱讀有哪先路徑。


這時可以把分隔符號分號取代為跳行,結果如下:

($env:Path).Replace(';', "`n")

輸出結果:

C:\Windows\Microsoft.NET\Framework\v4.0.30319
C:\ProgramData\Oracle\Java\javapath
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\Program Files\Microsoft SQL Server\110\Tools\Binn\
C:\ProgramFiles\TortoiseSVN\binC:\HashiCorp\Vagrant\bin
C:\Program Files\SourceGear\Common\DiffMerge\
C:\Program Files (x86)\Skype\Phone\

Powershell用起來比命令提示字元順好多!

2015年7月29日 星期三

持續改善實戰演練

2015/7/28 12:25-13:27,
2015/7/29 00:31-01:14


今天打的這篇將會出現一位未出現過的人物,名為Joanne,是一位嚴重受科技控制的女孩,一起出去時不時就會拿起手機喵一下是否有新訊息,吃飯吃到一半也不忘要拿起我的Lumia 925,技術純熟地開啟我的FB塗鴉牆或者instagram,看看我有沒有偷偷跟哪個小妹妹聯絡感情。看到這邊應該可以猜出她和我的關係了吧?沒錯!她就是我家的那隻啦XD

---

事情發生在7/25 (六)

這天很巧,和可愛的Joanne大人去吃一家員林赫赫有名的希拉餐廳,燈光美氣氛佳,可惜我那不給力的FB塗鴉牆出現多年不見的要好女性友人PO了疑似看見我的文章。

離開希拉之後,路過一間名字聽起來不錯的迥畫廊咖啡,點了幾杯飲料開始接受Joanne的質問。

---

Joanne:「她PO這個是因為看到我們嗎?」

我:「不會吧!我已經仔細環顧四周了,沒有看到我認識的人啊!」

Joanne:「她幹嘛PO的好像在躲我們一樣,這樣真的很不夠意思!」

我:「她的動態對我們來說也沒有很重要啊,這麼在乎幹嘛。」

---

講完之後我自己我有點生氣,為什麼要在意一個不是很重要的人這麼多!於是我專心看著咖啡店裡的,Joanne也很有默契地滑著手機,我倆不發一語,一直到我看到第26頁的時候,終於突破僵局手拉著手離開咖啡廳。然後從此過著幸福快樂的一天~~~了嗎?

當然沒有,這只是個小小的火種,悄悄的埋在Joanne的心中。

回到家約晚上七點多,老爸一如往常地看到我們見聊個兩句

老爸:「Joanne啊,你很不夠意思欸!我以為你今天回來會煮飯給我吃,害我等你們等這麼久」(老爸是講中文,以上對話純屬修改)

我聞到Joanne生氣的壞味道,趕緊出面緩和一下場面

我:「老爸,Joanne最近在台北工作很累,她是回來渡假休息的啦!不要為難一個正在渡假的人啊!」

老爸:「那我去煮個拿手的泡麵來孝敬你們兩個正在渡假的人要不要?」

講到這邊Joanne就委屈的跑去廁所。

老爸又接著說:「Joanne怎麼時常跑廁所,你要關心一下她。」

說完老爸也出去溜搭了,然後我開始看著週六晚檔,連播四集,共兩小時的動畫。

身為一個電視迷,說什麼也要排除順利觀賞節目的阻礙,我專心地看著電視裡生動的動畫,Joanne從廁所出來後,就零音量地坐在一旁玩筆電、手機。就連在動畫廣告時,我故意向空氣發出的問句,她也不為所動。但為了順利觀賞動畫演出。顧不得Joanne的任性,只好努力地催眠自己沒有任何事情發生。

就在動畫演完的那一刻,我的感官神經從電視飛回到我的身體,驚覺到身旁有股能量準備爆發,雖想當作沒事地把時間拖過去。但想到如果不讓Joanne爆發,不就枉費了從Teddy學來的人生道理軟體工程嗎?

敏捷開發的最大優點就是能透過一連串的活動讓問題浮現出來,發現問題之後,才有機會對症下藥。於是心裡開始有個聲音默默出現:「如果連一個人都沒辦法搞定,那怎麼可能有辦法搞定整個團隊?」

我跨出了很勇敢的第一步:「你生氣是不是因為爸爸說你怎麼沒有煮菜給他吃?」

此話一出,馬上引來Joanne的情緒爆炸,配著滔滔不絕的眼淚和哽咽又有磁性地聲音:「我今天跟你說很多事情,你都在第一時間否定我,都說這是小事不用在意,向你表達我的感覺,卻沒有得到情緒上的安慰,在你們家我是一個外人,如連你都不站我的立場替想,那我不是很孤單委屈嗎?你爸爸這樣說,我怎麼知道是開玩笑,我們的家庭教育就是不一樣啊!#$%@$#%^%$#^%#$%^」 講的太長我也忘記到底在講什麼XD

不過算是發現了Joanne的情緒崩潰點算是有點進展,吵了將近三十分鐘後,套入Retrospective,將今天所有事件的處理方式列出優點和缺點。

Could be better:

1. 我們兩個的個性一個大而化之;一個心思縝密,要站在Joanne立場多位她說話。
2. 你(Brian)遇到問題每次都沉默
3. 你(Brian)應該是最了解我(Joanne)的人,會知道我(Joanne)的心情

Good:

1. 查覺到異狀有即時出手幫Joanne說話,雖然說得很爛(渡假是三小?家長聽了會怎麼想?)
2. 舉行Retrospective讓雙方有理性溝通的機會


講完之後就和好了,再努力再加油,朝著更幸福快樂的日子努力!

以上內容由事實改編。這次網誌Delay了三天。