~singpolyma/biboumi

d62ca9f87906be6f046fe9d07afb8bfb69c166e3 — louiz’ 5 years ago ba879a8
Use if constexpr to make things a lot more readable
M src/database/index.hpp => src/database/index.hpp +9 -12
@@ 8,19 8,16 @@
namespace
{
template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
add_column_name(std::string&)
{ }

template <std::size_t N=0, typename... T>
typename std::enable_if<N < sizeof...(T), void>::type
add_column_name(std::string& out)
void add_column_name(std::string& out)
{
  using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<std::tuple<T...>>()))>::type;
  out += ColumnType::name;
  if (N != sizeof...(T) - 1)
    out += ",";
  add_column_name<N+1, T...>(out);
  if constexpr(N < sizeof...(T))
    {
      using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<std::tuple<T...>>()))>::type;
      out += ColumnType::name;
      if (N != sizeof...(T) - 1)
        out += ",";
      add_column_name<N + 1, T...>(out);
    }
}
}


M src/database/insert_query.hpp => src/database/insert_query.hpp +40 -51
@@ 11,20 11,17 @@
#include <tuple>

template <std::size_t N=0, typename... T>
typename std::enable_if<N < sizeof...(T), void>::type
update_autoincrement_id(std::tuple<T...>& columns, Statement& statement)
void update_autoincrement_id(std::tuple<T...>& columns, Statement& statement)
{
  using ColumnType = typename std::decay<decltype(std::get<N>(columns))>::type;
  if (std::is_same<ColumnType, Id>::value)
    auto&& column = std::get<Id>(columns);
  update_autoincrement_id<N+1>(columns, statement);
  if constexpr(N < sizeof...(T))
    {
      using ColumnType = typename std::decay<decltype(std::get<N>(columns))>::type;
      if (std::is_same<ColumnType, Id>::value)
        auto&& column = std::get<Id>(columns);
      update_autoincrement_id<N + 1>(columns, statement);
    }
}

template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
update_autoincrement_id(std::tuple<T...>&, Statement&)
{}

struct InsertQuery: public Query
{
  template <typename... T>


@@ 53,23 50,20 @@ struct InsertQuery: public Query
  }

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  bind_param(const std::tuple<T...>& columns, Statement& statement, int index=1)
  void bind_param(const std::tuple<T...>& columns, Statement& statement, int index=1)
  {
    auto&& column = std::get<N>(columns);
    using ColumnType = std::decay_t<decltype(column)>;
    if constexpr(N < sizeof...(T))
      {
        auto&& column = std::get<N>(columns);
        using ColumnType = std::decay_t<decltype(column)>;

    if (!std::is_same<ColumnType, Id>::value)
      actual_bind(statement, column.value, index++);
        if constexpr(!std::is_same<ColumnType, Id>::value)
          actual_bind(statement, column.value, index++);

    this->bind_param<N+1>(columns, statement, index);
        this->bind_param<N + 1>(columns, statement, index);
      }
  }

  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  bind_param(const std::tuple<T...>&, Statement&, int)
  {}

  template <typename... T>
  void insert_values(const std::tuple<T...>& columns)
  {


@@ 79,23 73,21 @@ struct InsertQuery: public Query
  }

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_value(const std::tuple<T...>& columns, int index=1)
  void insert_value(const std::tuple<T...>& columns, int index=1)
  {
    using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

    if (!std::is_same<ColumnType, Id>::value)
    if constexpr(N < sizeof...(T))
      {
        this->body += "$" + std::to_string(index++);
        if (N != sizeof...(T) - 1)
          this->body += ", ";
        using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

        if (!std::is_same<ColumnType, Id>::value)
          {
            this->body += "$" + std::to_string(index++);
            if (N != sizeof...(T) - 1)
              this->body += ", ";
          }
        this->insert_value<N + 1>(columns, index);
      }
    this->insert_value<N+1>(columns, index);
  }
  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  insert_value(const std::tuple<T...>&, const int)
  { }

  template <typename... T>
  void insert_col_names(const std::tuple<T...>& columns)


