|
#ifndef cosi_h |
|
#define cosi_h |
|
|
|
#include <array> |
|
#include <string> |
|
#include <tuple> |
|
|
|
#include <soci/values.h> |
|
#include <soci/type-conversion-traits.h> |
|
|
|
#include "../external/picojson/picojson.h" |
|
|
|
/** @mainpage |
|
|
|
Minimal stuff for easier `soci` and `picojson` usage. |
|
|
|
@section Usage |
|
|
|
First, create the named tuple from list of @ref field instances. First |
|
parameter is name in resulting JSON, second is optional field name in SQL |
|
query. If not used, the same name is used for both JSON and SQL. |
|
@code |
|
auto t = cosi::make( |
|
cosi::string_f {"data"}, |
|
cosi::int_f {"integerData", "COUNT(data)"}, |
|
cosi::double_f {"moreData", "another_table.data"} |
|
); |
|
@endcode |
|
|
|
The @ref make() function creates new @ref named_tuple object. Use the created |
|
object to add list of fields to `soci` SQL query and retrieve the data back to |
|
it. |
|
@code |
|
soci::session db; |
|
|
|
// t.sql_fields() == "data, COUNT(data), another_table.data" |
|
db << "SELECT " + t.sql_fields() + " FROM one_table, another_table", soci::into(t.data()); |
|
@endcode |
|
|
|
You can then retrieve the data from the tuple manually or convert them to |
|
`picojson` object: |
|
@code |
|
// moreData == 7.28 |
|
double moreData = std::get<2>(t.data()); |
|
|
|
// { |
|
// "data": "foo", |
|
// "integerData": 42, |
|
// "moreData": 7.28 |
|
// } |
|
const picojson::object json = t.json(); |
|
@endcode |
|
*/ |
|
|
|
namespace cosi { |
|
|
|
/** |
|
@brief Named field |
|
|
|
@see @ref string_f, @ref int_f, @ref uint_f, @ref float_f, @ref double_f |
|
*/ |
|
template<class T> class field { |
|
template<class...> friend class named_tuple; |
|
|
|
public: |
|
/** |
|
* @brief Constructor |
|
* @param json_name Field name used in JSON |
|
* @param sql_name Field name used in SQL query |
|
*/ |
|
explicit field(const std::string& json_name, const std::string& sql_name): _sql_name(sql_name), _json_name(json_name) {} |
|
|
|
/** |
|
* @brief Constructor |
|
* @param name Field name used both in JSON and SQL query |
|
*/ |
|
explicit field(const std::string& name): _sql_name(name) {} |
|
|
|
private: |
|
std::string _sql_name, _json_name; |
|
}; |
|
|
|
/** @brief Tuple of field names and values */ |
|
template<class ...T> class named_tuple { |
|
template<class ...U> friend named_tuple<U...> make(field<U>...); |
|
|
|
public: |
|
/** @brief Comma-separated SQL fields */ |
|
std::string sql_fields() const; |
|
|
|
/** @brief Data */ |
|
std::tuple<T...>& data() { return _data; } |
|
std::tuple<T...> data() const { return _data; } /**< @overload */ |
|
|
|
/** @brief Data as JSON */ |
|
picojson::object json() const; |
|
|
|
private: |
|
template<std::size_t n> using nth = std::integral_constant<std::size_t, n>; |
|
|
|
explicit named_tuple(field<T>... fields): _sql_names{{fields._sql_name...}}, _json_names{{fields._json_name...}} {} |
|
|
|
void json_nth(picojson::object&, nth<sizeof...(T)>) const {} /* Recursion stopper */ |
|
|
|
template<std::size_t n> void json_nth(picojson::object& o, nth<n>) const; |
|
|
|
std::array<std::string, sizeof...(T)> _sql_names, _json_names; |
|
std::tuple<T...> _data; |
|
}; |
|
|
|
/** @brief Make named tuple */ |
|
template<class ...T> inline named_tuple<T...> make(field<T>... values) { |
|
return named_tuple<T...>{values...}; |
|
} |
|
|
|
typedef field<std::string> string_f; /**< @brief String field */ |
|
typedef field<std::int32_t> int_f; /**< @brief 32-bit signed integer field */ |
|
typedef field<std::uint32_t> uint_f; /**< @brief 32-bit unsigned integer field */ |
|
typedef field<float> float_f; /**< @brief 32-bit floating-point field */ |
|
typedef field<double> double_f; /**< @brief 64-bit floating-point field */ |
|
|
|
namespace detail { |
|
std::string join(const std::string* begin, const std::string* end); |
|
} |
|
|
|
template<class ...T> inline std::string named_tuple<T...>::sql_fields() const { |
|
return detail::join(_sql_names.begin(), _sql_names.end()); |
|
} |
|
|
|
template<class ...T> picojson::object named_tuple<T...>::json() const { |
|
picojson::object o; |
|
json_nth(o, nth<0>{}); |
|
return o; |
|
} |
|
|
|
template<class ...T> template<std::size_t n> inline void named_tuple<T...>::json_nth(picojson::object& o, nth<n>) const { |
|
o.emplace(_json_names[n].empty() ? _sql_names[n] : _json_names[n], picojson::value{std::get<n>(_data)}); |
|
json_nth(o, nth<n + 1>{}); |
|
} |
|
|
|
} |
|
|
|
namespace soci { |
|
|
|
template<class ...T> struct type_conversion<std::tuple<T...>> { |
|
template<std::size_t n> using nth = std::integral_constant<std::size_t, n>; |
|
typedef values base_type; |
|
|
|
static void from_base(const base_type& in, indicator, std::tuple<T...>& out) { |
|
from_base_nth(in, out, nth<0>{}); |
|
} |
|
|
|
static void to_base(const std::tuple<T...>& in, base_type& out, indicator&) { |
|
to_base_nth(in, out, nth<0>{}); |
|
} |
|
|
|
static void from_base_nth(const base_type&, std::tuple<T...>&, nth<sizeof...(T)>) {} /* Recursion stopper */ |
|
|
|
template<std::size_t n> static void from_base_nth(const base_type& in, std::tuple<T...>& out, nth<n>) { |
|
from_base_nth(in >> std::get<n>(out), out, nth<n+1>{}); |
|
} |
|
|
|
static void to_base_nth(const std::tuple<T...>&, base_type&, nth<sizeof...(T)>) {} /* Recursion stopper */ |
|
|
|
template<std::size_t n> static void to_base_nth(const std::tuple<T...>& in, base_type& out, nth<n>) { |
|
to_base_nth(in, out << std::get<n>(in), nth<n+1>{}); |
|
} |
|
}; |
|
|
|
} |
|
|
|
#endif |