wxJson填坑記

wxJson 介紹

wxJson 是 專門爲 wxWidgets 這個C++ 跨平臺框架量身定做的 JSON 類庫, 這個庫本身很小,支持JSON讀寫操作,總共7個源文件,2009年 就已經開發好了,目前不再更新。不過官方依舊可以看到詳細的開發文檔,整整10年過去了,在這個新技術層出不窮的年代,這麼老的古董依舊散發着餘熱. 下面 挖坑(金子) 的故事稍稍有點長,請大家耐住性子,備好茶,枸杞泡起來. (加班熬夜的你是不是身體已經垮了…😃)

1. 無法編譯 wxJson 爲 DLL庫

從官網下載最新 wxJson安裝包,解壓後打開 Visual Studio 自帶的命令行工具(我的機器上是 x64 Native Tools Command Prompt for VS 2017),你們的環境可能和我不一樣,因爲我要編譯 64位 的wxJson。請注意,使用系統自帶的命令行是不行的,不能正常編譯。 進入解壓後的 build 子目錄,使用最新64位版 mingw32-make(版本4.2.1) , 運行語句 mingw32-make makefile.gcc, 會出現下面的錯誤提示, 本人試過調整 makefile.gcc 文件裏面的編譯參數,依舊無果.

/usr/bin/sh: wx-config: command not found
/usr/bin/sh: wx-config: command not found
/usr/bin/sh: wx-config: command not found
process_begin: CreateProcess(NULL, wx-config --static=no --toolkit= --version=., ...) failed.
make (e=2): ϵͳ�Ҳ���ָ�����ļ���
mingw32-make: *** [GNUmakefile:206: test_for_selected_wxbuild] Error 2

這個錯誤提示無法提供有用的線索,然後在安裝包根目錄下我發現一個叫 INSTALL.txt 的文件,裏面是wxJson的編譯說明。看了半天(下面是英文原話),作者想告訴我們,wxJson 源代碼其實不復雜,cpp文件就3個,直接拷貝文件到自己的項目中編譯也是不錯的選擇,這樣一來,不用操心 makefile 文件各種跨平臺編譯配置,也沒有單獨編譯wxJson ANSI/UNICODE,DEBUG/RELEASSE各種版本的麻煩,何樂而不爲呢?.

The library itself is only 150 KB in release mode and 460 KB in debug
mode so it is more simple to just include it in your own sources and
let the compiler of your application to compile the library itself.
You just have to copy the three source files located in the ‘src/’
subfolder in your own source folder and the four header files
located in the ‘include/’ subfolder in your own header folder.

Be sure to preserve the ‘wx/’ folder under the ‘include/’.
Next add the three source ‘.cpp’ files to your project and you are done.
This way, your application does not depend on wxJSON because it is
embedded in your app and you do not need to deal with ‘makefiles’,
‘bakefiles’, ‘configs’ and so on.

2. 拷貝wxJson源文件到工程目錄

因爲wxJson 庫本身文件目錄結構的原因, 把四個頭文件都放在 include/wx 子目錄下,我拷貝到自己項目中後,直接將源代碼和頭文件放在一個目錄裏,這樣就需要調整一下cpp源代碼文件包含的頭文件路徑,這個非常簡單,比如 將 #include <wx/jsonreader.h> 修改爲 "#include jsonreader.h";然後按照官方的例子,在項目中依葫蘆畫瓢。再次編譯,出現下面這種錯誤提示。