@@ 106,24 98,21 @@ struct InsertQuery: public Query
  }

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_col_name(const std::tuple<T...>& columns)
  void insert_col_name(const std::tuple<T...>& columns)
  {
    using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

    if (!std::is_same<ColumnType, Id>::value)
    if constexpr(N < sizeof...(T))
      {
        this->body += ColumnType::name;
        using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

        if (N < (sizeof...(T) - 1))
          this->body += ", ";
      }
        if (!std::is_same<ColumnType, Id>::value)
          {
            this->body += ColumnType::name;

    this->insert_col_name<N+1>(columns);
  }
            if (N < (sizeof...(T) - 1))
              this->body += ", ";
          }

  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  insert_col_name(const std::tuple<T...>&)
  {}
        this->insert_col_name<N + 1>(columns);
      }
  }
};

M src/database/query.hpp => src/database/query.hpp +15 -26
@@ 13,12 13,12 @@

void actual_bind(Statement& statement, const std::string& value, int index);
void actual_bind(Statement& statement, const std::int64_t& value, int index);
template <typename T, typename std::enable_if_t<std::is_integral<T>::value>* = 0>
void actual_bind(Statement& statement, const std::optional<bool>& value, int index);
template <typename T>
void actual_bind(Statement& statement, const T& value, int index)
{
  actual_bind(statement, static_cast<std::int64_t>(value), index);
}
void actual_bind(Statement& statement, const std::optional<bool>& value, int index);

#ifdef DEBUG_SQL_QUERIES
#include <utils/scopetimer.hpp>


@@ 57,38 57,27 @@ struct Query
#endif
};

template <typename ColumnType>
void add_param(Query& query, const ColumnType& column)
{
  std::cout << "add_param<ColumnType>" << std::endl;
  actual_add_param(query, column.value);
}

void actual_add_param(Query& query, const std::string& val);
void actual_add_param(Query& query, const std::optional<bool>& val);
template <typename T>
void actual_add_param(Query& query, const T& val)
{
  query.params.push_back(std::to_string(val));
}

void actual_add_param(Query& query, const std::string& val);

template <typename T>
typename std::enable_if<!std::is_integral<T>::value, Query&>::type
operator<<(Query& query, const T&)
{
  query.body += T::name;
  return query;
}
void actual_add_param(Query& query, const std::optional<bool>& val);

Query& operator<<(Query& query, const char* str);
Query& operator<<(Query& query, const std::string& str);
template <typename Integer>
typename std::enable_if<std::is_integral<Integer>::value, Query&>::type
operator<<(Query& query, const Integer& i)
template <typename T>
Query& operator<<(Query& query, const T& i)
{
  query.body += "$" + std::to_string(query.current_param++);
  actual_add_param(query, i);
  if constexpr(std::is_integral<T>::value)
    {
      query.body += "$" + std::to_string(query.current_param++);
      actual_add_param(query, i);
    }
  else
    {
      query.body += T::name;
    }
  return query;
}


M src/database/row.hpp => src/database/row.hpp +25 -27
@@ 29,41 29,39 @@ struct Row
    return col.value;
  }

  template <bool Coucou=true>
  void save(std::unique_ptr<DatabaseEngine>& db, typename std::enable_if<!is_one_of<Id, T...> && Coucou>::type* = nullptr)
  void save(std::unique_ptr<DatabaseEngine>& db)
  {
    this->insert(*db);
  }

  template <bool Coucou=true>
  void save(std::unique_ptr<DatabaseEngine>& db, typename std::enable_if<is_one_of<Id, T...> && Coucou>::type* = nullptr)
  {
    const Id& id = std::get<Id>(this->columns);
    if (id.value == Id::unset_value)
    if constexpr(is_one_of<Id, T...>)
      {
        this->insert(*db);
        if (db->last_inserted_rowid >= 0)
          std::get<Id>(this->columns).value = static_cast<Id::real_type>(db->last_inserted_rowid);
        const Id& id = std::get<Id>(this->columns);
        if (id.value == Id::unset_value)
          {
            this->insert(*db);
            if (db->last_inserted_rowid >= 0)
              std::get<Id>(this->columns).value = static_cast<Id::real_type>(db->last_inserted_rowid);
          }
        else
          this->update(*db);
      }
    else
      this->update(*db);
      this->insert(*db);
  }

 private:
  template <bool Coucou=true>
  void insert(DatabaseEngine& db, typename std::enable_if<is_one_of<Id, T...> && Coucou>::type* = nullptr)
  {
    InsertQuery query(this->table_name, this->columns);
    // Ugly workaround for non portable stuff
    query.body += db.get_returning_id_sql_string(Id::name);
    query.execute(db, this->columns);
  }

  template <bool Coucou=true>
  void insert(DatabaseEngine& db, typename std::enable_if<!is_one_of<Id, T...> && Coucou>::type* = nullptr)
  void insert(DatabaseEngine& db)
  {
    InsertQuery query(this->table_name, this->columns);
    query.execute(db, this->columns);
    if constexpr(is_one_of<Id, T...>)
      {
        InsertQuery query(this->table_name, this->columns);
        // Ugly workaround for non portable stuff
        query.body += db.get_returning_id_sql_string(Id::name);
        query.execute(db, this->columns);
      }
    else
      {
        InsertQuery query(this->table_name, this->columns);
        query.execute(db, this->columns);
      }
  }

  void update(DatabaseEngine& db)

