/
UCS
/
sqlite_orm
Обзор
Документация
Войти
/
UCS
/
sqlite_orm
Код
Запросы
0
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
examples/enum_binding.cpp
176 строк
6 KB
fnc12
changed PointerAlignment from Right to Left
21 ноя 2020, 20:38
21 ноя 2020, 20:38
1eaff43
Код
Авторство
О чём код?
#include <sqlite_orm/sqlite_orm.h> #include <iostream> #include <memory> #include <cassert> using std::cout; using std::endl; /** * This is a enum we want to map to our sqlite db. * First we have to decide what db type enum must have. * Let's make it `TEXT`: Gender::Male will store as 'male' string * and Gender::Female as 'female' string. */ enum class Gender { Male, Female, }; // This is a class with field `gender` with type `Gender`. struct SuperHero { int id; std::string name; Gender gender; }; // also we need transform functions to make string from enum.. std::string GenderToString(Gender gender) { switch(gender) { case Gender::Female: return "female"; case Gender::Male: return "male"; } throw std::domain_error("Invalid Gender enum"); } /** * and enum from string. This function has nullable result cause * string can be neither `male` nor `female`. Of course we * won't allow this to happed but as developers we must have * a scenario for this case. * These functions are just helpers. They will be called from several places * that's why I placed it separatedly. You can use any transformation type/form * (for example BETTER_ENUM https://github.com/aantron/better-enums) */ std::unique_ptr<Gender> GenderFromString(const std::string& s) { if(s == "female") { return std::make_unique<Gender>(Gender::Female); } else if(s == "male") { return std::make_unique<Gender>(Gender::Male); } return nullptr; } /** * This is where magic happens. To tell sqlite_orm how to act * with Gender enum we have to create a few service classes * specializations (traits) in sqlite_orm namespace. */ namespace sqlite_orm { /** * First of all is a type_printer template class. * It is responsible for sqlite type string representation. * We want Gender to be `TEXT` so let's just derive from * text_printer. Also there are other printers: real_printer and * integer_printer. We must use them if we want to map our type to `REAL` (double/float) * or `INTEGER` (int/long/short etc) respectively. */ template<> struct type_printer<Gender> : public text_printer {}; /** * This is a binder class. It is used to bind c++ values to sqlite queries. * Here we have to create gender string representation and bind it as string. * Any statement_binder specialization must have `int bind(sqlite3_stmt*, int, const T&)` function * which returns bind result. Also you can call any of `sqlite3_bind_*` functions directly. * More here https://www.sqlite.org/c3ref/bind_blob.html */ template<> struct statement_binder<Gender> { int bind(sqlite3_stmt* stmt, int index, const Gender& value) { return statement_binder<std::string>().bind(stmt, index, GenderToString(value)); // or return sqlite3_bind_text(stmt, index++, GenderToString(value).c_str(), -1, SQLITE_TRANSIENT); } }; /** * field_printer is used in `dump` and `where` functions. Here we have to create * a string from mapped object. */ template<> struct field_printer<Gender> { std::string operator()(const Gender& t) const { return GenderToString(t); } }; /** * This is a reverse operation: here we have to specify a way to transform string received from * database to our Gender object. Here we call `GenderFromString` and throw `std::runtime_error` if it returns * nullptr. Every `row_extractor` specialization must have `extract(const char*)` and `extract(sqlite3_stmt *stmt, * int columnIndex)` functions which return a mapped type value. */ template<> struct row_extractor<Gender> { Gender extract(const char* row_value) { if(auto gender = GenderFromString(row_value)) { return *gender; } else { throw std::runtime_error("incorrect gender string (" + std::string(row_value) + ")"); } } Gender extract(sqlite3_stmt* stmt, int columnIndex) { auto str = sqlite3_column_text(stmt, columnIndex); return this->extract((const char*)str); } }; } int main(int /* argc*/, char** /*argv*/) { using namespace sqlite_orm; auto storage = make_storage("", make_table("superheros", make_column("id", &SuperHero::id, primary_key()), make_column("name", &SuperHero::name), make_column("gender", &SuperHero::gender))); storage.sync_schema(); storage.remove_all<SuperHero>(); // insert Batman (male) storage.insert(SuperHero{-1, "Batman", Gender::Male}); // get Batman by name auto batman = storage.get_all<SuperHero>(where(c(&SuperHero::name) == "Batman")).front(); // print Batman cout << "batman = " << storage.dump(batman) << endl; // insert Wonder woman storage.insert(SuperHero{-1, "Wonder woman", Gender::Female}); // get all superheros auto allSuperHeros = storage.get_all<SuperHero>(); // print all superheros cout << "allSuperHeros = " << allSuperHeros.size() << endl; for(auto& superHero: allSuperHeros) { cout << storage.dump(superHero) << endl; } // insert a second male (Superman) storage.insert(SuperHero{-1, "Superman", Gender::Male}); // get all male superheros (2 expected) auto males = storage.get_all<SuperHero>(where(c(&SuperHero::gender) == Gender::Male)); cout << "males = " << males.size() << endl; assert(males.size() == 2); for(auto& superHero: males) { cout << storage.dump(superHero) << endl; } // get all female superheros (1 expected) auto females = storage.get_all<SuperHero>(where(c(&SuperHero::gender) == Gender::Female)); assert(females.size() == 1); cout << "females = " << females.size() << endl; for(auto& superHero: females) { cout << storage.dump(superHero) << endl; } return 0; }