fnc12/sqlite_orm 使用心得 === sqlite_orm[^sqlite_orm] 是一個 C++ 的 slite 函式庫,雖然在 github 上跟同類型的庫相比算是星星較多的,但是有不少潛在的問題,以下針對使用過程留下一些筆記,避免後人繼續坑... ### 環境建置 首先,它仰賴 Catch2,於是我從 github[^Catch2] 上 clone 下來編譯後安裝。 接著 `make` 遲遲過不了,後來發現是自動下載有點問題,不知道為什麼仰賴的 sqlite 函式庫是透過自動下載但是卻總是載不完整導致 sha1 驗證過不了。於是我移除 cmake 對 sqlite-amalgamation-3210000.zip 的 sha1 驗證,並從官方網站[^sqlite]手動下載。 總算完成 `make install`,趕緊開個 hello world 使用看看這個函式庫,但是 `cmake` 卻總是卡住,後來才發現是套件的別名與路徑有問題(作者提供的引用方式語法不對),於是我在 `cmake` 使用 `find_package(sqlite_orm REQUIRED NAMES sqlite_orm SqliteOrm)`,並且用指令修復標頭檔的相對位置: ```bash mkdir /usr/local/include/sqlite_orm sudo mv sqlite_orm.h sqlite_orm/. ``` ### 封裝問題 接著我試著要依照OOP的寫法封裝成 Repository ,卻碰到了問題。 從原始碼可以看出 ` make_storage` 的回傳型別與傳入的參數有關[^make_storage],也就是其型別受到資料庫的欄位設定影響。 > I mean there is no common storage_t type cause storage_t is templated just like `std::vector`: `std::vector<int>` ≠ `std::vector<char>` 因此要獲得型別必須使用以下方法[^type]: ```cpp= struct Employee { int id; std::string name; int age; std::string address; double salary; }; inline auto initStorage(const std::string &path) { using namespace sqlite_orm; return make_storage(path, make_table("COMPANY", make_column("ID", &Employee::id, primary_key()), make_column("NAME", &Employee::name), make_column("AGE", &Employee::age), make_column("ADDRESS", &Employee::address), make_column("SALARY", &Employee::salary))); } using Storage = decltype(initStorage("")); Storage storage = initStorage("storage.sqlite"); ``` 注意當中的 `using Storage = decltype(initStorage(""));`, 在獲得型別 `Storage` 必須先調用 `initStorage`,欲調用 `initStorage` 則必先定義該函數,這使得獲得型別時,程式碼已經處於定義階段,因此無法在宣告階段的標頭檔使用該型別[^undefined],換句話說,若要撰寫宣告與定義分離的的程式碼(`.cpp` 與`.h`)在實現上有一定程度的困難: ```cpp // SpeciesRepository.h class SpeciesRepository{ public: SpeciesRepository(); private: Storage storage; }; ``` 最後總算是把大部分的前置作業放到 `.hpp`,再從 class include 解決: ```cpp= //SpeciesStorage.hpp #pragma once #include <string> #include <vector> #include <sqlite_orm/sqlite_orm.h> struct Species{ int id; int parentId; int generation; int batch; bool testing; bool tested; std::vector<char> chromosome; int scored; }; inline auto createSpeciesStorage(const std::string &path) { using namespace sqlite_orm; return make_storage(path, make_table("species", make_column("id", &Species::id, autoincrement(), primary_key()), make_column("parent_id", &Species::parentId), make_column("generation", &Species::generation), make_column("batch", &Species::batch), make_column("testing", &Species::testing), make_column("tested", &Species::tested), make_column("chromosome", &Species::chromosome), make_column("scored", &Species::scored) )); } using SpeciesStorage = decltype(createSpeciesStorage("")); ``` ```cpp #pragma once #include <memory> #include "SpeciesStorage.hpp" class SpeciesRepository{ public: SpeciesRepository(const std::string &path); private: std::unique_ptr<SpeciesStorage> storage; }; ``` ### 又一個教學問題 好了,解決封裝問題了,該來寫一些 CRUD 的邏輯了,沒想到又碰到問題,依照 `READ.md` 提供的語法[^crud]嘗試: ```cpp storage.update_all(set(c(&User::lastName) = "Hardey", c(&User::typeId) = 2), where(c(&User::firstName) == "Tom")); ``` 卻得到的錯誤訊息: ``` error: invalid operands to binary expression ('internal::expression_t<bool Species::*>' and 'int') ``` 接著我在範例發現不一樣的寫法[^example]: ```cpp storage.update_all(set(assign(&Track::trackArtist, 3)), where(is_equal(&Track::trackName, "Mr. Bojangles"))); ``` ### IDE 解析問題 我使用 Netbeans 11.1,IDE 解析得到一大堆紅底線,程式碼維護起來更為困難。主要原因是該函式庫大量使用 template ,因此 IDE 不能順暢的辨識型別。類似的問題在我使用 STL 的容器也會發生。 ### 結論 花了不少時間在故障排除,雖然過程中也有學點東西,不過基本上已經到達我的停損點了,使用該函式庫的計畫暫時擱置,準備動身去嘗試其他替代方案了。對這個套件的作者沒有惡意,只是個人使用起來的體驗蠻糟糕的... [^example]: https://github.com/fnc12/sqlite_orm/blob/e8a9e9416f421303f4b8970caab26dadf8bae98b/examples/foreign_key.cpp#L73 [^crud]: https://github.com/fnc12/sqlite_orm#crud [^undefined]: https://stackoverflow.com/a/8752879/11833578 [^type]: https://github.com/fnc12/sqlite_orm/wiki/FAQ#quick--binary-fat [^make_storage]: https://github.com/fnc12/sqlite_orm/blob/master/include/sqlite_orm/sqlite_orm.h#L11837 [^Catch2]: https://github.com/catchorg/Catch2 [^sqlite]: https://www.sqlite.org/2017/sqlite-amalgamation-3210000.zip [^sqlite_orm]: https://github.com/fnc12/sqlite_orm ###### tags: `learning note` `c++` `2020-05-09`