M src/database/select_query.hpp => src/database/select_query.hpp +32 -45
@@ 15,48 15,37 @@
using namespace std::string_literals;

template <typename T>
typename std::enable_if<std::is_integral<T>::value, std::int64_t>::type
extract_row_value(Statement& statement, const int i)
auto extract_row_value(Statement& statement, const int i)
{
  return statement.get_column_int64(i);
}

template <typename T>
typename std::enable_if<std::is_same<std::string, T>::value, T>::type
extract_row_value(Statement& statement, const int i)
{
  return statement.get_column_text(i);
}

template <typename T>
typename std::enable_if<std::is_same<std::optional<bool>, T>::value, T>::type
extract_row_value(Statement& statement, const int i)
{
  const auto integer = statement.get_column_int(i);
  if (integer > 0)
    return true;
  else if (integer < 0)
    return false;
  return std::nullopt;
  if constexpr(std::is_integral<T>::value)
    return statement.get_column_int64(i);
  else if constexpr (std::is_same<std::string, T>::value)
    return statement.get_column_text(i);
  else if (std::is_same<std::optional<bool>, T>::value)
    {
      const auto integer = statement.get_column_int(i);
      if (integer > 0)
        return std::optional<bool>{true};
      else if (integer < 0)
        return std::optional<bool>{false};
      return std::optional<bool>{};
    }
}

template <std::size_t N=0, typename... T>
typename std::enable_if<N < sizeof...(T), void>::type
extract_row_values(Row<T...>& row, Statement& statement)
void extract_row_values(Row<T...>& row, Statement& statement)
{
  using ColumnType = typename std::remove_reference<decltype(std::get<N>(row.columns))>::type;
  if constexpr(N < sizeof...(T))
    {
      using ColumnType = typename std::remove_reference<decltype(std::get<N>(row.columns))>::type;

  auto&& column = std::get<N>(row.columns);
  column.value = static_cast<decltype(column.value)>(extract_row_value<typename ColumnType::real_type>(statement, N));
      auto&& column = std::get<N>(row.columns);
      column.value = static_cast<decltype(column.value)>(extract_row_value<typename ColumnType::real_type>(statement, N));

  extract_row_values<N+1>(row, statement);
      extract_row_values<N + 1>(row, statement);
    }
}

template <std::size_t N=0, typename... T>
typename std::enable_if<N == sizeof...(T), void>::type
extract_row_values(Row<T...>&, Statement&)
{}

