Skip to content

Commit

Permalink
A few more unit tests for tuple transformation
Browse files Browse the repository at this point in the history
  • Loading branch information
trueqbit committed Oct 25, 2023
1 parent fdf9ab8 commit a226d23
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 26 deletions.
10 changes: 4 additions & 6 deletions dev/functional/mpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
* Hence it contains only a very small subset of a full MPL.
*
* Three key concepts are critical to understanding:
* 1. A 'metafunction' is a class template that represents a function invocable at compile-time.
* 1. A 'metafunction' is a class template with a nested `type` typename, and represents a function invocable at compile-time.
* E.g. `template<class T> struct x { using type = int; };`
* 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type.
* 2. A 'metafunction operation' is an alias template for a class template or a nested template expression, whose instantiation yields a type.
* E.g. `template<class T> using alias_op_t = typename x<T>::type`
* 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming.
* More precisely, it's a class with a nested metafunction called "fn"
Expand Down Expand Up @@ -103,16 +103,14 @@ namespace sqlite_orm {
};

template<template<class...> class Fn, class... Args>
struct invoke_meta<std::enable_if_t<!is_alias_template_v<Fn, Args...>>, Fn, Args...> {
using type = typename Fn<Args...>::type;
};
struct invoke_meta<std::enable_if_t<!is_alias_template_v<Fn, Args...>>, Fn, Args...> : Fn<Args...> {};

