More AngelScript binding wrappers

Started by
27 comments, last by WitchLord 12 years, 1 month ago
The functions returned by WRAP_MFN() are generic calling conventions so should be registered with asCALL_GENERIC. Since it looks like you're using native calling conventions, then you don't need to use WRAP_MFN(), etc. Just use asMETHOD and asCALL_THISCALL. You would use these if you want to create generic calling convention wrappers for native functions. For example, if your platform doesn't support native calls.
Advertisement
Ah, I was so distracted by the example main that I forgot to try the simple route - you are correct that I am using the native calling convention.

asMETHOD and asCALL_THISCALL worked like a charm, thanks!



Now that that's in place and I've cleaned out all the @'s in my code, (I'm converting my system over to SPTRs from normal pointers,) now all I have left is to figure out how to work around the fact that while I can assign null to these, I can't compare to null: I can't find any info on how to overload the "is null" operator! :/ The reason is that my script has a function with an optional parameter that is defaulted to null:

// Yes, this isn't a C&P from my code; the code's too verbose for this example.

void myFunc(const string &in name, Entity entity = null) {
//...
if (!(parent_entity is null)) {
//...
}
//...
}

(The above code fails AS compilation with "No conversion from '<null handle>' to 'Entity' available.")

I'm thinking I might just overload the function with one that doesn't have the second option and just have one call the other. (Or, as these are actually constructors in a script class in my actual code, have both of them call a private method that does the work...)
Bypassing the null problem worked. But now my entire stack of cards seems to have fallen down: the script sptr heads off to uninit'd memory. I'm probably missing something simple again, but after several hours of work, I've made little progress.

File: sptrtypes.h


// Library Includes
#include <angelscript/sptrwrapper/as_smart_ptr_wrapper.h>

// Local Includes
#include "../sharedbase/Entity.h"

// Forward Declarations

// Typedefs

// Functions

// Helper functions

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template <typename T>
void construct(void* address) {
new (address) T();
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template <typename T>
void destroy(T* object) {
object->~T();
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template <typename T>
void copy_construct(void* address, T* other) {
new (address) T(*other);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template <typename T>
void assign(T * lhs, T* rhs) {
*lhs = *rhs;
}


// Registration functions

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void RegisterEntity(asIScriptEngine* const engine) {
int ret = 0;

// Register Object
ret = engine->RegisterObjectType("Entity", sizeof(EntitySPTR), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert(ret >= 0);

// Register behaviors and operations
ret = engine->RegisterObjectBehaviour("Entity", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Entity::FactoryAtAddress), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
// ret = engine->RegisterObjectBehaviour("Entity", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(construct<EntitySPTR>), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectBehaviour("Entity", asBEHAVE_CONSTRUCT, "void f(const Entity &in)", asFUNCTION(copy_construct<EntitySPTR>), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectBehaviour("Entity", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(destroy<EntitySPTR>), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "Entity &opAssign(const Entity &in)", asFUNCTION(assign<EntitySPTR>), asCALL_CDECL_OBJFIRST); assert(ret >= 0);

// Register properties
ret = engine->RegisterObjectProperty("Entity", "int id", offsetof(Entity, id)); assert(ret >= 0);
ret = engine->RegisterObjectProperty("Entity", "PropertyMap properties", offsetof(Entity, properties)); assert(ret >= 0);
ret = engine->RegisterObjectProperty("Entity", "float scale", offsetof(Entity, scale)); assert(ret >= 0);
ret = engine->RegisterObjectProperty("Entity", "Vector positionOffset", offsetof(Entity, location)); assert(ret >= 0);
ret = engine->RegisterObjectProperty("Entity", "Rotation rotationOffset", offsetof(Entity, rotation)); assert(ret >= 0);

// Register methods
ret = engine->RegisterObjectMethod("Entity", "void SetParent(Entity)", asMETHOD(Entity, SetParent), asCALL_THISCALL); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "Entity GetParent()", asMETHOD(Entity, GetParent), asCALL_THISCALL); assert(ret >= 0);

ret = engine->RegisterObjectMethod("Entity", "void ChangeScale(float)", asMETHOD(Entity, ChangeScale), asCALL_THISCALL); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "float GetWorldScale()", asMETHOD(Entity, GetWorldScale), asCALL_THISCALL); assert(ret >= 0);

ret = engine->RegisterObjectMethod("Entity", "Vector GetWorldPosition()", asMETHOD(Entity, GetWorldPosition), asCALL_THISCALL); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void SetWorldPosition(float, float, float)", asMETHODPR(Entity, SetWorldPosition, (float, float, float), void), asCALL_THISCALL); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void ChangePosition(Vector)", asMETHODPR(Entity, ChangePosition, (D3DXVECTOR3), void), asCALL_THISCALL); assert(ret >= 0);

ret = engine->RegisterObjectMethod("Entity", "Rotation GetWorldRotation()", asMETHOD(Entity, GetWorldRotation), asCALL_THISCALL); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void SetRotation(float, float, float)", asMETHODPR(Entity, SetRotation, (float, float, float), void), asCALL_THISCALL); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void ChangeRotation(float, float, float)", asMETHODPR(Entity, ChangeRotation, (float, float, float), void), asCALL_THISCALL); assert(ret >= 0);
}