template <typename... T>
struct SelectQuery: public Query
{


@@ 69,23 58,21 @@ struct SelectQuery: public Query
    }

    template <std::size_t N=0>
    typename std::enable_if<N < sizeof...(T), void>::type
    insert_col_name()
    void insert_col_name()
    {
      using ColumnsType = std::tuple<T...>;
      using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnsType>()))>::type;
      if constexpr(N < sizeof...(T))
        {
          using ColumnsType = std::tuple<T...>;
          using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnsType>()))>::type;

      this->body += " " + std::string{ColumnType::name};
          this->body += " " + std::string{ColumnType::name};

      if (N < (sizeof...(T) - 1))
        this->body += ", ";
          if (N < (sizeof...(T) - 1))
            this->body += ", ";

      this->insert_col_name<N+1>();
          this->insert_col_name<N + 1>();
        }
    }
    template <std::size_t N=0>
    typename std::enable_if<N == sizeof...(T), void>::type
    insert_col_name()
    {}

  SelectQuery& where()
    {

M src/database/table.hpp => src/database/table.hpp +20 -24
@@ 93,36 93,32 @@ class Table
 private:

  template <std::size_t N=0>
  typename std::enable_if<N < sizeof...(T), void>::type
  add_column_if_not_exists(DatabaseEngine& db, const std::set<std::string>& existing_columns)
  void add_column_if_not_exists(DatabaseEngine& db, const std::set<std::string>& existing_columns)
  {
    using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
    if (existing_columns.count(ColumnType::name) == 0)
      add_column_to_table<ColumnType>(db, this->name);
    add_column_if_not_exists<N+1>(db, existing_columns);
    if constexpr(N < sizeof...(T))
      {
        using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
        if (existing_columns.count(ColumnType::name) == 0)
          add_column_to_table<ColumnType>(db, this->name);
        add_column_if_not_exists<N + 1>(db, existing_columns);
      }
  }
  template <std::size_t N=0>
  typename std::enable_if<N == sizeof...(T), void>::type
  add_column_if_not_exists(DatabaseEngine&, const std::set<std::string>&)
  {}

  template <std::size_t N=0>
  typename std::enable_if<N < sizeof...(T), void>::type
  add_column_create(DatabaseEngine& db, std::string& str)
  void add_column_create(DatabaseEngine& db, std::string& str)
  {
    using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
    str += ColumnType::name;
    str += " ";
    str += ToSQLType<ColumnType>(db);
    if (N != sizeof...(T) - 1)
      str += ",";

    add_column_create<N+1>(db, str);
    if constexpr(N < sizeof...(T))
      {
        using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
        str += ColumnType::name;
        str += " ";
        str += ToSQLType<ColumnType>(db);
        if (N != sizeof...(T) - 1)
          str += ",";

        add_column_create<N + 1>(db, str);
      }
  }
  template <std::size_t N=0>
  typename std::enable_if<N == sizeof...(T), void>::type
  add_column_create(DatabaseEngine&, std::string&)
  { }

  const std::string name;
};

M src/database/update_query.hpp => src/database/update_query.hpp +23 -28
@@ 39,27 39,25 @@ struct UpdateQuery: public Query
  }

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  insert_col_name_and_value(const std::tuple<T...>& columns)
  void insert_col_name_and_value(const std::tuple<T...>& columns)
  {
    using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

    if (!std::is_same<ColumnType, Id>::value)
    if constexpr(N < sizeof...(T))
      {
        this->body += ColumnType::name + "=$"s + std::to_string(this->current_param);
        this->current_param++;
        using ColumnType = std::decay_t<decltype(std::get<N>(columns))>;

        if (N < (sizeof...(T) - 1))
          this->body += ", ";
      }
        if (!std::is_same<ColumnType, Id>::value)
          {
            this->body += ColumnType::name + "=$"s
                          + std::to_string(this->current_param);
            this->current_param++;

    this->insert_col_name_and_value<N+1>(columns);
  }
  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  insert_col_name_and_value(const std::tuple<T...>&)
  {}
            if (N < (sizeof...(T) - 1))
              this->body += ", ";
          }

        this->insert_col_name_and_value<N + 1>(columns);
      }
  }

  template <typename... T>
  void execute(DatabaseEngine& db, const std::tuple<T...>& columns)


@@ 76,23 74,20 @@ struct UpdateQuery: public Query
  }

  template <int N=0, typename... T>
  typename std::enable_if<N < sizeof...(T), void>::type
  bind_param(const std::tuple<T...>& columns, Statement& statement, int index=1)
  void bind_param(const std::tuple<T...>& columns, Statement& statement, int index=1)
  {
    auto&& column = std::get<N>(columns);
    using ColumnType = std::decay_t<decltype(column)>;
    if constexpr(N < sizeof...(T))
      {
        auto&& column = std::get<N>(columns);
        using ColumnType = std::decay_t<decltype(column)>;

    if (!std::is_same<ColumnType, Id>::value)
      actual_bind(statement, column.value, index++);
        if (!std::is_same<ColumnType, Id>::value)
          actual_bind(statement, column.value, index++);

    this->bind_param<N+1>(columns, statement, index);
        this->bind_param<N + 1>(columns, statement, index);
      }
  }

  template <int N=0, typename... T>
  typename std::enable_if<N == sizeof...(T), void>::type
  bind_param(const std::tuple<T...>&, Statement&, int)
  {}

  template <typename... T>
  void bind_id(const std::tuple<T...>& columns, Statement& statement)
  {