D:\work_c++\sniper\lib\wxJSON\jsonwriter.cpp|891|warning: 'int wxJSONWriter::WriteNullValue(wxOutputStream&)' redeclared without dllimport attribute after being referenced with dll linkage|
D:\work_c++\sniper\lib\spider.cpp|44|undefined reference to `__imp__ZN12wxJSONReaderC1Eii'|

一看是 dllimport 這個鬼東西搞得鬼,於是打開 json_defs.h 看到下面的宏定義

#ifdef WXMAKINGDLL_JSON
    #define WXDLLIMPEXP_JSON                  WXEXPORT
    #define WXDLLIMPEXP_DATA_JSON(type)       WXEXPORT type
#elif defined(WXUSINGDLL)
    #define WXDLLIMPEXP_JSON                  WXIMPORT
    #define WXDLLIMPEXP_DATA_JSON(type)       WXIMPORT type
#else // not making nor using DLL
    #define WXDLLIMPEXP_JSON
    #define WXDLLIMPEXP_DATA_JSON(type)	    type
#endif

因爲我項目中定義了 WXUSINGDLL 這個宏,所以上面這一段 if 宏定義塊只有這段代碼工作:

    #define WXDLLIMPEXP_JSON                  WXIMPORT
    #define WXDLLIMPEXP_DATA_JSON(type)       WXIMPORT type

於是我機智的將 #elif defined(WXUSINGDLL)註釋掉 ,於是上面的宏就執行 #else 塊了,變成

    #define WXDLLIMPEXP_JSON
    #define WXDLLIMPEXP_DATA_JSON(type)	    type

繼續編譯,這個時候調用代碼直接蹦了. 😦

3. 函數傳參類型錯誤

代碼奔潰了
天吶擼!不給人活路, 苦逼的我含着淚 單擊Stop 按鈕 停止了調試。這個時候,調試器打開了strvararg.h, 定位到下面這個函數。臥槽,完全懵逼!仔細一看,這個文件是wxWidgets-3.1.2 庫包含的文件,我開始有點凌亂了.

// normalizer for passing arguments to functions working with wchar_t* (and
// until ANSI build is removed, char* in ANSI build as well - FIXME-UTF8)
// string representation
#if !wxUSE_UTF8_LOCALE_ONLY
template<typename T>
struct wxArgNormalizerWchar : public wxArgNormalizer<T>
{
    wxArgNormalizerWchar(T value,
                         const wxFormatString *fmt, unsigned index)
        : wxArgNormalizer<T>(value, fmt, index) {}
};
#endif // !wxUSE_UTF8_LOCALE_ONLY

思索半天也不知道原因,初步估計應該是wxJson調用wxWidgets的函數出現問題。無奈之下,我放了個大招,開始在wxJson源文件下斷點,從調用的入口開始,密密麻麻放了一堆紅點點。單步開始運行,一路追蹤,半個小時過去了,有了重大發現。在 jsonval.cpp 源文件 第 1818 行, 代碼運行崩潰了. 就是下面這個函數第二條 wxLogTrace. 打印語句報錯了.

//! Return the item at the specified key.
/*!
 The function returns a reference to the object in the map
 that has the specified key.
 If \c key does not exist, a new NULL value is created with
 the provided key and a reference to it is returned.
 If this object does not contain a map, the old value is
 replaced by a map object.
*/
wxJSONValue&
wxJSONValue::Item( const wxString& key )
{
    wxLogTrace( traceMask, _T("(%s) searched key=\'%s\'"), __PRETTY_FUNCTION__, key.c_str());
    wxLogTrace( traceMask, _T("(%s) actual object: %s"), __PRETTY_FUNCTION__, GetInfo().c_str());
    //上面這條語句是報錯的源頭,需要註釋掉。
    
    wxJSONRefData* data = COW();
    wxJSON_ASSERT( data );

    if ( data->m_type != wxJSONTYPE_OBJECT )  {
        // deletes the contained value;
        data = SetType( wxJSONTYPE_OBJECT );
        return data->m_valMap[key];
    }
    wxLogTrace( traceMask, _T("(%s) searching key \'%s' in the actual object"),
                 __PRETTY_FUNCTION__, key.c_str() );
    return data->m_valMap[key];
}

wxLogTracewxWidgets 自帶的函數,我嘗試註釋掉這一句 wxLogTrace( traceMask, _T("(%s) actual object: %s"), __PRETTY_FUNCTION__, GetInfo().c_str()),重新編譯運行, 程序正常了!😃

好奇的你一定想知道,爲啥第二條 wxLogTrace 崩潰了呢? 具體原因我也不清楚,不過以我多年的經驗來看,wxLogTrace 的調用失敗應該是 wxWidgets 升級的緣故。新版 wxWidgets 已經不支持 wxJson 那種方式調用 wxLogTrace 了,畢竟 wxJson 已經十年不更新了,而 wxWidgets 還在緩慢升級維護中。 wxJson 源碼我大概看了個遍,註釋掉上面那條打印語句,完全不會影響 wxJson 的功能,大家可以放心去試。 如果在嘗試的過程中,你們有新的發現,請在評論區留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章