Skip to content

Commit

Permalink
Add automatic type conversion to row::get()
Browse files Browse the repository at this point in the history
  • Loading branch information
zann1x committed Feb 29, 2024
1 parent 8ddddca commit c0355f2
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 31 deletions.
2 changes: 1 addition & 1 deletion include/soci/row.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class SOCI_DECL row
template <typename T>
inline void add_holder(T* t, indicator* ind)
{
holders_.push_back(new details::type_holder<T>(t));
holders_.push_back(details::holder::make_holder(t));
indicators_.push_back(ind);
}

Expand Down
3 changes: 3 additions & 0 deletions include/soci/soci-platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
//base class must have dll interface
#pragma warning(disable:4251 4275)

// As long as we don't require C++17, we must disable the warning
// "conditional expression is constant"
#pragma warning(disable:4127)

// Define if you have the vsnprintf variants.
#if _MSC_VER < 1500
Expand Down
325 changes: 296 additions & 29 deletions include/soci/type-holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
#ifndef SOCI_TYPE_HOLDER_H_INCLUDED
#define SOCI_TYPE_HOLDER_H_INCLUDED

#include "soci/soci-backend.h"
#include "soci/soci-platform.h"
// std
#include <cstring>
#include "soci/soci-types.h"

#include <cmath>
#include <cstdint>
#include <ctime>
#include <limits>
#include <type_traits>
#include <typeinfo>

namespace soci
Expand Down Expand Up @@ -44,49 +50,310 @@ T* checked_ptr_cast(U* ptr)
return static_cast<T*>(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;

class holder
// Type safe conversion that fails at compilation if instantiated
template <typename T, typename U, typename Enable = void>
struct soci_cast
{
public:
holder() {}
virtual ~holder() {}
static inline T cast(U)
{
throw std::bad_cast();
}
};

template<typename T>
T get()
// 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)
{
type_holder<T>* p = checked_ptr_cast<type_holder<T> >(this);
if (p)
{
return p->template value<T>();
}
else
return val;
}
};

// Type safe conversion that is widening the type
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
)>::type>
{
static inline T cast(U val) {
intmax_t t_min = static_cast<intmax_t>((std::numeric_limits<T>::min)());
intmax_t u_min = static_cast<intmax_t>((std::numeric_limits<U>::min)());
uintmax_t t_max = static_cast<uintmax_t>((std::numeric_limits<T>::max)());
uintmax_t u_max = static_cast<uintmax_t>((std::numeric_limits<U>::max)());

if ((t_min > u_min && val < static_cast<U>(t_min)) ||
(t_max < u_max && val > static_cast<U>(t_max)))
{
throw std::bad_cast();
}
}

private:
return static_cast<T>(val);
}
};

template<typename T>
T value();
union type_holder
{
std::string* s;
int8_t* i8;
int16_t* i16;
int32_t* i32;
int64_t* i64;
uint8_t* u8;
uint16_t* u16;
uint32_t* u32;
uint64_t* u64;
double* d;
std::tm* t;
};

template <typename T>
class type_holder : public holder
struct type_holder_trait
{
static_assert(std::is_same<T, void>::value, "Unmatched raw type");
// dummy value to satisfy the template engine, never used
static const db_type type = (db_type)0;
};

template <>
struct type_holder_trait<std::string>
{
static const db_type type = db_string;
};

template <>
struct type_holder_trait<int8_t>
{
static const db_type type = db_int8;
};

template <>
struct type_holder_trait<int16_t>
{
static const db_type type = db_int16;
};

template <>
struct type_holder_trait<int32_t>
{
static const db_type type = db_int32;
};

template <>
struct type_holder_trait<int64_t>
{
static const db_type type = db_int64;
};

template <>
struct type_holder_trait<uint8_t>
{
static const db_type type = db_uint8;
};

template <>
struct type_holder_trait<uint16_t>
{
static const db_type type = db_uint16;
};

template <>
struct type_holder_trait<uint32_t>
{
static const db_type type = db_uint32;
};

template <>
struct type_holder_trait<uint64_t>
{
static const db_type type = db_uint64;
};

