Skip to content

Commit 4b61d6c

Browse files
authored
Reflection-based aliases (#101)
* Add reflection-based aliases usable via `<column/table/expression>.as<"my_alias">()` (requires reflection-capable compiler) * Add macro for checking if reflection functionalities should be included and add test code that uses the generated member
1 parent 4335893 commit 4b61d6c

7 files changed

Lines changed: 270 additions & 0 deletions

File tree

include/sqlpp23/core/basic/table.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include <sqlpp23/core/detail/type_set.h>
3636
#include <sqlpp23/core/type_traits.h>
3737

38+
#include <sqlpp23/core/name/create_reflection_name_tag.h>
39+
3840
namespace sqlpp {
3941
template <typename TableSpec>
4042
struct table_t : public TableSpec::template _table_columns<table_t<TableSpec>>,
@@ -44,6 +46,15 @@ struct table_t : public TableSpec::template _table_columns<table_t<TableSpec>>,
4446
-> table_as_t<TableSpec, name_tag_of_t<NameTagProvider>> {
4547
return {};
4648
}
49+
50+
#if SQLPP_INCLUDE_REFLECTION == 1
51+
template <::sqlpp::detail::fixed_string Alias>
52+
constexpr auto as() const -> table_as_t<
53+
TableSpec,
54+
name_tag_of_t<decltype(::sqlpp::meta::make_alias<Alias>())>> {
55+
return {};
56+
}
57+
#endif
4758
};
4859

4960
template <typename TableSpec>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#pragma once
2+
3+
/*
4+
* Copyright (c) 2026, Roland Bock
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* Redistributions of source code must retain the above copyright notice, this
11+
* list of conditions and the following disclaimer.
12+
*
13+
* Redistributions in binary form must reproduce the above copyright notice,
14+
* this list of conditions and the following disclaimer in the documentation
15+
* and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
#include <cstddef>
31+
#include <format>
32+
#include <stdexcept>
33+
#include <string_view>
34+
35+
namespace sqlpp::detail {
36+
template <::std::size_t Size>
37+
struct fixed_string {
38+
static constexpr ::std::size_t size = Size;
39+
40+
consteval fixed_string(const char (&str)[Size]) noexcept {
41+
for (::std::size_t i = 0; i < Size; ++i) {
42+
value[i] = str[i];
43+
}
44+
}
45+
46+
constexpr operator ::std::string_view() const {
47+
return ::std::string_view(value, Size);
48+
}
49+
50+
constexpr operator const char*() const { return value; }
51+
52+
consteval auto operator[](::std::size_t index) -> char {
53+
if (index >= Size) {
54+
throw ::std::out_of_range("index is out of range of fixed_string");
55+
}
56+
return value[index];
57+
}
58+
59+
char value[Size]{};
60+
};
61+
} // namespace sqlpp::detail
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#pragma once
2+
3+
/*
4+
* Copyright (c) 2026, Roland Bock
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* Redistributions of source code must retain the above copyright notice, this
11+
* list of conditions and the following disclaimer.
12+
*
13+
* Redistributions in binary form must reproduce the above copyright notice,
14+
* this list of conditions and the following disclaimer in the documentation
15+
* and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
#ifndef SQLPP_INCLUDE_REFLECTION
31+
#if defined(__cpp_impl_reflection) && __cpp_impl_reflection >= 202506L
32+
#define SQLPP_INCLUDE_REFLECTION 1
33+
#else
34+
#define SQLPP_INCLUDE_REFLECTION 0
35+
#endif
36+
#endif
37+
38+
#if SQLPP_INCLUDE_REFLECTION == 1
39+
40+
#include <meta>
41+
#include <optional>
42+
43+
#include <sqlpp23/core/detail/fixed_string.h>
44+
45+
namespace sqlpp::meta {
46+
47+
template <::sqlpp::detail::fixed_string Alias>
48+
struct reflection_alias {
49+
struct _sqlpp_name_tag {
50+
[[maybe_unused]] static constexpr bool require_quotes = false;
51+
[[maybe_unused]] static constexpr auto name = Alias;
52+
53+
template <typename T>
54+
struct _member_impl_outer_t {
55+
struct _member_impl_inner_t;
56+
57+
consteval {
58+
::std::meta::define_aggregate(
59+
^^_member_impl_inner_t,
60+
{
61+
::std::meta::data_member_spec(
62+
^^T, {
63+
.name = _sqlpp_name_tag::name,
64+
.alignment = std::nullopt,
65+
.bit_width = std::nullopt,
66+
.no_unique_address = false})});
67+
}
68+
};
69+
70+
// separate struct with base class necessary because it's not possible to
71+
// add a member function with `define_aggregate`
72+
template <typename T>
73+
struct _member_t : _member_impl_outer_t<T>::_member_impl_inner_t {
74+
auto& operator()(this auto&& self) {
75+
// used to access members of base
76+
constexpr auto ctx1 = ::std::meta::access_context::current();
77+
constexpr auto ctx2 = ctx1.via(^^_member_t);
78+
79+
// get first (and single) member of this struct which has `Alias` as its
80+
// name
81+
return self
82+
.[: ::std::meta::nonstatic_data_members_of(
83+
^^typename _member_impl_outer_t<T>::_member_impl_inner_t,
84+
ctx2)[0]:];
85+
}
86+
};
87+
};
88+
};
89+
90+
template <::sqlpp::detail::fixed_string Alias>
91+
consteval auto make_alias() -> ::sqlpp::meta::reflection_alias<Alias> {
92+
return {};
93+
}
94+
95+
} // namespace sqlpp::meta
96+
#endif

include/sqlpp23/core/operator/enable_as.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2929
#include <sqlpp23/core/operator/as_expression.h>
3030
#include <sqlpp23/core/type_traits.h>
3131

32+
#include <sqlpp23/core/name/create_reflection_name_tag.h>
33+
3234
namespace sqlpp {
3335
// To be used as CRTP base for expressions that should offer the as() member
3436
// function.
@@ -39,6 +41,16 @@ class enable_as {
3941
-> decltype(::sqlpp::as(std::forward<Expr>(self), alias)) {
4042
return ::sqlpp::as(std::forward<Expr>(self), alias);
4143
}
44+
45+
#if SQLPP_INCLUDE_REFLECTION == 1
46+
template <::sqlpp::detail::fixed_string Alias, typename Expr>
47+
constexpr auto as(this Expr&& self)
48+
-> decltype(::sqlpp::as(::std::forward<Expr>(self),
49+
::sqlpp::meta::make_alias<Alias>())) {
50+
return ::sqlpp::as(::std::forward<Expr>(self),
51+
::sqlpp::meta::make_alias<Alias>());
52+
}
53+
#endif
4254
};
4355

4456
template <typename T>

tests/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ add_subdirectory(helpers)
5353
add_subdirectory(serialize)
5454
add_subdirectory(types)
5555
add_subdirectory(usage)
56+
add_subdirectory(meta)

tests/core/meta/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright (c) 2026, Roland Bock
2+
# All rights reserved.
3+
#
4+
# Redistribution and use in source and binary forms, with or without modification,
5+
# are permitted provided that the following conditions are met:
6+
#
7+
# Redistributions of source code must retain the above copyright notice, this
8+
# list of conditions and the following disclaimer.
9+
#
10+
# Redistributions in binary form must reproduce the above copyright notice, this
11+
# list of conditions and the following disclaimer in the documentation and/or
12+
# other materials provided with the distribution.
13+
#
14+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
25+
function(create_test name)
26+
set(target sqlpp23_core_meta_${name})
27+
add_executable(${target} ${name}.cpp)
28+
target_link_libraries(${target} PRIVATE sqlpp23::core sqlpp23_testing sqlpp23_core_testing sqlpp23_core_testing)
29+
add_test(NAME ${target} COMMAND ${target})
30+
endfunction()
31+
32+
create_test(alias)

tests/core/meta/alias.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2026 Roland Bock
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* * Redistributions of source code must retain the above copyright notice,
9+
* this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above copyright notice,
11+
* this list of conditions and the following disclaimer in the documentation
12+
* and/or other materials provided with the distribution.
13+
*
14+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24+
* POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
#include <sqlpp23/tests/core/all.h>
28+
29+
int main(int, char*[]) {
30+
#if SQLPP_INCLUDE_REFLECTION == 1
31+
const auto foo = test::TabFoo{};
32+
const auto bar = test::TabBar{};
33+
34+
SQLPP_COMPARE(avg(bar.id + 7).as<"my_average">(),
35+
"AVG(tab_bar.id + 7) AS my_average");
36+
37+
const auto table_a = foo.as<"table_a">();
38+
const auto table_b = bar.as<"table_b">();
39+
40+
SQLPP_COMPARE(sqlpp::select(table_a.id.as<"id_a">(), table_a.intN,
41+
table_b.id.as<"id_b">(), table_b.textN)
42+
.from(table_a.join(table_b).on(table_a.id == table_b.id)),
43+
"SELECT table_a.id AS id_a, table_a.int_n, table_b.id AS id_b, "
44+
"table_b.text_n "
45+
"FROM tab_foo AS table_a "
46+
"INNER JOIN tab_bar AS table_b "
47+
"ON table_a.id = table_b.id");
48+
49+
sqlpp::mock_db::connection db = sqlpp::mock_db::make_test_connection();
50+
for (const auto& row :
51+
db(sqlpp::select(table_a.id.as<"id_a">()).from(table_a))) {
52+
std::cout << row.id_a << std::endl;
53+
}
54+
#endif
55+
56+
return 0;
57+
}

0 commit comments

Comments
 (0)