From a360be98f8c7141291b0bebc44a49eb2804d41cf Mon Sep 17 00:00:00 2001 From: Thibault FRESNET Date: Sun, 22 Oct 2023 17:00:50 +0200 Subject: [PATCH] Add automatic conversions in get<> --- include/soci/row.h | 6 +- include/soci/statement.h | 3 +- include/soci/type-holder.h | 216 +++++++++++++++++++++++++++++++------ 3 files changed, 191 insertions(+), 34 deletions(-) diff --git a/include/soci/row.h b/include/soci/row.h index 8cd816dc4..d9d0c30d2 100644 --- a/include/soci/row.h +++ b/include/soci/row.h @@ -55,10 +55,12 @@ class SOCI_DECL row indicator get_indicator(std::string const& name) const; template - inline void add_holder(T* t, indicator* ind) + inline T* add_holder(indicator* ind) { - holders_.push_back(new details::type_holder(t)); + T* val = nullptr; + holders_.push_back(details::holder::make_holder(val)); indicators_.push_back(ind); + return val; } column_properties const& get_properties(std::size_t pos) const; diff --git a/include/soci/statement.h b/include/soci/statement.h index b356762e0..c841fef12 100644 --- a/include/soci/statement.h +++ b/include/soci/statement.h @@ -138,9 +138,8 @@ class SOCI_DECL statement_impl template void into_row() { - T * t = new T(); indicator * ind = new indicator(i_ok); - row_->add_holder(t, ind); + T* t = row_->add_holder(ind); exchange_for_row(into(*t, *ind)); } diff --git a/include/soci/type-holder.h b/include/soci/type-holder.h index f103edb58..69076a0a0 100644 --- a/include/soci/type-holder.h +++ b/include/soci/type-holder.h @@ -8,17 +8,141 @@ #ifndef SOCI_TYPE_HOLDER_H_INCLUDED #define SOCI_TYPE_HOLDER_H_INCLUDED -#include "soci/soci-platform.h" +#include "soci/soci-backend.h" // std -#include +#include #include +#include +#include + +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif namespace soci { +class type_holder_bad_cast : public std::bad_cast +{ +public: + type_holder_bad_cast(soci::data_type dt, const std::type_info& target) + { + info = std::bad_cast::what(); + switch (dt) + { + case soci::dt_string: info.append(": no cast defined from std::string to "); break; + case soci::dt_date: info.append(": no cast defined from std::tm to "); break; + case soci::dt_double: info.append(": no cast defined from double to "); break; + case soci::dt_integer: info.append(": no cast defined from int to "); break; + case soci::dt_long_long: info.append(": no cast defined from long long to "); break; + case soci::dt_unsigned_long_long: info.append(": no cast defined from unsigned long long to "); break; + case soci::dt_blob: info.append(": no cast defined from std::string(blob) to "); break; + case soci::dt_xml: info.append(": no cast defined from std::string(xml) to "); break; + default: info.append(": no cast defined from unknown data_type(").append(std::to_string((int)dt)).append(") to "); break; + } + info.append(target.name()); + } + type_holder_bad_cast(const std::type_info& source, const std::type_info& target) + { + info = std::bad_cast::what(); + info.append(": no cast defined from ").append(source.name()).append(" to ").append(target.name()); + } + type_holder_bad_cast(const std::string& message) + { + info = std::bad_cast::what(); + info.append(": ").append(message); + } + const char* what() const noexcept override + { + return info.c_str(); + } +private: + std::string info; +}; + +// Type safe conversion that fails at compilation if instanciated +template +struct soci_cast { static inline T cast(U) +{ throw type_holder_bad_cast(typeid(T), typeid(U)); } }; +// Type safe conversion that is a noop +template +struct soci_cast::value>::type> +{ static inline T cast(U val) { return val; } }; + +// Type safe conversion that is widening +template +struct soci_cast::value) && + std::is_integral::value && std::is_integral::value && + (uintmax_t)std::numeric_limits::max() >= (uintmax_t)std::numeric_limits::max() && + (intmax_t)std::numeric_limits::min() <= (intmax_t)std::numeric_limits::min())>::type> +{ static inline T cast(U val) { return static_cast(val); } }; + +#ifdef SOCI_WIDENING_CASTS +// Type safe integral conversion that can narrow the min side +template +struct soci_cast::value && std::is_integral::value && + (uintmax_t)std::numeric_limits::max() >= (uintmax_t)std::numeric_limits::max() && + (intmax_t)std::numeric_limits::min() > (intmax_t)std::numeric_limits::min())>::type> +{ + static inline T cast(U val) + { + if ((intmax_t)std::numeric_limits::min() > (intmax_t)val) + throw type_holder_bad_cast("narrowing cast on the min side"); + return static_cast(val); + } +}; -namespace details +// Type safe integral conversion that can narrow the max side +template +struct soci_cast::value && std::is_integral::value && + (uintmax_t)std::numeric_limits::max() < (uintmax_t)std::numeric_limits::max() && + (intmax_t)std::numeric_limits::min() <= (intmax_t)std::numeric_limits::min())>::type> { + static inline T cast(U val) + { + if ((uintmax_t)std::numeric_limits::max() < (uintmax_t)val) + throw type_holder_bad_cast("narrowing cast on the max side"); + return static_cast(val); + } +}; +// Type safe integral conversion that can narrow on both sides +template +struct soci_cast::value && std::is_integral::value && + (uintmax_t)std::numeric_limits::max() < (uintmax_t)std::numeric_limits::max() && + (intmax_t)std::numeric_limits::min() > (intmax_t)std::numeric_limits::min())>::type> +{ + static inline T cast(U val) + { + T ret = static_cast(val); + if (static_cast(ret) != val) + throw type_holder_bad_cast("narrowing cast on the min or max side"); + return ret; + } +}; + +// Type safe conversion involving at least one floating-point value +template +struct soci_cast::value && + (std::is_floating_point::value || std::is_floating_point::value)>::type> +{ + static inline T cast(U val) + { + T ret = static_cast(val); + if (static_cast(ret) != val) + throw type_holder_bad_cast("cast lost floating precision"); + return ret; + } +}; +#endif + +namespace details +{ // Returns U* as T*, if the dynamic type of the pointer is really T. // // This should be used instead of dynamic_cast<> because using it doesn't work @@ -46,48 +170,80 @@ T* checked_ptr_cast(U* ptr) // Base class holder + derived class type_holder for storing type data // instances in a container of holder objects -template -class type_holder; +union SOCI_DECL type_holder +{ + type_holder() {}; + ~type_holder() {} + std::string s; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + uint8_t ui8; + uint16_t ui16; + uint32_t ui32; + uint64_t ui64; + double d; + std::tm t = {}; +}; -class holder +class SOCI_DECL holder { public: - holder() {} - virtual ~holder() {} + template + static holder* make_holder(T*& val); + ~holder(); template T get() { - type_holder* p = checked_ptr_cast >(this); - if (p) + switch (dt) { - return p->template value(); - } - else - { - throw std::bad_cast(); + case soci::dt_string: return soci_cast::cast(val.s); + case soci::dt_date: return soci_cast::cast(val.t); + case soci::dt_double: return soci_cast::cast(val.d); + case soci::dt_integer: return soci_cast::cast(val.i32); + case soci::dt_long_long: return soci_cast::cast(val.i64); + case soci::dt_unsigned_long_long: return soci_cast::cast(val.ui64); + case soci::dt_blob: return soci_cast::cast(val.s); + case soci::dt_xml: return soci_cast::cast(val.s); + default: throw type_holder_bad_cast(dt, typeid(T)); } } - private: - - template - T value(); + holder(soci::data_type dt_); + const soci::data_type dt; + type_holder val; }; -template -class type_holder : public holder +template +struct type_holder_raw_get { -public: - type_holder(T * t) : t_(t) {} - ~type_holder() override { delete t_; } - - template - TypeValue value() const { return *t_; } - -private: - T * t_; + static_assert(std::is_same::value, "Unmatched raw type"); + static const data_type type = 0; + static inline T get(type_holder& val) + { + throw std::bad_cast(); // Unreachable: only provided for compilation corectness + } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_string; static inline std::string* get(type_holder& val) { return &val.s; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_integer; static inline int8_t* get(type_holder& val) { return &val.i8; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_integer; static inline int16_t* get(type_holder& val) { return &val.i16; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_integer; static inline int32_t* get(type_holder& val) { return &val.i32; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_long_long; static inline int64_t* get(type_holder& val) { return &val.i64; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_integer; static inline uint8_t* get(type_holder& val) { return &val.ui8; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_integer; static inline uint16_t* get(type_holder& val) { return &val.ui16; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_integer; static inline uint32_t* get(type_holder& val) { return &val.ui32; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_unsigned_long_long; static inline uint64_t* get(type_holder& val) { return &val.ui64; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_double; static inline double* get(type_holder& val) { return &val.d; } }; +template<> struct type_holder_raw_get { static const data_type type = soci::dt_date; static inline std::tm* get(type_holder& val) { return &val.t; } }; + +template +holder* holder::make_holder(T*& val) { + holder* h = new holder(type_holder_raw_get::type); + val = type_holder_raw_get::get(h->val); + return h; +} } // namespace details