#if defined(SOCI_INT64_IS_LONG)
template <>
struct type_holder_trait<long long> : type_holder_trait<int64_t>
{
};

template <>
struct type_holder_trait<unsigned long long> : type_holder_trait<uint64_t>
{
};
#elif defined(SOCI_LONG_IS_64_BIT)
template <>
struct type_holder_trait<long> : type_holder_trait<int64_t>
{
};

template <>
struct type_holder_trait<unsigned long> : type_holder_trait<uint64_t>
{
};
#else
template <>
struct type_holder_trait<long> : type_holder_trait<int32_t>
{
};

template <>
struct type_holder_trait<unsigned long> : type_holder_trait<uint32_t>
{
};
#endif

template <>
struct type_holder_trait<double>
{
static const db_type type = db_double;
};

template <>
struct type_holder_trait<std::tm>
{
static const db_type type = db_date;
};

// Base class for storing type data instances in a container of holder objects
class holder
{
public:
type_holder(T * t) : t_(t) {}
~type_holder() override { delete t_; }
template <typename T>
static holder* make_holder(T* val)
{
return new holder(type_holder_trait<T>::type, val);
}

template<typename TypeValue>
TypeValue value() const { return *t_; }
~holder()
{
switch (dt_)
{
case db_double:
delete val_.d;
break;
case db_int8:
delete val_.i8;
break;
case db_int16:
delete val_.i16;
break;
case db_int32:
delete val_.i32;
break;
case db_int64:
delete val_.i64;
break;
case db_uint8:
delete val_.u8;
break;
case db_uint16:
delete val_.u16;
break;
case db_uint32:
delete val_.u32;
break;
case db_uint64:
delete val_.u64;
break;
case db_date:
delete val_.t;
break;
case db_blob:
case db_xml:
case db_string:
delete val_.s;
break;
default:
break;
}
}

template <typename T>
T get()
{
switch (dt_)
{
case db_int8:
return soci_cast<T, int8_t>::cast(*val_.i8);
case db_int16:
return soci_cast<T, int16_t>::cast(*val_.i16);
case db_int32:
return soci_cast<T, int32_t>::cast(*val_.i32);
case db_int64:
return soci_cast<T, int64_t>::cast(*val_.i64);
case db_uint8:
return soci_cast<T, uint8_t>::cast(*val_.u8);
case db_uint16:
return soci_cast<T, uint16_t>::cast(*val_.u16);
case db_uint32:
return soci_cast<T, uint32_t>::cast(*val_.u32);
case db_uint64:
return soci_cast<T, uint64_t>::cast(*val_.u64);
case db_double:
return soci_cast<T, double>::cast(*val_.d);
case db_date:
return soci_cast<T, std::tm>::cast(*val_.t);
case db_blob:
case db_xml:
case db_string:
return soci_cast<T, std::string>::cast(*val_.s);
default:
throw std::bad_cast();
}
}

private:
T * t_;
holder(db_type dt, void* val) : dt_(dt)
{
switch (dt_)
{
case db_double:
val_.d = static_cast<double*>(val);
break;
case db_int8:
val_.i8 = static_cast<int8_t*>(val);
break;
case db_int16:
val_.i16 = static_cast<int16_t*>(val);
break;
case db_int32:
val_.i32 = static_cast<int32_t*>(val);
break;
case db_int64:
val_.i64 = static_cast<int64_t*>(val);
break;
case db_uint8:
val_.u8 = static_cast<uint8_t*>(val);
break;
case db_uint16:
val_.u16 = static_cast<uint16_t*>(val);
break;
case db_uint32:
val_.u32 = static_cast<uint32_t*>(val);
break;
case db_uint64:
val_.u64 = static_cast<uint64_t*>(val);
break;
case db_date:
val_.t = static_cast<std::tm*>(val);
break;
case db_blob:
case db_xml:
case db_string:
val_.s = static_cast<std::string*>(val);
break;
default:
break;
}
}

const db_type dt_;
type_holder val_;
};

} // namespace details
Expand Down
Loading

0 comments on commit c0355f2

Please sign in to comment.