Skip to content

Commit

Permalink
Make iterator_t std::input_iterator compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-datcu committed Mar 20, 2021
1 parent 707424a commit 85c8600
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 115 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -53,6 +53,7 @@ if (MSVC)
string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
add_compile_options(/EHsc)
add_compile_options(/Zc:__cplusplus)
add_compile_options(/MP) # Allow multi parrallel build

if ("${CMAKE_GENERATOR}" MATCHES "(Win64|x64)")
message(STATUS "Add /bigobj flag to compiler")
Expand Down
80 changes: 32 additions & 48 deletions dev/iterator.h
Expand Up @@ -30,20 +30,22 @@ namespace sqlite_orm {
* call. When one finishes iterating it the pointer
* inside the shared_ptr is nulled out in all copies.
*/
std::shared_ptr<sqlite3_stmt*> stmt;
view_type& view;
std::shared_ptr<statement_finalizer> stmt;

// only null for the default constructed iterator
view_type* view;

/**
* shared_ptr is used over unique_ptr here
* so that the iterator can be copyable.
*/
std::shared_ptr<value_type> current;

void extract_value(std::unique_ptr<value_type>& temp) {
temp = std::make_unique<value_type>();
auto& storage = this->view.storage;
void extract_value() {
auto& storage = this->view->storage;
auto& impl = storage.template get_impl<value_type>();
object_from_column_builder<value_type> builder{*temp, *this->stmt};
this->current = std::make_shared<value_type>();
object_from_column_builder<value_type> builder{*this->current, this->stmt->get()};
impl.table.for_each_column(builder);
}

Expand All @@ -53,75 +55,57 @@ namespace sqlite_orm {
using reference = value_type&;
using iterator_category = std::input_iterator_tag;

iterator_t(sqlite3_stmt* stmt_, view_type& view_) :
stmt(std::make_shared<sqlite3_stmt*>(stmt_)), view(view_) {
this->operator++();
}

iterator_t(const iterator_t&) = default;
iterator_t() : view(nullptr){};

iterator_t(iterator_t&&) = default;

iterator_t& operator=(iterator_t&&) = default;

iterator_t& operator=(const iterator_t&) = default;

~iterator_t() {
if(this->stmt) {
statement_finalizer f{*this->stmt};
}
iterator_t(sqlite3_stmt* stmt_, view_type& view_) :
stmt(std::make_shared<statement_finalizer>(stmt_)), view(&view_) {
next();
}

value_type& operator*() {
if(!this->stmt) {
const value_type& operator*() const {
if(!this->stmt || !this->current) {
throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator));
}
if(!this->current) {
std::unique_ptr<value_type> value;
this->extract_value(value);
this->current = move(value);
}
return *this->current;
}

value_type* operator->() {
const value_type* operator->() const {
return &(this->operator*());
}

void operator++() {
if(this->stmt && *this->stmt) {
auto ret = sqlite3_step(*this->stmt);
private:
void next() {
this->current.reset();
if(this->stmt) {
auto ret = sqlite3_step(this->stmt->get());
switch(ret) {
case SQLITE_ROW:
this->current = nullptr;
this->extract_value();
break;
case SQLITE_DONE:
this->stmt.reset();
break;
case SQLITE_DONE: {
statement_finalizer f{*this->stmt};
*this->stmt = nullptr;
} break;
default: {
auto db = this->view.connection.get();
auto db = this->view->connection.get();
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
}
}
}
}

public:
iterator_t<V>& operator++() {
next();
return *this;
}

void operator++(int) {
this->operator++();
}

bool operator==(const iterator_t& other) const {
if(this->stmt && other.stmt) {
return *this->stmt == *other.stmt;
} else {
if(!this->stmt && !other.stmt) {
return true;
} else {
return false;
}
}
return this->current == other.current;
}

bool operator!=(const iterator_t& other) const {
Expand Down
12 changes: 4 additions & 8 deletions dev/statement_finalizer.h
@@ -1,19 +1,15 @@
#pragma once

#include <memory> // std::unique_ptr
#include <sqlite3.h>
#include <type_traits> // std::integral_constant

namespace sqlite_orm {

/**
* Guard class which finalizes `sqlite3_stmt` in dtor
*/
struct statement_finalizer {
sqlite3_stmt* stmt = nullptr;
using statement_finalizer =
std::unique_ptr<sqlite3_stmt, std::integral_constant<decltype(&sqlite3_finalize), sqlite3_finalize>>;

statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {}

inline ~statement_finalizer() {
sqlite3_finalize(this->stmt);
}
};
}
2 changes: 1 addition & 1 deletion dev/view.h
Expand Up @@ -76,7 +76,7 @@ namespace sqlite_orm {
}

iterator_t<self> end() {
return {nullptr, *this};
return {};
}
};
}
Expand Down
95 changes: 37 additions & 58 deletions include/sqlite_orm/sqlite_orm.h
Expand Up @@ -5073,22 +5073,17 @@ namespace sqlite_orm {
}
#pragma once

#include <memory> // std::unique_ptr
#include <sqlite3.h>
#include <type_traits> // std::integral_constant

namespace sqlite_orm {

/**
* Guard class which finalizes `sqlite3_stmt` in dtor
*/
struct statement_finalizer {
sqlite3_stmt* stmt = nullptr;

statement_finalizer(decltype(stmt) stmt_) : stmt(stmt_) {}

inline ~statement_finalizer() {
sqlite3_finalize(this->stmt);
}
};
using statement_finalizer =
std::unique_ptr<sqlite3_stmt, std::integral_constant<decltype(&sqlite3_finalize), sqlite3_finalize>>;
}
#pragma once

Expand Down Expand Up @@ -7380,20 +7375,22 @@ namespace sqlite_orm {
* call. When one finishes iterating it the pointer
* inside the shared_ptr is nulled out in all copies.
*/
std::shared_ptr<sqlite3_stmt*> stmt;
view_type& view;
std::shared_ptr<statement_finalizer> stmt;

// only null for the default constructed iterator
view_type* view;

/**
* shared_ptr is used over unique_ptr here
* so that the iterator can be copyable.
*/
std::shared_ptr<value_type> current;

void extract_value(std::unique_ptr<value_type>& temp) {
temp = std::make_unique<value_type>();
auto& storage = this->view.storage;
void extract_value() {
auto& storage = this->view->storage;
auto& impl = storage.template get_impl<value_type>();
object_from_column_builder<value_type> builder{*temp, *this->stmt};
this->current = std::make_shared<value_type>();
object_from_column_builder<value_type> builder{*this->current, this->stmt->get()};
impl.table.for_each_column(builder);
}

Expand All @@ -7403,75 +7400,57 @@ namespace sqlite_orm {
using reference = value_type&;
using iterator_category = std::input_iterator_tag;

iterator_t(sqlite3_stmt* stmt_, view_type& view_) :
stmt(std::make_shared<sqlite3_stmt*>(stmt_)), view(view_) {
this->operator++();
}
iterator_t() : view(nullptr){};

iterator_t(const iterator_t&) = default;

iterator_t(iterator_t&&) = default;

iterator_t& operator=(iterator_t&&) = default;

iterator_t& operator=(const iterator_t&) = default;

~iterator_t() {
if(this->stmt) {
statement_finalizer f{*this->stmt};
}
iterator_t(sqlite3_stmt* stmt_, view_type& view_) :
stmt(std::make_shared<statement_finalizer>(stmt_)), view(&view_) {
next();
}

value_type& operator*() {
if(!this->stmt) {
const value_type& operator*() const {
if(!this->stmt || !this->current) {
throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator));
}
if(!this->current) {
std::unique_ptr<value_type> value;
this->extract_value(value);
this->current = move(value);
}
return *this->current;
}

value_type* operator->() {
const value_type* operator->() const {
return &(this->operator*());
}

void operator++() {
if(this->stmt && *this->stmt) {
auto ret = sqlite3_step(*this->stmt);
private:
void next() {
this->current.reset();
if(this->stmt) {
auto ret = sqlite3_step(this->stmt->get());
switch(ret) {
case SQLITE_ROW:
this->current = nullptr;
this->extract_value();
break;
case SQLITE_DONE:
this->stmt.reset();
break;
case SQLITE_DONE: {
statement_finalizer f{*this->stmt};
*this->stmt = nullptr;
} break;
default: {
auto db = this->view.connection.get();
auto db = this->view->connection.get();
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
}
}
}
}

public:
iterator_t<V>& operator++() {
next();
return *this;
}

void operator++(int) {
this->operator++();
}

bool operator==(const iterator_t& other) const {
if(this->stmt && other.stmt) {
return *this->stmt == *other.stmt;
} else {
if(!this->stmt && !other.stmt) {
return true;
} else {
return false;
}
}
return this->current == other.current;
}

bool operator!=(const iterator_t& other) const {
Expand Down Expand Up @@ -8637,7 +8616,7 @@ namespace sqlite_orm {
}

iterator_t<self> end() {
return {nullptr, *this};
return {};
}
};
}
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Expand Up @@ -43,6 +43,7 @@ add_executable(unit_tests
pragma_tests.cpp
simple_query.cpp
static_tests/is_bindable.cpp
static_tests/iterator_t.cpp
static_tests/arithmetic_operators_result_type.cpp
static_tests/tuple_conc.cpp
static_tests/node_tuple.cpp
Expand Down
36 changes: 36 additions & 0 deletions tests/static_tests/iterator_t.cpp
@@ -0,0 +1,36 @@
#include <type_traits>
#include <utility>
#include <sqlite_orm/sqlite_orm.h>
#include <catch2/catch.hpp>

using namespace sqlite_orm;

struct User {
int id = 0;
std::string name;
};

TEST_CASE("iterator_t") {
using storage = decltype(make_storage(
"aPath",
make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))));
using iter = decltype(std::declval<storage>().iterate<User>().begin());

// weakly_incrementable
static_assert(std::is_default_constructible_v<iter>, "needs to be default constructible");
static_assert(std::is_same_v<typename iter::difference_type, std::ptrdiff_t>, "needs to have difference_type");
static_assert(std::is_same_v<decltype(++std::declval<iter>()), iter&>, "needs to be incrementable");
using check = decltype(std::declval<iter>()++);

// indirectly_readable
static_assert(std::is_same_v<decltype(*std::declval<const iter>()), const User&>,
"needs to be const dereferencable");

// input_iterator
static_assert(std::is_same_v<typename iter::iterator_category, std::input_iterator_tag>,
"needs to have iterator_category");

// sentinel
static_assert(std::is_same_v<decltype(std::declval<const iter>() == std::declval<const iter>()), bool>,
"supports equality checking");
}

0 comments on commit 85c8600

Please sign in to comment.