Jump to content
  • Advertisement
ghackenberg

Using boost::shared_ptr with AngelScript

This topic is 2409 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi Andreas,

since I already got valuable feedback from you about the string issue (thread link) I wanted to start the discussion about another topic as well: "boost::shared_ptr". We use shared pointers in our application a lot, e.g. as return values of manager objects or as parameters to function calls. To make the application interface scriptable we decided to try wrapping the "boost::shared_ptr" class in AngelScript. What I came up with look like the following:

First a wrapper for constructing and desctructing shared pointer objects is defined
/// \brief Wrapper template for shared pointer behavior.
template <typename Type>
struct SharedPointerWrapper
{
/// \brief Constructor wrapper.
static void construct(void* memory);
/// \brief Destructor wrapper.
static void destruct(void* memory);
};

template <typename Type>
void SharedPointerWrapper<Type>::construct(void* memory)
{
new(memory) boost::shared_ptr<Type>();
}
template <typename Type>
void SharedPointerWrapper<Type>::destruct(void* memory)
{
((boost::shared_ptr<Type>*)memory)->~shared_ptr();
}


Then I have a registration function, which looks as follows. Note that shared pointers are registered as value type with constructor/destructor and assignment operators. Finally, also a "get" method is provided to retrieve a reference to the contained object. The boost implementation of the get method just returns the raw pointer.
/// \brief Function template for registering shared pointer types.
template <typename Type>
void RegisterSharedPointer(const Core::String& ptrTypeName, const Core::String& typeName, asIScriptEngine* engine)
{
Core::String getSignature;
getSignature.append(typeName).append("& get()");

int r;

r = engine->RegisterObjectType(ptrTypeName.c_str(), sizeof(boost::shared_ptr<Type>), asOBJ_VALUE | asOBJ_APP_CLASS_CDA); assert(r >= 0);

r = engine->RegisterObjectBehaviour(ptrTypeName.c_str(), asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(SharedPointerWrapper<Type>::construct), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectBehaviour(ptrTypeName.c_str(), asBEHAVE_DESTRUCT, "void f()", asFUNCTION(SharedPointerWrapper<Type>::destruct), asCALL_CDECL_OBJLAST); assert(r >= 0);

r = engine->RegisterObjectMethod(ptrTypeName.c_str(), (ptrTypeName + "& opAssign(const " + ptrTypeName + " &in other)").c_str(), asMETHODPR(boost::shared_ptr<Type>, operator=, (boost::shared_ptr<Type> const &), boost::shared_ptr<Type>&), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod(ptrTypeName.c_str(), getSignature.c_str(), asMETHOD(boost::shared_ptr<Type>, get), asCALL_THISCALL); assert(r >= 0);
}


Now I can register "boost::shared_ptr" classes as follows:
RegisterSharedPointer<Type>("TypePtr", "Type", engine);


Before executing that line I have to make sure that the type "Type" has been registered on the engine. Since I only want AngelScript code to remember the shared pointer object I currently register the contained type as "asOBJ_REF | asOBJ_SCOPED", i.e.:
engine->RegisterObjectType("Type", 0, asOBJ_REF | asOBJ_SCOPED);
engine->RegisterObjectMethod("Type", "string getProperty()", ...);


This finally allows me to write the following in AngelScript:
TypePtr shared = manager.getSomeSharedTypeInstance();
print(shared.get().getProperty());


So far the concept does not seem to crash or memory-leak. It also seems to be possible to pass shared pointer instances allocated by AngelScript back to CPP, i.e.
anotherManager.use(shared);


I was wondering what you think about this issue.

Regards,
Georg

Share this post


Link to post
Share on other sites
Advertisement
I really like this way.
I would soon have the same problem when exposing the UI System to angelscript: Every widget is stored in a shared_ptr and I thought how I could expose them anyway, without giving the scripter the ability to screw up and keep references that are long gone and invalid.

Maybe one could register the shared_ptr as a template, but I haven't worked with templates so far, so I can't be sure if and how that would work.

Share this post


Link to post
Share on other sites
Hi WitchLord,

Can you imagine a way to automatically expose the calls of the internal object through the SharedPointer interface?

In code:

ObjectPtr obj;
obj.ptr().doSomething(); // the ptr() is somehow cumbersome here
// obj.doSomething(); // this would be nice but...

We already tried some alternative e.g. making the function "ptr()" a property "ptr" to avoid at least the brackets but thats still not as good as it should be.

Any ideas how to achieve such a behaviour generically and automatically (and safely ;-) )?

xad

Share this post


Link to post
Share on other sites
For that it would be necessary to register wrapper functions, on the smart pointer interface itself. Maybe something like this:


void ObjectPtr_Method(boost::shared_ptr &ptr)
{
// If no object is stored in the shared pointer this must not be executed
if( !ptr )
{
asIScriptContext *ctx = asGetActiveContext();
ctx->SetException("Null pointer");
return;
}

// Execute the method on the ptr
ptr->Method();
}


Register the function as a method, but with the asCALL_CDECL_OBJLAST calling convention.

It should be possible to create these wrapper methods automatically through templates. Perhaps the code in the autowrapper add-on can be adapted for this purposes.

Share this post


Link to post
Share on other sites
Sorry to bring back the dead topic, but I was having an issue with type registration as you show using asOBJ_REF | asOBJ_SCOPED. This required me to register some additional behaviors for "Type". I changed to using asOBJ_NOHANDLE instead of asOBJ_SCOPED, and the requirement for those behaviors went bye-bye, but the code still works when passing back and forth to the application.

Also it should be noted you need a factory (not the behavior) for "TypePTR" to get an instance from the application for using in the script. You do touch on the factory by just having it instantiate it in the script via a function.

If I am missing something important by not going the scoped route please let me know now.

Share this post


Link to post
Share on other sites
You may want to take a look at the code that SiCrane posted in this other thread:

http://www.gamedev.net/topic/617111-more-angelscript-binding-wrappers/




The NOHANDLE flag is meant for uninstanciable types, e.g singletons. The SCOPED type is meant for types with specific memory management requirements but that should behave like value types.

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!