1  
//
1  
//
2  
// Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0.
4  
// Distributed under the Boost Software License, Version 1.0.
5  
// https://www.boost.org/LICENSE_1_0.txt
5  
// https://www.boost.org/LICENSE_1_0.txt
6  
//
6  
//
7  

7  

8  
#ifndef BOOST_HTTP_DB_SCHEMA_HPP
8  
#ifndef BOOST_HTTP_DB_SCHEMA_HPP
9  
#define BOOST_HTTP_DB_SCHEMA_HPP
9  
#define BOOST_HTTP_DB_SCHEMA_HPP
10  

10  

11  
#include <cstdint>
11  
#include <cstdint>
12  
#include <tuple>
12  
#include <tuple>
13  
#include <type_traits>
13  
#include <type_traits>
14  
#include <string_view>
14  
#include <string_view>
15  

15  

16  
namespace boost {
16  
namespace boost {
17  
namespace http {
17  
namespace http {
18  
namespace db {
18  
namespace db {
19  

19  

20  
/** Bitwise flags describing column properties.
20  
/** Bitwise flags describing column properties.
21  

21  

22  
    Accumulated on a @ref field_t descriptor via
22  
    Accumulated on a @ref field_t descriptor via
23  
    builder-style member functions.
23  
    builder-style member functions.
24  
*/
24  
*/
25  
enum field_flags : unsigned
25  
enum field_flags : unsigned
26  
{
26  
{
27  
    flag_none           = 0,
27  
    flag_none           = 0,
28  
    flag_primary_key    = 1 << 0,
28  
    flag_primary_key    = 1 << 0,
29  
    flag_auto_increment = 1 << 1,
29  
    flag_auto_increment = 1 << 1,
30  
    flag_not_null       = 1 << 2,
30  
    flag_not_null       = 1 << 2,
31  
    flag_unique         = 1 << 3,
31  
    flag_unique         = 1 << 3,
32  
    flag_indexed        = 1 << 4
32  
    flag_indexed        = 1 << 4
33  
};
33  
};
34  

34  

35  
/** Describe a single column mapped to a struct member.
35  
/** Describe a single column mapped to a struct member.
36  

36  

37  
    The member pointer is stored as a data member so
37  
    The member pointer is stored as a data member so
38  
    that the natural syntax works without requiring
38  
    that the natural syntax works without requiring
39  
    angle brackets. The entire schema description is
39  
    angle brackets. The entire schema description is
40  
    constexpr and available at compile time.
40  
    constexpr and available at compile time.
41  

41  

42  
    @par Example
42  
    @par Example
43  
    @code
43  
    @code
44  
    field("id", &user::id).primary_key().auto_increment()
44  
    field("id", &user::id).primary_key().auto_increment()
45  
    field("email", &user::email).not_null().unique()
45  
    field("email", &user::email).not_null().unique()
46  
    @endcode
46  
    @endcode
47  

47  

48  
    @tparam T Member value type (e.g. `std::string`).
48  
    @tparam T Member value type (e.g. `std::string`).
49  
    @tparam C Containing class type (e.g. `user`).
49  
    @tparam C Containing class type (e.g. `user`).
50  

50  

51  
    @see field, embed_t, has_one_t
51  
    @see field, embed_t, has_one_t
52  
*/
52  
*/
53  
template <typename T, typename C>
53  
template <typename T, typename C>
54  
struct field_t
54  
struct field_t
55  
{
55  
{
56  
    using value_type = T;
56  
    using value_type = T;
57  
    using class_type = C;
57  
    using class_type = C;
58  

58  

59  
    std::string_view name;
59  
    std::string_view name;
60  
    T C::* pointer;
60  
    T C::* pointer;
61  
    unsigned flags = flag_none;
61  
    unsigned flags = flag_none;
62  

62  

63  
    /// Mark this column as the primary key.
63  
    /// Mark this column as the primary key.
64  
    constexpr field_t& primary_key()    { flags |= flag_primary_key;    return *this; }
64  
    constexpr field_t& primary_key()    { flags |= flag_primary_key;    return *this; }
65  

65  

66  
    /// Mark this column as auto-incrementing.
66  
    /// Mark this column as auto-incrementing.
67  
    constexpr field_t& auto_increment() { flags |= flag_auto_increment; return *this; }
67  
    constexpr field_t& auto_increment() { flags |= flag_auto_increment; return *this; }
68  

68  

69  
    /// Mark this column as NOT NULL.
69  
    /// Mark this column as NOT NULL.
70  
    constexpr field_t& not_null()       { flags |= flag_not_null;       return *this; }
70  
    constexpr field_t& not_null()       { flags |= flag_not_null;       return *this; }
71  

71  

72  
    /// Mark this column as UNIQUE.
72  
    /// Mark this column as UNIQUE.
73  
    constexpr field_t& unique()         { flags |= flag_unique;         return *this; }
73  
    constexpr field_t& unique()         { flags |= flag_unique;         return *this; }
74  

74  

75  
    /// Mark this column as indexed.
75  
    /// Mark this column as indexed.
76  
    constexpr field_t& indexed()        { flags |= flag_indexed;        return *this; }
76  
    constexpr field_t& indexed()        { flags |= flag_indexed;        return *this; }
77  

77  

78  
    /// Return true if this column is a primary key.
78  
    /// Return true if this column is a primary key.
79  
    constexpr bool is_primary_key()    const { return flags & flag_primary_key; }
79  
    constexpr bool is_primary_key()    const { return flags & flag_primary_key; }
80  

80  

81  
    /// Return true if this column is auto-incrementing.
81  
    /// Return true if this column is auto-incrementing.
82  
    constexpr bool is_auto_increment() const { return flags & flag_auto_increment; }
82  
    constexpr bool is_auto_increment() const { return flags & flag_auto_increment; }
83  

83  

84  
    /// Return true if this column is NOT NULL.
84  
    /// Return true if this column is NOT NULL.
85  
    constexpr bool is_not_null()       const { return flags & flag_not_null; }
85  
    constexpr bool is_not_null()       const { return flags & flag_not_null; }
86  

86  

87  
    /// Return true if this column is UNIQUE.
87  
    /// Return true if this column is UNIQUE.
88  
    constexpr bool is_unique()         const { return flags & flag_unique; }
88  
    constexpr bool is_unique()         const { return flags & flag_unique; }
89  

89  

90  
    /// Return true if this column is indexed.
90  
    /// Return true if this column is indexed.
91  
    constexpr bool is_indexed()        const { return flags & flag_indexed; }
91  
    constexpr bool is_indexed()        const { return flags & flag_indexed; }
92  

92  

93  
    /// Return the member value from an object.
93  
    /// Return the member value from an object.
94  
    constexpr T const& get(C const& obj) const
94  
    constexpr T const& get(C const& obj) const
95  
    {
95  
    {
96  
        return obj.*pointer;
96  
        return obj.*pointer;
97  
    }
97  
    }
98  

98  

99  
    /// Set the member value on an object.
99  
    /// Set the member value on an object.
100  
    constexpr void set(C& obj, T const& value) const
100  
    constexpr void set(C& obj, T const& value) const
101  
    {
101  
    {
102  
        obj.*pointer = value;
102  
        obj.*pointer = value;
103  
    }
103  
    }
104  

104  

105  
    /// Set the member value on an object by move.
105  
    /// Set the member value on an object by move.
106  
    constexpr void set(C& obj, T&& value) const
106  
    constexpr void set(C& obj, T&& value) const
107  
    {
107  
    {
108  
        obj.*pointer = static_cast<T&&>(value);
108  
        obj.*pointer = static_cast<T&&>(value);
109  
    }
109  
    }
110  
};
110  
};
111  

111  

112  
/** Create a field descriptor for a member pointer.
112  
/** Create a field descriptor for a member pointer.
113  

113  

114  
    Deduces `T` and `C` from the member pointer so
114  
    Deduces `T` and `C` from the member pointer so
115  
    template arguments are never needed at the call site.
115  
    template arguments are never needed at the call site.
116  

116  

117  
    @par Example
117  
    @par Example
118  
    @code
118  
    @code
119  
    field("email", &user::email)
119  
    field("email", &user::email)
120  
    field("id", &user::id).primary_key().auto_increment()
120  
    field("id", &user::id).primary_key().auto_increment()
121  
    @endcode
121  
    @endcode
122  

122  

123  
    @param name Column name in the database table.
123  
    @param name Column name in the database table.
124  
    @param ptr  Pointer to the mapped data member.
124  
    @param ptr  Pointer to the mapped data member.
125  

125  

126  
    @return A @ref field_t descriptor for the member.
126  
    @return A @ref field_t descriptor for the member.
127  

127  

128  
    @see field_t
128  
    @see field_t
129  
*/
129  
*/
130  
template <typename T, typename C>
130  
template <typename T, typename C>
131  
constexpr auto field(std::string_view name, T C::* ptr)
131  
constexpr auto field(std::string_view name, T C::* ptr)
132  
    -> field_t<T, C>
132  
    -> field_t<T, C>
133  
{
133  
{
134  
    return { name, ptr };
134  
    return { name, ptr };
135  
}
135  
}
136  

136  

137  
/** Describe a nested struct flattened into the parent table.
137  
/** Describe a nested struct flattened into the parent table.
138  

138  

139  
    The nested type must provide its own `tag_invoke`
139  
    The nested type must provide its own `tag_invoke`
140  
    overloads for @ref fields_t. A prefix is prepended
140  
    overloads for @ref fields_t. A prefix is prepended
141  
    to each nested column name to avoid collisions.
141  
    to each nested column name to avoid collisions.
142  

142  

143  
    @par Example
143  
    @par Example
144  
    @code
144  
    @code
145  
    // If user::addr is an address with field "street",
145  
    // If user::addr is an address with field "street",
146  
    // the resulting column is "addr_street".
146  
    // the resulting column is "addr_street".
147  
    embed("addr_", &user::addr)
147  
    embed("addr_", &user::addr)
148  
    @endcode
148  
    @endcode
149  

149  

150  
    @tparam T Nested struct type.
150  
    @tparam T Nested struct type.
151  
    @tparam C Containing class type.
151  
    @tparam C Containing class type.
152  

152  

153  
    @see embed, field_t
153  
    @see embed, field_t
154  
*/
154  
*/
155  
template <typename T, typename C>
155  
template <typename T, typename C>
156  
struct embed_t
156  
struct embed_t
157  
{
157  
{
158  
    using value_type = T;
158  
    using value_type = T;
159  
    using class_type = C;
159  
    using class_type = C;
160  

160  

161  
    std::string_view prefix;
161  
    std::string_view prefix;
162  
    T C::* pointer;
162  
    T C::* pointer;
163  

163  

164  
    /// Return the nested struct from an object.
164  
    /// Return the nested struct from an object.
165  
    constexpr T const& get(C const& obj) const
165  
    constexpr T const& get(C const& obj) const
166  
    {
166  
    {
167  
        return obj.*pointer;
167  
        return obj.*pointer;
168  
    }
168  
    }
169  

169  

170  
    /// Set the nested struct on an object.
170  
    /// Set the nested struct on an object.
171  
    constexpr void set(C& obj, T const& value) const
171  
    constexpr void set(C& obj, T const& value) const
172  
    {
172  
    {
173  
        obj.*pointer = value;
173  
        obj.*pointer = value;
174  
    }
174  
    }
175  

175  

176  
    /// Set the nested struct on an object by move.
176  
    /// Set the nested struct on an object by move.
177  
    constexpr void set(C& obj, T&& value) const
177  
    constexpr void set(C& obj, T&& value) const
178  
    {
178  
    {
179  
        obj.*pointer = static_cast<T&&>(value);
179  
        obj.*pointer = static_cast<T&&>(value);
180  
    }
180  
    }
181  
};
181  
};
182  

182  

183  
/** Create an embedded field descriptor for a nested struct.
183  
/** Create an embedded field descriptor for a nested struct.
184  

184  

185  
    @param prefix String prepended to nested column names.
185  
    @param prefix String prepended to nested column names.
186  
    @param ptr    Pointer to the nested data member.
186  
    @param ptr    Pointer to the nested data member.
187  

187  

188  
    @return An @ref embed_t descriptor.
188  
    @return An @ref embed_t descriptor.
189  

189  

190  
    @see embed_t
190  
    @see embed_t
191  
*/
191  
*/
192  
template <typename T, typename C>
192  
template <typename T, typename C>
193  
constexpr auto embed(std::string_view prefix, T C::* ptr)
193  
constexpr auto embed(std::string_view prefix, T C::* ptr)
194  
    -> embed_t<T, C>
194  
    -> embed_t<T, C>
195  
{
195  
{
196  
    return { prefix, ptr };
196  
    return { prefix, ptr };
197  
}
197  
}
198  

198  

199  
/** Describe a one-to-one relationship to another table.
199  
/** Describe a one-to-one relationship to another table.
200  

200  

201  
    The referenced type must provide its own
201  
    The referenced type must provide its own
202  
    `tag_invoke` overloads for @ref table_name_t
202  
    `tag_invoke` overloads for @ref table_name_t
203  
    and @ref fields_t.
203  
    and @ref fields_t.
204  

204  

205  
    @par Example
205  
    @par Example
206  
    @code
206  
    @code
207  
    has_one("address_id", &user::addr)
207  
    has_one("address_id", &user::addr)
208  
    @endcode
208  
    @endcode
209  

209  

210  
    @tparam T Referenced struct type.
210  
    @tparam T Referenced struct type.
211  
    @tparam C Containing class type.
211  
    @tparam C Containing class type.
212  

212  

213  
    @see has_one, has_many_t
213  
    @see has_one, has_many_t
214  
*/
214  
*/
215  
template <typename T, typename C>
215  
template <typename T, typename C>
216  
struct has_one_t
216  
struct has_one_t
217  
{
217  
{
218  
    using value_type = T;
218  
    using value_type = T;
219  
    using class_type = C;
219  
    using class_type = C;
220  

220  

221  
    std::string_view foreign_key;
221  
    std::string_view foreign_key;
222  
    T C::* pointer;
222  
    T C::* pointer;
223  

223  

224  
    /// Return the related object from the parent.
224  
    /// Return the related object from the parent.
225  
    constexpr T const& get(C const& obj) const
225  
    constexpr T const& get(C const& obj) const
226  
    {
226  
    {
227  
        return obj.*pointer;
227  
        return obj.*pointer;
228  
    }
228  
    }
229  

229  

230  
    /// Set the related object on the parent.
230  
    /// Set the related object on the parent.
231  
    constexpr void set(C& obj, T const& value) const
231  
    constexpr void set(C& obj, T const& value) const
232  
    {
232  
    {
233  
        obj.*pointer = value;
233  
        obj.*pointer = value;
234  
    }
234  
    }
235  

235  

236  
    /// Set the related object on the parent by move.
236  
    /// Set the related object on the parent by move.
237  
    constexpr void set(C& obj, T&& value) const
237  
    constexpr void set(C& obj, T&& value) const
238  
    {
238  
    {
239  
        obj.*pointer = static_cast<T&&>(value);
239  
        obj.*pointer = static_cast<T&&>(value);
240  
    }
240  
    }
241  
};
241  
};
242  

242  

243  
/** Create a one-to-one relationship descriptor.
243  
/** Create a one-to-one relationship descriptor.
244  

244  

245  
    @param foreign_key Column name of the foreign key.
245  
    @param foreign_key Column name of the foreign key.
246  
    @param ptr         Pointer to the related data member.
246  
    @param ptr         Pointer to the related data member.
247  

247  

248  
    @return A @ref has_one_t descriptor.
248  
    @return A @ref has_one_t descriptor.
249  

249  

250  
    @see has_one_t
250  
    @see has_one_t
251  
*/
251  
*/
252  
template <typename T, typename C>
252  
template <typename T, typename C>
253  
constexpr auto has_one(std::string_view foreign_key, T C::* ptr)
253  
constexpr auto has_one(std::string_view foreign_key, T C::* ptr)
254  
    -> has_one_t<T, C>
254  
    -> has_one_t<T, C>
255  
{
255  
{
256  
    return { foreign_key, ptr };
256  
    return { foreign_key, ptr };
257  
}
257  
}
258  

258  

259  
/** Describe a one-to-many relationship.
259  
/** Describe a one-to-many relationship.
260  

260  

261  
    The child type has a member acting as the foreign
261  
    The child type has a member acting as the foreign
262  
    key pointing back to the parent's primary key.
262  
    key pointing back to the parent's primary key.
263  
    The child's foreign key member pointer is stored
263  
    The child's foreign key member pointer is stored
264  
    so the library can build the appropriate JOIN or
264  
    so the library can build the appropriate JOIN or
265  
    subquery.
265  
    subquery.
266  

266  

267  
    @par Example
267  
    @par Example
268  
    @code
268  
    @code
269  
    has_many(&user::posts, &post::user_id)
269  
    has_many(&user::posts, &post::user_id)
270  
    @endcode
270  
    @endcode
271  

271  

272  
    @tparam Collection Container type in the parent
272  
    @tparam Collection Container type in the parent
273  
        (e.g. `std::vector< post >`).
273  
        (e.g. `std::vector< post >`).
274  
    @tparam Parent     Parent class type.
274  
    @tparam Parent     Parent class type.
275  
    @tparam FK         Foreign key value type in the child.
275  
    @tparam FK         Foreign key value type in the child.
276  
    @tparam Child      Child class type.
276  
    @tparam Child      Child class type.
277  

277  

278  
    @see has_many, has_one_t
278  
    @see has_many, has_one_t
279  
*/
279  
*/
280  
template <typename Collection, typename Parent, typename FK, typename Child>
280  
template <typename Collection, typename Parent, typename FK, typename Child>
281  
struct has_many_t
281  
struct has_many_t
282  
{
282  
{
283  
    using collection_type = Collection;
283  
    using collection_type = Collection;
284  
    using parent_type     = Parent;
284  
    using parent_type     = Parent;
285  
    using child_type      = Child;
285  
    using child_type      = Child;
286  
    using fk_value_type   = FK;
286  
    using fk_value_type   = FK;
287  

287  

288  
    Collection Parent::* pointer;
288  
    Collection Parent::* pointer;
289  
    FK Child::* foreign_key;
289  
    FK Child::* foreign_key;
290  
};
290  
};
291  

291  

292  
/** Create a one-to-many relationship descriptor.
292  
/** Create a one-to-many relationship descriptor.
293  

293  

294  
    @param ptr Pointer to the collection member in the parent.
294  
    @param ptr Pointer to the collection member in the parent.
295  
    @param fk  Pointer to the foreign key member in the child.
295  
    @param fk  Pointer to the foreign key member in the child.
296  

296  

297  
    @return A @ref has_many_t descriptor.
297  
    @return A @ref has_many_t descriptor.
298  

298  

299  
    @see has_many_t
299  
    @see has_many_t
300  
*/
300  
*/
301  
template <
301  
template <
302  
    typename Collection, typename Parent,
302  
    typename Collection, typename Parent,
303  
    typename FK, typename Child>
303  
    typename FK, typename Child>
304  
constexpr auto has_many(
304  
constexpr auto has_many(
305  
    Collection Parent::* ptr,
305  
    Collection Parent::* ptr,
306  
    FK Child::* fk)
306  
    FK Child::* fk)
307  
    -> has_many_t<Collection, Parent, FK, Child>
307  
    -> has_many_t<Collection, Parent, FK, Child>
308  
{
308  
{
309  
    return { ptr, fk };
309  
    return { ptr, fk };
310  
}
310  
}
311  

311  

312  
/** Tag type for retrieving the table name of a mapped type.
312  
/** Tag type for retrieving the table name of a mapped type.
313  

313  

314  
    Customize via `tag_invoke`:
314  
    Customize via `tag_invoke`:
315  

315  

316  
    @par Example
316  
    @par Example
317  
    @code
317  
    @code
318  
    constexpr auto tag_invoke(
318  
    constexpr auto tag_invoke(
319  
        db::table_name_t, user const&)
319  
        db::table_name_t, user const&)
320  
    {
320  
    {
321  
        return "users";
321  
        return "users";
322  
    }
322  
    }
323  
    @endcode
323  
    @endcode
324  

324  

325  
    @see fields_t, HasMapping
325  
    @see fields_t, HasMapping
326  
*/
326  
*/
327  
struct table_name_t
327  
struct table_name_t
328  
{
328  
{
329  
    template <typename T>
329  
    template <typename T>
330  
    constexpr auto operator()(T const& v) const
330  
    constexpr auto operator()(T const& v) const
331  
    {
331  
    {
332  
        return tag_invoke(*this, v);
332  
        return tag_invoke(*this, v);
333  
    }
333  
    }
334  
};
334  
};
335  

335  

336  
/** Tag type for retrieving the field descriptors of a mapped type.
336  
/** Tag type for retrieving the field descriptors of a mapped type.
337  

337  

338  
    Customize via `tag_invoke`:
338  
    Customize via `tag_invoke`:
339  

339  

340  
    @par Example
340  
    @par Example
341  
    @code
341  
    @code
342  
    constexpr auto tag_invoke(
342  
    constexpr auto tag_invoke(
343  
        db::fields_t, user const&)
343  
        db::fields_t, user const&)
344  
    {
344  
    {
345  
        return std::tuple(
345  
        return std::tuple(
346  
            db::field("id", &user::id)
346  
            db::field("id", &user::id)
347  
                .primary_key().auto_increment(),
347  
                .primary_key().auto_increment(),
348  
            db::field("email", &user::email)
348  
            db::field("email", &user::email)
349  
                .not_null().unique(),
349  
                .not_null().unique(),
350  
            db::field("name", &user::name));
350  
            db::field("name", &user::name));
351  
    }
351  
    }
352  
    @endcode
352  
    @endcode
353  

353  

354  
    @see table_name_t, HasMapping
354  
    @see table_name_t, HasMapping
355  
*/
355  
*/
356  
struct fields_t
356  
struct fields_t
357  
{
357  
{
358  
    template <typename T>
358  
    template <typename T>
359  
    constexpr auto operator()(T const& v) const
359  
    constexpr auto operator()(T const& v) const
360  
    {
360  
    {
361  
        return tag_invoke(*this, v);
361  
        return tag_invoke(*this, v);
362  
    }
362  
    }
363  
};
363  
};
364  

364  

365  
/// Customization point object for @ref table_name_t.
365  
/// Customization point object for @ref table_name_t.
366  
inline constexpr table_name_t table_name{};
366  
inline constexpr table_name_t table_name{};
367  

367  

368  
/// Customization point object for @ref fields_t.
368  
/// Customization point object for @ref fields_t.
369  
inline constexpr fields_t     fields{};
369  
inline constexpr fields_t     fields{};
370  

370  

371  
/** Concept for types with a complete schema mapping.
371  
/** Concept for types with a complete schema mapping.
372  

372  

373  
    A conforming type must provide `tag_invoke`
373  
    A conforming type must provide `tag_invoke`
374  
    overloads for both @ref table_name_t and
374  
    overloads for both @ref table_name_t and
375  
    @ref fields_t.
375  
    @ref fields_t.
376  

376  

377  
    @par Syntactic Requirements
377  
    @par Syntactic Requirements
378  
    @li `tag_invoke( table_name, v )` is convertible
378  
    @li `tag_invoke( table_name, v )` is convertible
379  
        to `std::string_view`.
379  
        to `std::string_view`.
380  
    @li `tag_invoke( fields, v )` is a valid expression.
380  
    @li `tag_invoke( fields, v )` is a valid expression.
381  

381  

382  
    @tparam T The type to check for a schema mapping.
382  
    @tparam T The type to check for a schema mapping.
383  

383  

384  
    @see table_name_t, fields_t
384  
    @see table_name_t, fields_t
385  
*/
385  
*/
386  
template <typename T>
386  
template <typename T>
387  
concept HasMapping = requires(T const& v)
387  
concept HasMapping = requires(T const& v)
388  
{
388  
{
389  
    { tag_invoke(table_name, v) } -> std::convertible_to<std::string_view>;
389  
    { tag_invoke(table_name, v) } -> std::convertible_to<std::string_view>;
390  
    { tag_invoke(fields,     v) };
390  
    { tag_invoke(fields,     v) };
391  
};
391  
};
392  

392  

393  
namespace detail {
393  
namespace detail {
394  

394  

395  
template <typename Tuple, typename F, std::size_t... Is>
395  
template <typename Tuple, typename F, std::size_t... Is>
396  
constexpr void for_each_impl(
396  
constexpr void for_each_impl(
397  
    Tuple const& t, F&& f, std::index_sequence<Is...>)
397  
    Tuple const& t, F&& f, std::index_sequence<Is...>)
398  
{
398  
{
399  
    (f(std::get<Is>(t)), ...);
399  
    (f(std::get<Is>(t)), ...);
400  
}
400  
}
401  

401  

402  
} // namespace detail
402  
} // namespace detail
403  

403  

404  
/** Invoke a callable for each field in a mapped type.
404  
/** Invoke a callable for each field in a mapped type.
405  

405  

406  
    @param f Callable invoked as `f( field_descriptor )`
406  
    @param f Callable invoked as `f( field_descriptor )`
407  
        for every field in `T`'s mapping.
407  
        for every field in `T`'s mapping.
408  

408  

409  
    @tparam T A type satisfying @ref HasMapping.
409  
    @tparam T A type satisfying @ref HasMapping.
410  

410  

411  
    @see field_count
411  
    @see field_count
412  
*/
412  
*/
413  
template <HasMapping T, typename F>
413  
template <HasMapping T, typename F>
414  
constexpr void for_each_field(F&& f)
414  
constexpr void for_each_field(F&& f)
415  
{
415  
{
416  
    constexpr auto fs = tag_invoke(fields, T{});
416  
    constexpr auto fs = tag_invoke(fields, T{});
417  
    detail::for_each_impl(
417  
    detail::for_each_impl(
418  
        fs,
418  
        fs,
419  
        static_cast<F&&>(f),
419  
        static_cast<F&&>(f),
420  
        std::make_index_sequence<
420  
        std::make_index_sequence<
421  
            std::tuple_size_v<decltype(fs)>>{});
421  
            std::tuple_size_v<decltype(fs)>>{});
422  
}
422  
}
423  

423  

424  
/** Return the number of fields in a mapped type.
424  
/** Return the number of fields in a mapped type.
425  

425  

426  
    @tparam T A type satisfying @ref HasMapping.
426  
    @tparam T A type satisfying @ref HasMapping.
427  

427  

428  
    @return The compile-time field count.
428  
    @return The compile-time field count.
429  

429  

430  
    @see for_each_field
430  
    @see for_each_field
431  
*/
431  
*/
432  
template <HasMapping T>
432  
template <HasMapping T>
433  
constexpr std::size_t field_count()
433  
constexpr std::size_t field_count()
434  
{
434  
{
435  
    constexpr auto fs = tag_invoke(fields, T{});
435  
    constexpr auto fs = tag_invoke(fields, T{});
436  
    return std::tuple_size_v<decltype(fs)>;
436  
    return std::tuple_size_v<decltype(fs)>;
437  
}
437  
}
438  

438  

439  
} // namespace db
439  
} // namespace db
440  
} // namespace http
440  
} // namespace http
441  
} // namespace boost
441  
} // namespace boost
442  

442  

443  
#endif
443  
#endif