Skip to content

Commit

Permalink
Add automatic conversions in get<>
Browse files Browse the repository at this point in the history
  • Loading branch information
Sildra committed Oct 22, 2023
1 parent 9bcc5f8 commit a360be9
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 34 deletions.
6 changes: 4 additions & 2 deletions include/soci/row.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ class SOCI_DECL row
indicator get_indicator(std::string const& name) const;

template <typename T>
inline void add_holder(T* t, indicator* ind)
inline T* add_holder(indicator* ind)
{
holders_.push_back(new details::type_holder<T>(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;
Expand Down
3 changes: 1 addition & 2 deletions include/soci/statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,8 @@ class SOCI_DECL statement_impl
template<typename T>
void into_row()
{
T * t = new T();
indicator * ind = new indicator(i_ok);
row_->add_holder(t, ind);
T* t = row_->add_holder<T>(ind);
exchange_for_row(into(*t, *ind));
}

Expand Down
216 changes: 186 additions & 30 deletions include/soci/type-holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <cstring>
#include <ctime>
#include <typeinfo>
#include <type_traits>
#include <limits>

#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<typename T, typename U, typename Enable = void>
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<typename T, typename U>
struct soci_cast<T, U, typename std::enable_if<std::is_same<T, U>::value>::type>
{ static inline T cast(U val) { return val; } };

// Type safe conversion that is widening
template<typename T, typename U>
struct soci_cast<T, U, typename std::enable_if<((!std::is_same<T, U>::value) &&
std::is_integral<T>::value && std::is_integral<U>::value &&
(uintmax_t)std::numeric_limits<T>::max() >= (uintmax_t)std::numeric_limits<U>::max() &&
(intmax_t)std::numeric_limits<T>::min() <= (intmax_t)std::numeric_limits<U>::min())>::type>
{ static inline T cast(U val) { return static_cast<T>(val); } };

#ifdef SOCI_WIDENING_CASTS
// Type safe integral conversion that can narrow the min side
template<typename T, typename U>
struct soci_cast<T, U, typename std::enable_if<(
std::is_integral<T>::value && std::is_integral<U>::value &&
(uintmax_t)std::numeric_limits<T>::max() >= (uintmax_t)std::numeric_limits<U>::max() &&
(intmax_t)std::numeric_limits<T>::min() > (intmax_t)std::numeric_limits<U>::min())>::type>
{
static inline T cast(U val)
{
if ((intmax_t)std::numeric_limits<T>::min() > (intmax_t)val)
throw type_holder_bad_cast("narrowing cast on the min side");
return static_cast<T>(val);
}
};

namespace details
// Type safe integral conversion that can narrow the max side
template<typename T, typename U>
struct soci_cast<T, U, typename std::enable_if<(
std::is_integral<T>::value && std::is_integral<U>::value &&
(uintmax_t)std::numeric_limits<T>::max() < (uintmax_t)std::numeric_limits<U>::max() &&
(intmax_t)std::numeric_limits<T>::min() <= (intmax_t)std::numeric_limits<U>::min())>::type>
{
static inline T cast(U val)
{
if ((uintmax_t)std::numeric_limits<T>::max() < (uintmax_t)val)
throw type_holder_bad_cast("narrowing cast on the max side");
return static_cast<T>(val);
}
};

// Type safe integral conversion that can narrow on both sides
template<typename T, typename U>
struct soci_cast<T, U, typename std::enable_if<(
std::is_integral<T>::value && std::is_integral<U>::value &&
(uintmax_t)std::numeric_limits<T>::max() < (uintmax_t)std::numeric_limits<U>::max() &&
(intmax_t)std::numeric_limits<T>::min() > (intmax_t)std::numeric_limits<U>::min())>::type>
{
static inline T cast(U val)
{
T ret = static_cast<T>(val);
if (static_cast<U>(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<typename T, typename U>
struct soci_cast<T, U, typename std::enable_if <!std::is_same<T, U>::value &&
(std::is_floating_point<T>::value || std::is_floating_point<U>::value)>::type>
{
static inline T cast(U val)
{
T ret = static_cast<T>(val);
if (static_cast<U>(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
Expand Down Expand Up @@ -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 <typename T>
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<typename T>
static holder* make_holder(T*& val);
~holder();

template<typename T>
T get()
{
type_holder<T>* p = checked_ptr_cast<type_holder<T> >(this);
if (p)
switch (dt)
{
return p->template value<T>();
}
else
{
throw std::bad_cast();
case soci::dt_string: return soci_cast<T, std::string>::cast(val.s);
case soci::dt_date: return soci_cast<T, std::tm>::cast(val.t);
case soci::dt_double: return soci_cast<T, double>::cast(val.d);
case soci::dt_integer: return soci_cast<T, int32_t>::cast(val.i32);
case soci::dt_long_long: return soci_cast<T, int64_t>::cast(val.i64);
case soci::dt_unsigned_long_long: return soci_cast<T, uint64_t>::cast(val.ui64);
case soci::dt_blob: return soci_cast<T, std::string>::cast(val.s);
case soci::dt_xml: return soci_cast<T, std::string>::cast(val.s);
default: throw type_holder_bad_cast(dt, typeid(T));
}
}

private:

template<typename T>
T value();
holder(soci::data_type dt_);
const soci::data_type dt;
type_holder val;
};

template <typename T>
class type_holder : public holder
template<typename T>
struct type_holder_raw_get
{
public:
type_holder(T * t) : t_(t) {}
~type_holder() override { delete t_; }

template<typename TypeValue>
TypeValue value() const { return *t_; }

private:
T * t_;
static_assert(std::is_same<T, void>::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<std::string> { 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<int8_t> { 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<int16_t> { 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<int32_t> { 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<int64_t> { 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<uint8_t> { 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<uint16_t> { 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<uint32_t> { 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<uint64_t> { 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<double> { static const data_type type = soci::dt_double; static inline double* get(type_holder& val) { return &val.d; } };
template<> struct type_holder_raw_get<std::tm> { static const data_type type = soci::dt_date; static inline std::tm* get(type_holder& val) { return &val.t; } };

template<typename T>
holder* holder::make_holder(T*& val) {
holder* h = new holder(type_holder_raw_get<T>::type);
val = type_holder_raw_get<T>::get(h->val);
return h;
}

} // namespace details

Expand Down

0 comments on commit a360be9

Please sign in to comment.