-
-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathexample-dao.cpp
110 lines (92 loc) · 3.1 KB
/
example-dao.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* ***README***
* This example shows how attributes can be used to build powerful
* abstractions. Here we utilize two user-defined attributes to
* generate SQL queries which insert elements into a database.
* The SQL queries are generated all at compile-time using const_strings.
*/
#include <cstdint>
#include <sstream>
#include <iostream>
#include "refl.hpp"
/** An attribute for specifying a DB table's properties */
struct Table : refl::attr::usage::type
{
const char* name;
constexpr Table(const char* name) noexcept
: name(name)
{
}
};
enum class DataType
{
ID,
TEXT,
};
/** An attribute for specifying a DB column's properties */
struct Column : refl::attr::usage::field
{
const char* name;
const DataType data_type;
constexpr Column(const char* name, DataType dataType) noexcept
: name(name), data_type(dataType)
{ }
};
/** User will serve as the "model" for the "Users table" */
struct User
{
std::uint32_t id;
std::string email;
};
/** Define the reflection metadata for the model */
REFL_AUTO(
type(User, Table{"Users"}),
field(id, Column{"ID", DataType::ID}),
field(email, Column{"Email", DataType::TEXT})
);
template <typename Member>
constexpr auto make_sql_field_spec(Member)
{
using namespace refl;
constexpr auto col = descriptor::get_attribute<Column>(Member{});
if constexpr (DataType::ID == col.data_type) {
// convert the const char* data member to a refl::const_string<N>
// (necessary to be able to do compile-time string concat)
return REFL_MAKE_CONST_STRING(col.name) + " int PRIMARY KEY";
}
else if constexpr (DataType::TEXT == col.data_type) {
return REFL_MAKE_CONST_STRING(col.name) + " TEXT";
}
}
/**
* Creates a CREATE TABLE SQL query.
* Returns the query as a refl::const_string<N>.
*/
template <typename T>
constexpr auto make_sql_create_table()
{
using namespace refl;
using Td = type_descriptor<T>;
// Verify that the type has the Table attribute.
static_assert(descriptor::has_attribute<Table>(Td{}));
// Concatenate all the members' column definitions together
constexpr auto fields = accumulate(Td::members,
[](auto acc, auto member) {
return acc + ",\n\t" + make_sql_field_spec(member);
}, make_const_string())
.template substr<2>(); // remove the initial ",\n"
constexpr auto tbl = descriptor::get_attribute<Table>(Td{});
// convert the const char* data member to a refl::const_string<N>
// (necessary to be able to do compile-time string concat)
constexpr auto tbl_name = REFL_MAKE_CONST_STRING(tbl.name);
// return the resulting compile-time string
return "CREATE TABLE " + tbl_name + " (\n" + fields + "\n);";
}
int main()
{
// sql is of type const_string<N> where N is the length of the
// compile-time string.
constexpr auto sql = make_sql_create_table<User>();
std::cout << sql << "\n";
std::cout << "Number of characters: " << sizeof(sql) - 1 << "\n";
}