Skip to content

Commit

Permalink
Allow retrieval of movable types from row
Browse files Browse the repository at this point in the history
  • Loading branch information
zann1x committed Mar 22, 2024
1 parent 2c3a83f commit 36baa80
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
6 changes: 4 additions & 2 deletions include/soci/row.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class SOCI_DECL row
typedef typename type_conversion<T>::base_type base_type;
static_assert(details::can_use_from_base<type_conversion<T>>(),
"Can't use row::get() with this type (not convertible/copy-assignable from base_type) - did you mean to use move_as?");
base_type const& baseVal = holders_.at(pos)->get<base_type>();
base_type const& baseVal =
holders_.at(pos)->get<base_type>(details::value_cast_tag{});

T ret;
type_conversion<T>::from_base(baseVal, *indicators_.at(pos), ret);
Expand All @@ -91,7 +92,8 @@ class SOCI_DECL row
typedef typename type_conversion<T>::base_type base_type;
static_assert(details::can_use_move_from_base<T, base_type>(),
"row::move_as() can only be called with types that can be instantiated from a base type rvalue reference");
base_type & baseVal = holders_.at(pos)->get<base_type>();
base_type & baseVal =
holders_.at(pos)->get<base_type>(details::value_reference_tag{});

T ret;
type_conversion<T>::move_from_base(baseVal, *indicators_.at(pos), ret);
Expand Down
74 changes: 73 additions & 1 deletion include/soci/type-holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#ifndef SOCI_TYPE_HOLDER_H_INCLUDED
#define SOCI_TYPE_HOLDER_H_INCLUDED

#include "soci/blob.h"
#include "soci/error.h"
#include "soci/soci-backend.h"
#include "soci/soci-types.h"
Expand Down Expand Up @@ -50,6 +51,26 @@ T* checked_ptr_cast(U* ptr)
return static_cast<T*>(ptr);
}

template <typename T, typename U, typename Enable = void>
struct soci_return_same
{
static inline T& value(U&)
{
throw std::bad_cast();
}
};

template <typename T, typename U>
struct soci_return_same<
T, U,
typename std::enable_if<std::is_same<T, U>::value>::type>
{
static inline T& value(U& val)
{
return val;
}
};

// Type safe conversion that throws if the types are mismatched
template <typename T, typename U, typename Enable = void>
struct soci_cast
Expand Down Expand Up @@ -120,6 +141,7 @@ union type_holder
uint64_t* u64;
double* d;
std::tm* t;
blob* b;
};

template <typename T>
Expand Down Expand Up @@ -223,6 +245,15 @@ struct type_holder_trait<std::tm>
static const db_type type = db_date;
};

template <>
struct type_holder_trait<blob>
{
static const db_type type = db_blob;
};

struct value_cast_tag{};
struct value_reference_tag{};

// Class for storing type data instances in a container of holder objects
class holder
{
Expand Down Expand Up @@ -268,6 +299,8 @@ class holder
delete val_.t;
break;
case db_blob:
delete val_.b;
break;
case db_xml:
case db_string:
delete val_.s;
Expand All @@ -282,7 +315,7 @@ class holder
#pragma warning(disable:4702)
#endif
template <typename T>
T get()
T get(value_cast_tag)
{
switch (dt_)
{
Expand All @@ -307,13 +340,50 @@ class holder
case db_date:
return soci_cast<T, std::tm>::cast(*val_.t);
case db_blob:
// blob is not copyable
break;
case db_xml:
case db_string:
return soci_cast<T, std::string>::cast(*val_.s);
}

throw std::bad_cast();
}

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

throw std::bad_cast();
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
Expand Down Expand Up @@ -354,6 +424,8 @@ class holder
val_.t = static_cast<std::tm*>(val);
return;
case db_blob:
val_.b = static_cast<blob*>(val);
return;
case db_xml:
case db_string:
val_.s = static_cast<std::string*>(val);
Expand Down
3 changes: 2 additions & 1 deletion src/core/row.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#define SOCI_SOURCE
#include "soci/row.h"
#include "soci/type-holder.h"

#include <cstddef>
#include <cctype>
Expand Down Expand Up @@ -114,7 +115,7 @@ template <>
blob row::move_as<blob>(std::size_t pos) const
{
typedef typename type_conversion<blob>::base_type base_type;
base_type & baseVal = holders_.at(pos)->get<base_type>();
base_type & baseVal = holders_.at(pos)->get<base_type>(value_reference_tag{});

blob ret;
type_conversion<blob>::move_from_base(baseVal, *indicators_.at(pos), ret);
Expand Down

0 comments on commit 36baa80

Please sign in to comment.