Question

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();
    }
}
Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top