Skip to content

Commit

Permalink
added any example
Browse files Browse the repository at this point in the history
  • Loading branch information
fnc12 committed Dec 14, 2024
1 parent fe76354 commit a6d8df0
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"system_error": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"__config": "cpp"
"__config": "cpp",
"any": "cpp"
}
}
23 changes: 22 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
cmake_minimum_required (VERSION 3.16)
cmake_minimum_required(VERSION 3.16)

# note: find_package(SQLite3 REQUIRED) already done in top-level CMakeLists

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

file(GLOB files "*.cpp")

set(run_example_targets)

foreach(file ${files})
get_filename_component(file_basename ${file} NAME_WE)

add_executable(${file_basename} ${file})
# note: sqlite3 already linked in top-level CMakeLists

target_link_libraries(${file_basename} PRIVATE sqlite_orm)

add_custom_target(run_${file_basename}
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${file_basename}
DEPENDS ${file_basename}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running example: ${file_basename}"
VERBATIM
)

list(APPEND run_example_targets run_${file_basename})
endforeach()

add_custom_target(run_all_examples
DEPENDS ${run_example_targets}
)
155 changes: 155 additions & 0 deletions examples/any.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* This example demonstrates how to use std::any as a mapped type.
* It is a bit more complex than the other examples, because it
* uses custom row_extractor and statement_binder.
* Please note that this implementation is not the one and only
* option of implementation of std:any bindong to sqlite_orm.
* It is just an example of how to use std::any as a mapped type.
* Implementation is based on 5 types SQLite supports:
* NULL, INTEGER, REAL, TEXT, BLOB.
* NULL is mapped to std::nullopt.
* INTEGER is mapped to int.
* REAL is mapped to double.
* TEXT is mapped to std::string.
* BLOB is mapped to std::vector<char>.
*/
#include <sqlite_orm/sqlite_orm.h>
#include <any>
#include <iostream>
#include <cstdio>

using namespace sqlite_orm;
using std::cout;
using std::endl;

namespace sqlite_orm {

template<>
struct row_extractor<std::any> {
std::any extract(sqlite3_stmt* stmt, int columnIndex) const {
const int type = sqlite3_column_type(stmt, columnIndex);
switch(type) {
case SQLITE_NULL:
return std::nullopt;
case SQLITE_INTEGER:
return sqlite3_column_int(stmt, columnIndex);
case SQLITE_FLOAT:
return sqlite3_column_double(stmt, columnIndex);
case SQLITE_TEXT: {
const unsigned char* text = sqlite3_column_text(stmt, columnIndex);
return std::string(reinterpret_cast<const char*>(text));
}
case SQLITE_BLOB: {
const void* blob = sqlite3_column_blob(stmt, columnIndex);
const int size = sqlite3_column_bytes(stmt, columnIndex);
return std::vector<char>(reinterpret_cast<const char*>(blob),
reinterpret_cast<const char*>(blob) + size);
}
default:
throw std::runtime_error("Unsupported SQLite column type for std::any");
}
}

std::any extract(sqlite3_value* value) const {
const int type = sqlite3_value_type(value);
switch(type) {
case SQLITE_NULL:
return std::nullopt;
case SQLITE_INTEGER:
return sqlite3_value_int(value);
case SQLITE_FLOAT:
return sqlite3_value_double(value);
case SQLITE_TEXT: {
const unsigned char* text = sqlite3_value_text(value);
return std::string(reinterpret_cast<const char*>(text));
}
case SQLITE_BLOB: {
const void* blob = sqlite3_value_blob(value);
const int size = sqlite3_value_bytes(value);
return std::vector<char>(reinterpret_cast<const char*>(blob),
reinterpret_cast<const char*>(blob) + size); // Handle BLOB
}
default:
throw std::runtime_error("Unsupported SQLite value type for std::any");
}
}
};

template<>
struct statement_binder<std::any> {
int bind(sqlite3_stmt* stmt, int index, const std::any& value) const {
if(!value.has_value()) {
return sqlite3_bind_null(stmt, index);
}

if(value.type() == typeid(int)) {
return sqlite3_bind_int(stmt, index, std::any_cast<int>(value));
} else if(value.type() == typeid(double)) {
return sqlite3_bind_double(stmt, index, std::any_cast<double>(value));
} else if(value.type() == typeid(std::string)) {
const auto& text = std::any_cast<std::string>(value);
return sqlite3_bind_text(stmt, index, text.c_str(), static_cast<int>(text.size()), SQLITE_TRANSIENT);
} else if(value.type() == typeid(std::vector<char>)) {
const auto& blob = std::any_cast<std::vector<char>>(value);
return sqlite3_bind_blob(stmt, index, blob.data(), static_cast<int>(blob.size()), SQLITE_TRANSIENT);
}

return SQLITE_MISMATCH;
}
};

template<>
struct type_printer<std::any> : public text_printer {};

template<>
struct field_printer<std::any> {
std::string operator()(const std::any& value) const {
if(!value.has_value()) {
return "NULL";
}

if(value.type() == typeid(int)) {
return std::to_string(std::any_cast<int>(value));
} else if(value.type() == typeid(double)) {
return std::to_string(std::any_cast<double>(value));
} else if(value.type() == typeid(std::string)) {
return std::any_cast<std::string>(value);
} else if(value.type() == typeid(std::vector<char>)) {
const auto& blob = std::any_cast<std::vector<char>>(value);
std::ostringstream oss;
oss << "0x";
for(unsigned char c: blob) {
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
}
return oss.str();
}

throw std::runtime_error("Unsupported type in std::any field_printer");
}
};
}

int main() {
struct Value {
int id = 0;
std::any value;
};
auto filename = "any.sqlite";
::remove(filename);
auto storage = make_storage(
filename,
make_table("test", make_column("id", &Value::id, primary_key()), make_column("value", &Value::value)));
storage.sync_schema();
storage.replace(Value{1, std::any{1}});
storage.replace(Value{2, std::any{2.5}});
storage.replace(Value{3, std::any{std::string("Hello, world!")}});
storage.replace(
Value{4, std::any{std::vector<char>{'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'}}});

cout << "Test:" << endl;
for(auto& test: storage.iterate<Value>()) {
cout << storage.dump(test) << endl;
}
cout << endl;
return 0;
}
1 change: 0 additions & 1 deletion examples/blob_binding.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#include <sqlite3.h>
#include <sqlite_orm/sqlite_orm.h>
#include <cstdint>
#include <iostream>
Expand Down
9 changes: 6 additions & 3 deletions examples/generated_column.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
#include <iostream>

#if SQLITE_VERSION_NUMBER >= 3031000
#define ENABLE_THIS_EXAMPLE
#endif

#ifdef ENABLE_THIS_EXAMPLE
using namespace sqlite_orm;
using std::cout;
using std::endl;
#endif // ENABLE_THIS_EXAMPLE

int main() {
#ifdef ENABLE_THIS_EXAMPLE
struct Product {
int id = 0;
std::string name;
Expand Down Expand Up @@ -57,8 +62,6 @@ int main() {
cout << storage.dump(product) << endl;
}
cout << endl;

#endif // ENABLE_THIS_EXAMPLE
return 0;
}

#endif // SQLITE_VERSION_NUMBER >= 3031000

0 comments on commit a6d8df0

Please sign in to comment.