Note: Entity has a private ctor.

Entity::Factory


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/// Creates a new Entity at the specified address.
void Entity::FactoryAtAddress(void* address) {
new (address) EntitySPTR(new Entity());
}





Everything compiles fine, but when my test script runs, it has some strange crashes involving access violations in seemingly unrelated code.

Test Script"


void main(void) {
Log(FLOW, "main: Loadup script started.");

string test;

{
//Entity myEntity;
Entity myEntity;

myEntity.id = -12; // Access violation below involving a std::string when this is used.
// myEntity.id = 12; // Execution proceeds fine with this one.
}

{
test = "Entity without parens"; // This string is the one that gets the violation.

Entity myEntity;

Log(INFO, test + ": id is '" + myEntity.id + "' giving result: " + (myEntity.id == -1)); // Entity's ctor initializes this property to -1, so if this is pointed at the correct section of memory, one of these tests should return true. However, all return false.
}

{
test = "Entity with parens";

Entity myEntity();

Log(INFO, test + ": id is '" + myEntity.id + "' giving result: " + (myEntity.id == -1));
}

{
test = "Entity with assignment of ctor";

Entity myEntity = Entity();

Log(INFO, test + ": id is '" + myEntity.id + "' giving result: " + (myEntity.id == -1));
}

}



This looks like VERY strange behavior to me, resulting in me coming to the conclusion that something in the registration is still wrong. Especially as it seems that I'm not even using anything from the as_smart_ptr_wrapper header...
You must use the wrappers from the as_smart_ptr_wrapper.h when registering the functions. Otherwise you're registering the actual class methods, but AngelScript doesn't have the actual object pointer, so when the methods are called you're likely to have memory invasions, crashes, and other nasty problems.

Thanks for alerting me on the package I uploaded. I'll update it with the fixes. I also noticed I put the wrong code in the as_smart_ptr_wrapper.h so that needs to be fixed too.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I've propagated the changes to the python files, here's the patch: [attachment=7476:wrapper_gen.patch.txt]

Now that I've run those python files to regenerate the headers I'm going to re-attempt my code, I'll let you know the results.
Ok, my code now seems to be sane: my test script runs without complaint. Even the test.cpp file looks normal, now that I have the correct header and understanding. Thank you both very much for the pointers in the correct direction!

For reader learning:
Switched over to using CALLER and CALLER_PR, eg:

ret = engine->RegisterObjectMethod("Entity", "void ChangeScale(float)", CALLER(Entity, ChangeScale), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "float GetWorldScale()", CALLER(Entity, GetWorldScale), asCALL_CDECL_OBJFIRST); assert(ret >= 0);


//...

ret = engine->RegisterObjectMethod("Entity", "Rotation GetWorldRotation()", CALLER(Entity, GetWorldRotation), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void SetRotation(float, float, float)", CALLER_PR(Entity, SetRotation, (float, float, float), void), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void ChangeRotation(float, float, float)", CALLER_PR(Entity, ChangeRotation, (float, float, float), void), asCALL_CDECL_OBJFIRST); assert(ret >= 0);


And I had to register via property accessors instead of direct properties, eg:


ret = engine->RegisterObjectMethod("Entity", "const int& get_id() const", REF_GETTER(Entity, id), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
ret = engine->RegisterObjectMethod("Entity", "void set_id(const int& in)", REF_SETTER(Entity, id), asCALL_CDECL_OBJFIRST); assert(ret >= 0);
Thanks for the patch. I'll apply it as soon as I get the time.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I didn't notice this when I was writing generic code from existing native binding but these bindings still require writing proxy functions for constructors and destructors. So I hacked in automatic wrappers for them on the header generator in SVN. The constructor version uses an argument type list like the PR macros. Examples:

r = engine->RegisterObjectBehaviour("Base", asBEHAVE_CONSTRUCT, "void f()", WRAP_CON(Base, ()), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Base", asBEHAVE_CONSTRUCT, "void f(const Base & in)", WRAP_CON(Base, (const Base &)), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Base", asBEHAVE_DESTRUCT, "void f()", WRAP_DES(Base), asCALL_GENERIC); assert(r >= 0);


#include <stdio.h>
#include <string>

// Generate templates for up to this number of function parameters
const int max_args = 4;

using namespace std;

void PrintTemplate(const char *base, const char *typeNameList, const char *retType, const char *objType, const char *isConst, const char *newExpr, const char *objExpr, const char *argList1, const char *argList2, const char *wrapName);
void PrintConstructor(const char *comma, const char *typeNameList, const char *typeList, const char *argList);

int main()
{
printf("#ifndef AS_GEN_WRAPPER_H\n"
"#define AS_GEN_WRAPPER_H\n"
"\n"
"#include \"angelscript.h\"\n"
"#include <new>\n"
"\n"
"namespace gw {\n"
"\n"
"template <typename T> class Proxy {\n"
" public:\n"
" T value;\n"
" Proxy(T value) : value(value) {}\n"
" static T cast(void * ptr) {\n"
" return reinterpret_cast<Proxy<T> *>(&ptr)->value;\n"
" }\n"
" private:\n"
" Proxy(const Proxy &);\n"
" Proxy & operator=(const Proxy &);\n"
"};\n"
"\n"
"template <typename T> struct Wrapper {};\n"
"template <typename T> struct ObjFirst {};\n"
"template <typename T> struct ObjLast {};\n"
"template <typename T> struct Constructor {};\n"
"\n"
"template <typename T>\n"
"void destroy(asIScriptGeneric * gen) {\n"
" static_cast<T *>(gen->GetObject())->~T();\n"
"}\n");

string typename_list = "typename A0";
string type_list = "A0";
string arg_list = "\n static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value";
string new_exp = "new (gen->GetAddressOfReturnLocation()) Proxy<R>";
string obj_exp = "static_cast<T *>(gen->GetObject())->*";
string obj_arg_exp = "\n Proxy<T>::cast(gen->GetObject())";

PrintTemplate("", "", "void", "", "", "", "", "void", "", "Wrapper");
PrintTemplate("typename R", "", "R", "", "", new_exp.c_str(), "", "void", "", "Wrapper");
PrintTemplate("typename T", "", "void", "T::", "", "", obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T, typename R", "", "R", "T::", "", new_exp.c_str(), obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T", "", "void", "T::", " const", "", obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T, typename R", "", "R", "T::", " const", new_exp.c_str(), obj_exp.c_str(), "void", "", "Wrapper");

PrintTemplate("typename T", "", "void", "", "", "", "", "T", obj_arg_exp.c_str(), "ObjFirst");
PrintTemplate("typename T, typename R", "", "R", "", "", new_exp.c_str(), "", "T", obj_arg_exp.c_str(), "ObjFirst");
PrintTemplate("typename T", "", "void", "", "", "", "", "T", obj_arg_exp.c_str(), "ObjLast");
PrintTemplate("typename T, typename R", "", "R", "", "", new_exp.c_str(), "", "T", obj_arg_exp.c_str(), "ObjLast");

PrintConstructor("", "", "", "");

for( int i = 0; i < max_args; i++ )
{
PrintTemplate("", typename_list.c_str(), "void", "", "", "", "", type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "T::", "", "", obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "T::", "", new_exp.c_str(), obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "T::", " const", "", obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "T::", " const", new_exp.c_str(), obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");

PrintTemplate("typename T, ", typename_list.c_str(), "void", "", "", "", "", ("T, " + type_list).c_str(), (obj_arg_exp + "," + arg_list).c_str(), "ObjFirst");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", ("T, " + type_list).c_str(), (obj_arg_exp + "," + arg_list).c_str(), "ObjFirst");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "", "", "", "", (type_list + ", T").c_str(), (arg_list + "," + obj_arg_exp).c_str(), "ObjLast");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", (type_list + ", T").c_str(), (arg_list + "," + obj_arg_exp).c_str(), "ObjLast");

PrintConstructor(", ", typename_list.c_str(), type_list.c_str(), arg_list.c_str());

char buf[5];
sprintf(buf, "%d", i + 1);
typename_list += ", typename A" + string(buf);
type_list += ", A" + string(buf);
arg_list += ",\n static_cast<Proxy <A" + string(buf) + "> *>(gen->GetAddressOfArg(" + string(buf) + "))->value";
}

printf("template <typename T>\n"
"struct Id {\n"
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr f(void) { return asFUNCTION(&Wrapper<T>::template f<fn_ptr>); }\n"
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr of(void) { return asFUNCTION(&ObjFirst<T>::template f<fn_ptr>); }\n"
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr ol(void) { return asFUNCTION(&ObjLast<T>::template f<fn_ptr>); }\n"
"};\n"
"\n"
"template <typename T>\n"
"Id<T> id(T fn_ptr) { return Id<T>(); }\n"
"\n"
"#define WRAP_FN(name) (::gw::id(name).f< name >())\n"
"#define WRAP_MFN(ClassType, name) (::gw::id(&ClassType::name).f< &ClassType::name >())\n"
"#define WRAP_OBJ_FIRST(name) (::gw::id(name).of< name >())\n"
"#define WRAP_OBJ_LAST(name) (::gw::id(name).ol< name >())\n"
"\n"
"#define WRAP_FN_PR(name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (*)Parameters>::f< name >))\n"
"#define WRAP_MFN_PR(ClassType, name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (ClassType::*)Parameters>::f< &ClassType::name >))\n"
"#define WRAP_OBJ_FIRST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjFirst<ReturnType (*)Parameters>::f< name >))\n"
"#define WRAP_OBJ_LAST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjLast<ReturnType (*)Parameters>::f< name >))\n"
"\n"
"#define WRAP_CON(ClassType, Parameters) asFUNCTION((::gw::Constructor<ClassType Parameters>::f))\n"
"#define WRAP_DES(ClassType) asFUNCTION((::gw::destroy<ClassType>))\n"
"\n"
"} // end namespace gw\n"
"\n"
"#endif\n");

return 0;
}

// 0 1 2 3 4 5 6 7 8 9
void PrintTemplate(const char *base, const char *typeNameList, const char *retType, const char *objType, const char *isConst, const char *newExpr, const char *objExpr, const char *argList1, const char *argList2, const char *wrapName)
{
/*
template <{0}{1}>
struct {9}<{2} ({3}*)({7}){4}> {{
template <{2} ({3}*fp)({7}){4}>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {{
{5}(({6}fp)({8}));
}}
}};
*/

printf("template <%s%s>\n", base, typeNameList);
printf("struct %s<%s (%s*)(%s)%s> {\n", wrapName, retType, objType, argList1, isConst);
printf(" template <%s (%s*fp)(%s)%s>\n", retType, objType, argList1, isConst);
printf(" static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n");
printf(" %s((%sfp)(%s));\n", newExpr, objExpr, argList2);
printf(" }\n");
printf("};\n");
}

void PrintConstructor(const char *comma, const char *typeNameList, const char *typeList, const char *argList) {
printf("template <typename T%s%s>\n", comma, typeNameList);
printf("struct Constructor <T (%s)> {\n", typeList);
printf(" static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n");
printf(" new (gen->GetObject()) T(%s);\n", argList);
printf(" }\n");
printf("};\n");
}
Nice. I'll update the SVN.

Thanks,
Andreas

PS. You're almost making the native calling conventions obselete ;)

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement