4

I'm trying to return an std::shared_ptr from a method bound with Luabind, but it doesn't seem to recognize the type.

Luabind code:

module(lua)
[
    class_<Character, BaseEntity, std::shared_ptr<Character> > ("Character"),
        def("createCharacter", &Character::createCharacter)
];

createCharacter code:

std::shared_ptr<Character> Character::createCharacter(Game* gameInstance, const Character::CharacterSetup& characterSetup, string sprite, b2World* world)
{
    return std::shared_ptr<Character>(new Character(gameInstance, characterSetup, sprite, world));
}

If I call this method in a Lua script, nothing gets returned, and execution stops there. However, if I change the method to return a Character*, it works as expected. Some googling around tells me that returning a shared_ptr shouldn't be a problem.

What am I doing wrong?

Also, I have this code so Luabind can understand std::shared_ptr:

namespace luabind
{
    template <typename T>
    T* get_pointer(std::shared_ptr<T> const& p)
    {
        return p.get();
    }
}
tacospice
  • 647
  • 5
  • 20
  • 3
    Have you tried `boost::shared_ptr` instead? – Luc Danton Feb 23 '13 at 08:49
  • I'm not too familiar with luabind, but if it doesn't store the shared pointer but just uses get_pointer() to get a real pointer, it will drop the last reference (thus deleting the object) while it keeps the (now dangling) pointer. Firstly, make sure your class is properly made noncopyable. Secondly, add breakpoints to constructor and destructor. – Ulrich Eckhardt Feb 23 '13 at 09:49

1 Answers1

6

I had to solve the very same problem.

It's a bit complicated. Basically you need to define a function with the prototype

template <typename T>
T* get_pointer(std::shared_ptr<T> const&);

This function additionally must reside in the same namespace as std::shared_ptr, thus std::. Note that a function in the global namespace or luabind namespace, as yours, won't work, because luabind uses special tricks to ensure that only ADL is used when checking if a certain type is a smart pointer. The only way to circumvent this is to define your function in the luabind::detail::has_get_pointer_ namespace instead of just luabind.

But defining your function in this namespace alone, won't work either (at least for Boost <1.53). Although defining the function in namespace std is technically not allowed by the C++-standard, it is the only possible way for Boost <1.53. Since 1.53, however, Boost defines it's own boost::get_pointer() overloads for std::shared_ptr (and std::unique_ptr). For this version it's enough to make Boost's get_pointer() visible in the luabind::detail::has_get_pointer_ namespace, since luabind is using this function anywhere it uses smart pointers (see the luabind/get_pointer.hpp header). Defining the function in std wont even work then, because luabind would provoke an ambiguous call.

So if you want a get_pointer() overload for std::shared_ptr which works for Boost <1.53 and >= 1.53 and also for MSVC 10 (and maybe 9) (these define shared_ptr in std::tr1 instead of std), I can offer you my (historically grown ;-) ) header:

#ifndef SHAREDPTR_CONVERTER_HPP_INCLUDED
#define SHAREDPTR_CONVERTER_HPP_INCLUDED SHAREDPTR_CONVERTER_HPP_INCLUDED

#include <boost/version.hpp>

#if BOOST_VERSION >= 105300

#include <boost/get_pointer.hpp>


namespace luabind { namespace detail { namespace has_get_pointer_ {
    template<class T>
    T * get_pointer(std::shared_ptr<T> const& p) { return p.get(); }
}}}

#else // if BOOST_VERSION < 105300

#include <memory>

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

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

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

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

} // namespace std

#endif // if BOOST_VERSION < 105300

#endif
Oberon
  • 3,219
  • 15
  • 30