/*
* Invoke metafunction or metafunction operation.
*
* @attention If using an alias template, be aware that it isn't recognizable whether an alias template is
* a. an alias of a class template (e.g. `template<class T> using aliased = x<T>`) or
* b. an alias of a nested template expression yielding a result (e.g. `template<class T> alias_op_t = typename x<T>::type`)
* b. an alias of a class template or nested template expression yielding a result (e.g. `template<class T> alias_op_t = typename x<T>::type`)
* Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or
* in other words, `invoke_meta_t` expects an alias of a nested template expression (b.).
* The alternative in that case is to use `invoke_fn_t`.
Expand Down
1 change: 1 addition & 0 deletions dev/tuple_helper/tuple_transformer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace sqlite_orm {
template<class Pack, template<class...> class Op>
using transform_tuple_t = typename tuple_transformer<Pack, Op>::type;

// note: applying a combiner like `plus_fold_integrals` needs fold expressions
#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
/*
* Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results.
Expand Down
11 changes: 5 additions & 6 deletions include/sqlite_orm/sqlite_orm.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,9 +746,9 @@ namespace sqlite_orm {
* Hence it contains only a very small subset of a full MPL.
*
* Three key concepts are critical to understanding:
* 1. A 'metafunction' is a class template that represents a function invocable at compile-time.
* 1. A 'metafunction' is a class template with a nested `type` typename, and represents a function invocable at compile-time.
* E.g. `template<class T> struct x { using type = int; };`
* 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type.
* 2. A 'metafunction operation' is an alias template for a class template or a nested template expression, whose instantiation yields a type.
* E.g. `template<class T> using alias_op_t = typename x<T>::type`
* 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming.
* More precisely, it's a class with a nested metafunction called "fn"
Expand Down Expand Up @@ -841,16 +841,14 @@ namespace sqlite_orm {
};

template<template<class...> class Fn, class... Args>
struct invoke_meta<std::enable_if_t<!is_alias_template_v<Fn, Args...>>, Fn, Args...> {
using type = typename Fn<Args...>::type;
};
struct invoke_meta<std::enable_if_t<!is_alias_template_v<Fn, Args...>>, Fn, Args...> : Fn<Args...> {};

/*
* Invoke metafunction or metafunction operation.
*
* @attention If using an alias template, be aware that it isn't recognizable whether an alias template is
* a. an alias of a class template (e.g. `template<class T> using aliased = x<T>`) or
* b. an alias of a nested template expression yielding a result (e.g. `template<class T> alias_op_t = typename x<T>::type`)
* b. an alias of a class template or nested template expression yielding a result (e.g. `template<class T> alias_op_t = typename x<T>::type`)
* Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or
* in other words, `invoke_meta_t` expects an alias of a nested template expression (b.).
* The alternative in that case is to use `invoke_fn_t`.
Expand Down Expand Up @@ -9889,6 +9887,7 @@ namespace sqlite_orm {
template<class Pack, template<class...> class Op>
using transform_tuple_t = typename tuple_transformer<Pack, Op>::type;

// note: applying a combiner like `plus_fold_integrals` needs fold expressions
#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
/*
* Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results.
Expand Down
6 changes: 3 additions & 3 deletions tests/statement_serializer_tests/bindables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ array<string, N> single_value_array(const char* s) {
}

template<class T>
using wrap_in_literal = internal::literal_holder<T>;
using make_literal_holder = internal::literal_holder<T>;

inline void require_string(const string& value, const string& expected) {
REQUIRE(value == expected);
Expand Down Expand Up @@ -192,7 +192,7 @@ TEST_CASE("bindables") {
}
SECTION("non-bindable literals") {
context.replace_bindable_with_question = true;
constexpr auto t = make_default_tuple<internal::transform_tuple_t<Tuple, wrap_in_literal>>();
constexpr auto t = make_default_tuple<internal::transform_tuple_t<Tuple, make_literal_holder>>();
test_tuple(t, context, e);
}
}
Expand Down Expand Up @@ -254,7 +254,7 @@ TEST_CASE("bindables") {
}
SECTION("non-bindable literals") {
context.replace_bindable_with_question = true;
auto t = make_default_tuple<internal::transform_tuple_t<Tuple, wrap_in_literal>>();
auto t = make_default_tuple<internal::transform_tuple_t<Tuple, make_literal_holder>>();
test_tuple(t, context, e);
}
}
Expand Down
5 changes: 5 additions & 0 deletions tests/static_tests/functional/mpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
#include <functional> // std::less

using namespace sqlite_orm;
using internal::literal_holder;

template<class TransparentFunction>
using transparent_of_t = typename TransparentFunction::is_transparent;

template<class T>
using make_literal_holder = literal_holder<T>;

TEST_CASE("mpl") {
using mpl_is_same = mpl::quote_fn<std::is_same>;

Expand All @@ -20,6 +24,7 @@ TEST_CASE("mpl") {
STATIC_REQUIRE(std::is_same<mpl::invoke_op_t<std::common_type_t, int>, int>::value);
STATIC_REQUIRE(std::is_same<mpl::invoke_meta_t<std::common_type, int>, int>::value);
STATIC_REQUIRE(std::is_same<mpl::invoke_meta_t<std::common_type_t, int>, int>::value);
STATIC_REQUIRE(std::is_same<mpl::invoke_meta_t<make_literal_holder, int>, literal_holder<int>>::value);
STATIC_REQUIRE(mpl::invoke_t<mpl_is_same, int, int>::value);
STATIC_REQUIRE_FALSE(mpl::invoke_t<mpl::not_<mpl_is_same>, int, int>::value);
STATIC_REQUIRE(mpl::invoke_t<mpl::bind_front_fn<std::is_constructible, std::vector<int>>, size_t, int>::value);
Expand Down
49 changes: 38 additions & 11 deletions tests/static_tests/functional/tuple_transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
#include <type_traits> // std::is_same

using namespace sqlite_orm;
using internal::field_type_t;
using internal::literal_holder;
using internal::transform_tuple_t;
#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
using internal::nested_tuple_size_for_t;
#endif

template<class T>
using make_literal_holder = literal_holder<T>;

#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
struct tuple_maker {
template<class... Types>
auto operator()(Types&&... types) const {
return std::make_tuple(std::forward<Types>(types)...);
}
};
#endif

TEST_CASE("tuple_helper static") {
SECTION("tuple_transformer") {
Expand All @@ -12,18 +30,27 @@ TEST_CASE("tuple_helper static") {
std::string b;
std::string c;
};
auto column1 = make_column("id", &Table::id);
auto column2 = make_column("a", &Table::a);
auto column3 = make_column("b", &Table::b);
auto column4 = make_column("c", &Table::c);

using Column1 = decltype(column1);
using Column2 = decltype(column2);
using Column3 = decltype(column3);
using Column4 = decltype(column4);
using ColumnsTuple = std::tuple<Column1, Column2, Column3, Column4>;
using ColumnsMappedTypes = internal::transform_tuple_t<ColumnsTuple, internal::field_type_t>;

auto columnsTuple = std::make_tuple(make_column("id", &Table::id),
make_column("a", &Table::a),
make_column("b", &Table::b),
make_column("c", &Table::c));
using ColumnsTuple = decltype(columnsTuple);
using ColumnsMappedTypes = transform_tuple_t<ColumnsTuple, field_type_t>;
using Expected = std::tuple<int64_t, std::string, std::string, std::string>;
STATIC_REQUIRE(std::is_same<ColumnsMappedTypes, Expected>::value);

STATIC_REQUIRE(std::is_same<transform_tuple_t<std::tuple<bool, int>, make_literal_holder>,
std::tuple<literal_holder<bool>, literal_holder<int>>>::value);

#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
// make tuples out of tuple elements, and count the first 2 of 3 resulting tuples
STATIC_REQUIRE(
std::is_same<nested_tuple_size_for_t<std::tuple, std::tuple<int, int, int>, std::make_index_sequence<2>>,
std::integral_constant<size_t, 2>>::value);

REQUIRE(internal::recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) ==
std::make_tuple(3, 1, 2));
#endif
}
}

0 comments on commit a226d23

Please sign in to comment.