Skip to content

Commit

Permalink
Improve shared_ptr support (closes #4).
Browse files Browse the repository at this point in the history
The following only applies if one of the shared_ptr_converter or
std_shared_ptr_converter headers is included. shared_ptr means
std:: or boost::shared_ptr if the latter is included but only
boost::shared_ptr if just shared_ptr_converter is included (yes, that's
a bit counter inuitive):

1. If the shared_ptr requested (from C++) has the exact same type as the
   one which is present in Lua (if any), the behavior is now the same as
   with the automatic smart pointer support (i.e. use_count will be
   increased properly but raw object equality in Lua won't be preserved).
2. If the pointee type of the requested shared_ptr has a shared_from_this
   member function (checked automatically at compile time), this will be
   used to obtain a shared_ptr. Caveats:
   (1) If the object is not already hold in a shared_ptr, behavior is
       undefined (probably a bad_weak_ptr exception will be thrown).
   (2) If the enable_shared_from_this() member is not a function with
       the right prototype (ptr_t enable_shared_from_this() with ptr_t
       being convertible to the requested shared_ptr<T> type) a compile
       time error will result.
3. Otherwise, behavior is the same as for boost::shared_ptr before this
   change:
   A new shared_ptr will be created from the raw pointer associated with
   the Lua object (even if it is not held in a shared_ptr) whose deleter
   holds a strong reference to the Lua object, thus preventing it's
   destruction until the reference is released by invoking the deleter
   (i.e. by resetting or destroying the shared_ptr) or (and this could
   be problematic) until the asocciated lua_State is closed.

The ideal solution would of course be to return the equivalent of
static_cast<requested_shared_ptr>(original_shared_ptr), but this would only
be possible for a fixed set of smart pointer types, requiring luabind to
generate cast functions between the shared_ptrs -- a major overhaul to the
already very complicated inheritance/cast system which currently relies on
caching ptrdiff_ts and passing void* around which are (contrary to shared_ptr)
trivially destructible.
  • Loading branch information
Oberon00 committed Aug 5, 2013
1 parent dbfa920 commit dd1cb88
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 50 deletions.
123 changes: 98 additions & 25 deletions luabind/shared_ptr_converter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

# include <luabind/detail/decorate_type.hpp> // for LUABIND_DECORATE_TYPE
# include <luabind/detail/policy.hpp> // for default_converter, etc
# include <luabind/detail/yes_no.hpp>
# include <luabind/get_main_thread.hpp> // for get_main_thread
# include <luabind/handle.hpp> // for handle

Expand All @@ -15,6 +16,8 @@

namespace luabind {

namespace mpl = boost::mpl;

namespace detail
{

Expand All @@ -32,36 +35,110 @@ namespace detail
handle life_support;
};

// From http://stackoverflow.com/a/1007175/2128694
// (by Johannes Schaub - litb, "based on a brilliant idea of someone on
// usenet")
template <typename T>
struct has_shared_from_this_aux
{
has_shared_from_this_aux(); // not implemented; silence Clang warning
private:

struct fallback { int shared_from_this; }; // introduce member name
struct derived : T, fallback
{
derived(); // not implemented; silence MSVC warnings C4510 and C4610
};

template<typename C, C> struct check;

template<typename C> static no_t f(
check<int fallback::*, &C::shared_from_this>*);
template<typename C> static yes_t f(...);

public:
BOOST_STATIC_CONSTANT(bool, value =
sizeof(f<derived>(0)) == sizeof(yes_t));
};

template <typename T>
struct has_shared_from_this:
mpl::bool_<has_shared_from_this_aux<T>::value>
{};

} // namespace detail

template <class T>
struct default_converter<boost::shared_ptr<T> >
: default_converter<T*>
using boost::get_deleter;

namespace detail {

template <class P>
struct shared_ptr_converter
: default_converter<typename P::element_type*>
{
typedef boost::mpl::false_ is_native;
private:
typedef P ptr_t;
typedef typename ptr_t::element_type* rawptr_t;
detail::value_converter m_val_cv;
int m_val_score;

// no shared_from_this() available
ptr_t shared_from_raw(rawptr_t raw, lua_State* L, int index, mpl::false_)
{
return ptr_t(raw, detail::shared_ptr_deleter(L, index));
}

// shared_from_this() available.
ptr_t shared_from_raw(rawptr_t raw, lua_State*, int, mpl::true_)
{
return raw->shared_from_this();
}

public:
shared_ptr_converter(): m_val_score(-1) {}

typedef mpl::false_ is_native;

template <class U>
int match(lua_State* L, U, int index)
{
return default_converter<T*>::match(
L, LUABIND_DECORATE_TYPE(T*), index);
// Check if the value on the stack is a holder with exactly ptr_t
// as pointer type.
m_val_score = m_val_cv.match(L, LUABIND_DECORATE_TYPE(ptr_t), index);
if (m_val_score >= 0)
return m_val_score;

// Fall back to raw_ptr.
return default_converter<rawptr_t>::match(
L, LUABIND_DECORATE_TYPE(rawptr_t), index);
}

template <class U>
boost::shared_ptr<T> apply(lua_State* L, U, int index)
ptr_t apply(lua_State* L, U, int index)
{
T* raw_ptr = default_converter<T*>::apply(
L, LUABIND_DECORATE_TYPE(T*), index);
// First, check if we got away without upcasting.
if (m_val_score >= 0)
{
ptr_t ptr = m_val_cv.apply(
L, LUABIND_DECORATE_TYPE(ptr_t), index);
return ptr;
}

// If not obtain, a raw pointer and construct are shared one from it.
rawptr_t raw_ptr = default_converter<rawptr_t>::apply(
L, LUABIND_DECORATE_TYPE(rawptr_t), index);
if (!raw_ptr)
return boost::shared_ptr<T>();
return boost::shared_ptr<T>(
raw_ptr, detail::shared_ptr_deleter(L, index));
return ptr_t();

return shared_from_raw(
raw_ptr, L, index,
detail::has_shared_from_this<typename ptr_t::element_type>());
}

void apply(lua_State* L, boost::shared_ptr<T> const& p)
void apply(lua_State* L, ptr_t const& p)
{
if (detail::shared_ptr_deleter* d =
boost::get_deleter<detail::shared_ptr_deleter>(p))
get_deleter<detail::shared_ptr_deleter>(p)) // Rely on ADL.
{
d->life_support.push(L);
}
Expand All @@ -75,19 +152,15 @@ struct default_converter<boost::shared_ptr<T> >
void converter_postcall(lua_State*, U const&, int)
{}
};
} // namepace detail

template <class T>
struct default_converter<boost::shared_ptr<T> const&>
: default_converter<boost::shared_ptr<T> >
{};

#ifdef BOOST_HAS_RVALUE_REFS
template <class T>
struct default_converter<boost::shared_ptr<T>&&>
: default_converter<boost::shared_ptr<T> >
{};
#endif
template <typename T>
struct default_converter<boost::shared_ptr<T> >:
detail::shared_ptr_converter<boost::shared_ptr<T> > {};

template <typename T>
struct default_converter<boost::shared_ptr<T> const&>:
detail::shared_ptr_converter<boost::shared_ptr<T> > {};
} // namespace luabind

#endif // LUABIND_SHARED_PTR_CONVERTER_090211_HPP
50 changes: 29 additions & 21 deletions luabind/std_shared_ptr_converter.hpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
#if defined(LUABIND_TEST_HEADERCOMPILE)
# include <boost/config.hpp>
# if (defined(BOOST_NO_CXX11_SMART_PTR) \
&& (!defined(BOOST_MSVC) || BOOST_MSVC < 1600))
// Skip file contents if in headercompile test and std::shared_ptr is not
// supported.
# define LUABIND_STD_SHAREDPTR_CONVERTER_HPP_INCLUDED
# endif
#endif


#ifndef LUABIND_STD_SHAREDPTR_CONVERTER_HPP_INCLUDED
#define LUABIND_STD_SHAREDPTR_CONVERTER_HPP_INCLUDED LUABIND_STD_SHAREDPTR_CONVERTER_HPP_INCLUDED

#include <boost/config.hpp>
#if (defined(BOOST_NO_CXX11_SMART_PTR) \
&& (!defined(BOOST_MSVC) || BOOST_MSVC < 1600))
# define LUABIND_NO_STD_SHARED_PTR
#else

#include <memory> // shared_ptr
# include <luabind/shared_ptr_converter.hpp>
# include <memory> // shared_ptr

#if BOOST_VERSION >= 105300
# include <luabind/detail/has_get_pointer.hpp>
# if BOOST_VERSION >= 105300
# include <luabind/detail/has_get_pointer.hpp>

# include <boost/get_pointer.hpp>
# include <boost/get_pointer.hpp>

namespace luabind { namespace detail { namespace has_get_pointer_ {
template<class T>
Expand All @@ -42,24 +36,38 @@ using boost::get_pointer;



#else // if BOOST_VERSION < 105300
# else // if BOOST_VERSION < 105300

// Not standard conforming: add function to ::std(::tr1)
namespace std {

#if defined(_MSC_VER) && _MSC_VER < 1700
# if defined(_MSC_VER) && _MSC_VER < 1700
namespace tr1 {
#endif
# endif

template<class T>
T * get_pointer(shared_ptr<T> const& p) { return p.get(); }

#if defined(_MSC_VER) && _MSC_VER < 1700
# if defined(_MSC_VER) && _MSC_VER < 1700
} // namespace tr1
#endif
# endif

} // namespace std

#endif // if BOOST_VERSION < 105300 / else

#endif
namespace luabind {
using std::get_deleter;

template <typename T>
struct default_converter<std::shared_ptr<T> >:
detail::shared_ptr_converter<std::shared_ptr<T> > {};

template <typename T>
struct default_converter<std::shared_ptr<T> const&>:
detail::shared_ptr_converter<std::shared_ptr<T> > {};
} // namespace luabind

#endif // if smart pointers are available

#endif // LUABIND_STD_SHAREDPTR_CONVERTER_HPP_INCLUDED
31 changes: 30 additions & 1 deletion test/test_automatic_smart_ptr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ struct X

int X::alive = 0;


struct D: X
{
D(int value_): X(value_) {}
};

struct ptr
{
ptr(X* p_)
Expand Down Expand Up @@ -68,6 +74,13 @@ ptr make3()
return ptr(new X(3));
}

boost::shared_ptr<D> make_d()
{
return boost::shared_ptr<D>(new D(10));
}

void needs_x(boost::shared_ptr<X>) {}

} // namespace unnamed

void test_main(lua_State* L)
Expand All @@ -78,9 +91,14 @@ void test_main(lua_State* L)
class_<X>("X")
.def_readonly("value", &X::value),

class_<D, X>("D"),

def("make1", make1),
def("make2", make2),
def("make3", make3)
def("make3", make3),

def("make_d", make_d),
def("needs_x", needs_x)
];

DOSTRING(L,
Expand Down Expand Up @@ -112,4 +130,15 @@ void test_main(lua_State* L)
TEST_CHECK(X::alive == 1);
spx.reset();
TEST_CHECK(X::alive == 0);

DOSTRING(L,
"d = make_d()\n"
"status, err = pcall(needs_x, d)\n"
"assert(not status)\n"
"pat = '^No matching overload found, candidates:\\n'\n"
"pat = pat .. 'void needs_x%(custom %[.+%]%)$'\n"
"if not err:match(pat) then\n"
" error('expected \"' .. pat .. '\", got \"' .. err .. '\"')\n"
"end\n"
);
}
Loading

0 comments on commit dd1cb88

Please sign in to comment.