Skip to content

Instantly share code, notes, and snippets.

@mosra
Last active December 17, 2017 15:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mosra/8388582 to your computer and use it in GitHub Desktop.
Save mosra/8388582 to your computer and use it in GitHub Desktop.
cosi, minimal stuff for easier soci + picojson usage

Usage

First, create the named tuple from list of cosi::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.

auto t = cosi::make(
    cosi::string_f  {"data"},
    cosi::int_f     {"integerData", "COUNT(data)"},
    cosi::double_f  {"moreData", "another_table.data"}
);

The cosi::make() function creates new cosi::named_tuple object. Use the created object to add list of fields to soci SQL query and retrieve the data back to it.

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());

You can then retrieve the data from the tuple manually or convert them to picojson object:

// moreData == 7.28
double moreData = std::get<2>(t.data());

// {
//   "data": "foo",
//   "integerData": 42,
//   "moreData": 7.28
// }
const picojson::object json = t.json();
#include "cosi.h"
namespace cosi { namespace detail {
std::string join(const std::string* const begin, const std::string* const end) {
if(begin == end) return {};
/* Compute size of resulting string, count also delimiters */
std::size_t size = 0;
for(const std::string* it = begin; it != end; ++it)
size += it->size() + 2;
size -= 2;
/* Reserve memory for resulting string */
std::string result;
result.reserve(size);
result += *begin;
/* Join strings */
for(const std::string* it = begin+1; it != end; ++it)
(result += ", ") += *it;
return result;
}
}}
#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment