How to use MethodThroughPointer ?

Started by
5 comments, last by Deyja 17 years ago
I have classes where I want script to only deal with the pointers to these classes, and to call member functions as if they were directly callable through the pointer. I ran across MethodThroughPointer that seems to do this, but I'm not sure how to use the template syntax.

void ScriptLight::RegisterScriptClass(void)
{
ScriptManager::engine->RegisterObjectType("Light", sizeof(Light*), asOBJ_PRIMITIVE);

ScriptManager::engine->RegisterObjectMethod("Light", "void setType(int)", asMETHOD(Wrap::MethodThroughPointer1<void, Light*,Light,setType,int>),asCALL_CDDECL_OBJLAST); 

So you see rather than dealing with instances of class Light, script should only deal with Light* and pass that around as you usually do with pointers. Here's the output:

Error	2	error C2645: no qualified name for pointer to member (found ':: *')	c:\rakengine\source\engine\script\scriptlight.cpp	16
Error	3	error C2143: syntax error : missing ',' before '`global namespace''	c:\rakengine\source\engine\script\scriptlight.cpp	16
Error	4	error C2882: '`global namespace'' : illegal use of namespace identifier in expression	c:\rakengine\source\engine\script\scriptlight.cpp	16
Error	5	fatal error C1903: unable to recover from previous error(s); stopping compilation	c:\rakengine\source\engine\script\scriptlight.cpp	16

Can I get an example of how to setup the syntax of MethodThroughPointer for this?
Advertisement
I don't know anything about Wrap::MethodThroughPointer1. But if you want to register a function with call convention asCALL_CDECL_OBJLAST, you need to use the asFUNCTION macro to obtain the function address:

asFUNCTION(Wrap::MethodThroughPointer1<void, Light*,Light,setType,int>)


Regards,
Andreas

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 figured out how to call it - now the problem is asFUNCTION won't do the cast:

My call:
ScriptManager::engine->RegisterObjectMethod("Light", "void setType(int)",  asFUNCTION((Wrap::MethodThroughPointer1<void, Light*,Light,&Light::setType,Light::LightTypes>)),asCALL_CDECL_OBJLAST);


Wrap:
template<typename R,typename P, typename C, typename M, typename P1>R MethodThroughPointer1(P1 p1, P* thisp) { return ((**thisp).*M)(p1); }


Compile error:
Error	1	error C2440: 'type cast' : cannot convert from 'R (__cdecl *)(P1,P *)' to 'void (__cdecl *)(void)'	c:\rakengine\source\engine\script\scriptlight.cpp	36


What I'm doing is no different from the other wrapper functions, which work fine, for example:

ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ADD_ASSIGN, "Vector3 &f(const Vector3 &in)", asFUNCTION((Wrap::AddAssign<Ogre::Vector3&,Ogre::Vector3,Ogre::Vector3&>)), asCALL_CDECL_OBJLAST);


Wrap:
template<typename R,typename F,typename S> R AddAssign(const S rhs, F* thisp) { return (*thisp) += rhs; }


The compile error refers to
inline asUPtr asFunctionPtr(asFUNCTION_t func){	asUPtr p;	asMemClear(&p, sizeof(p));	p.f.func = func;	return p;}


I tried taking everything out and doing reinterpret_cast, but the compiler still complains.

All I want to do is dereference a pointer for a function call, rather than call the same function directly.
First, understand that I wrote the Wrap namespace, not Witchlord. It's not actually part of Angelscript, nor is it even an 'official' addon.

It also looks like you've modified the template, and, in fact, this is part of the problem.

It originally looked like this
template<typename R,typename P, typename C, R(C::*M)(), typename P1>	R MethodThroughPointer1(P1 p1, P* thisp) { return ((**thisp).*M)(p1); }


And you got compiler errors from that. You should have, because (and I've just tested) the template is bugged. I overlooked something because I didn't really know what I was doing at the time. You also can't just make M a normal template parameter, because of how method pointers work. It doesn't work that way either.

However, this version should work!

template<typename R,typename P, typename C, typename P1, R(C::*M)(P1)>	R MethodThroughPointer1(P1 p1, P* thisp) { return ((**thisp).*M)(p1); }


Notice that the order of parameters changed slightly. Also, remember that it is very important to specify the parameter types exactly. If the function takes a const reference, you must include that type information when instantiating the template. This is because it will determine the type of method pointer you must give it. If you specify them wrong, you'll get an error like

1>.\source\bind_entity.cpp(33) : error C2440: 'specialization' : cannot convert from 'bool (__thiscall Tile::c_Entity::* )(const std::string &)' to 'bool (__thiscall Tile::c_Entity::* const )(std::string &)'


If you look at the two function signatures in the error message, the first is the function you gave it, and the second is the signature you told it to expect. I know this behavior is different than the rest of the Wrap templates, they will let you use the un-adorned type as the template argument, and in fact will force you to write const in the definition angelscript sees, but I really can't guess the 'proper form' for arbitrary methods!

You might have encountered this same error, or an error from angelscript, if you left the &s off the typenames in your call to AddAssign the first time around.

The other MethodThroughPointer# functions should be changed accordingly.
Thanks! Here is the correct version, for others that need it:

	template<typename R,typename P, typename C, R(C::*M)()>	R MethodThroughPointer0(P* thisp) { return ((**thisp).*M)(); }	template<typename R,typename P, typename C, typename P1, R(C::*M)(P1)>	R MethodThroughPointer1(P1 p1, P* thisp) { return ((**thisp).*M)(p1); }	template<typename R,typename P, typename C, typename P1, typename P2, R(C::*M)(P1,P2)>	R MethodThroughPointer2(P1 p1, P2 p2, P* thisp) { return ((**thisp).*M)(p1,p2); }	template<typename R,typename P, typename C, typename P1, typename P2, typename P3, R(C::*M)(P1,P2,P3)>	R MethodThroughPointer3(P1 p1, P2 p2, P3 p3, P* thisp) { return ((**thisp).*M)(p1,p2,p3); }	template<typename R,typename P, typename C, typename P1, typename P2, typename P3, typename P4, R(C::*M)(P1,P2,P3,P4)>	R MethodThroughPointer4(P1 p1, P2 p2, P3 p3, P4 p4, P* thisp) { return ((**thisp).*M)(p1,p2,p3,p4); }	template<typename R,typename P, typename C, typename P1, typename P2, typename P3, typename P4, typename P5, R(C::*M)(P1,P2,P3,P4,P5)>	R MethodThroughPointer5(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P* thisp) { return ((**thisp).*M)(p1,p2,p3,p4,p5); }


Usage:
ScriptManager::engine->RegisterObjectMethod("Light", "void setType(int)",  asFUNCTION((Wrap::MethodThroughPointer1<void, Light*,Light,Light::LightTypes,&Light::setType>)),asCALL_CDECL_OBJLAST);

By the way, this isn't easy to figure out - to use it with a const function, the const has to be placed as follows:

ScriptManager::engine->RegisterObjectMethod("Light", "int getType() const",  asFUNCTION((Wrap::MethodThroughPointer0<Light::LightTypes, Light*,Light const,&Light::getType>)),asCALL_CDECL_OBJLAST);
Not much to be done about that. Member function pointers are quirky.

This topic is closed to new replies.

Advertisement