views:

1032

answers:

3

I am hosting SpiderMonkey in a current project and would like to have template functions generate some of the simple property get/set methods, eg:

template <typename TClassImpl, int32 TClassImpl::*mem>
JSBool JS_DLL_CALLBACK WriteProp(JSContext* cx, JSObject* obj, jsval id, jsval* vp)
{
 if (TClassImpl* pImpl = (TClassImpl*)::JS_GetInstancePrivate(cx, obj, &TClassImpl::s_JsClass, NULL))
  return ::JS_ValueToInt32(cx, *vp, &(pImpl->*mem));
 return JS_FALSE;
}

Used:

::JSPropertySpec Vec2::s_JsProps[] = {
 {"x", 1, JSPROP_PERMANENT, &JsWrap::ReadProp<Vec2, &Vec2::x>, &JsWrap::WriteProp<Vec2, &Vec2::x>},
 {"y", 2, JSPROP_PERMANENT, &JsWrap::ReadProp<Vec2, &Vec2::y>, &JsWrap::WriteProp<Vec2, &Vec2::y>},
 {0}
};

This works fine, however, if I add another member type:

template <typename TClassImpl, JSObject* TClassImpl::*mem>
JSBool JS_DLL_CALLBACK WriteProp(JSContext* cx, JSObject* obj, jsval id, jsval* vp)
{
 if (TClassImpl* pImpl = (TClassImpl*)::JS_GetInstancePrivate(cx, obj, &TClassImpl::s_JsClass, NULL))
  return ::JS_ValueToObject(cx, *vp, &(pImpl->*mem));
 return JS_FALSE;
}

Then Visual C++ 9 attempts to use the JSObject* wrapper for int32 members!

1>d:\projects\testing\jswnd\src\main.cpp(93) : error C2440: 'specialization' : cannot convert from 'int32 JsGlobal::Vec2::* ' to 'JSObject *JsGlobal::Vec2::* const '
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>d:\projects\testing\jswnd\src\main.cpp(93) : error C2973: 'JsWrap::ReadProp' : invalid template argument 'int32 JsGlobal::Vec2::* '
1>        d:\projects\testing\jswnd\src\wrap_js.h(64) : see declaration of 'JsWrap::ReadProp'
1>d:\projects\testing\jswnd\src\main.cpp(93) : error C2440: 'initializing' : cannot convert from 'overloaded-function' to 'JSPropertyOp'
1>        None of the functions with this name in scope match the target type

Surprisingly, parening JSObject* incurs a parse error! (unexpected '('). This is probably a VC++ error (can anyone test that "template void foo() {}" compiles in GCC?). Same error with "typedef JSObject* PObject; ..., PObject TClassImpl::mem>", void, struct Undefined*, and double. Since the function usage is fully instantiated: "&ReadProp", there should be no normal function overload semantics coming into play, it is a defined function at that point and gets priority over template functions. It seems the template ordering is failing here.

Vec2 is just:

class Vec2
{
public:
 int32 x, y;

 Vec2(JSContext* cx, JSObject* obj, uintN argc, jsval* argv);

 static ::JSClass s_JsClass;
 static ::JSPropertySpec s_JsProps[];
};

JSPropertySpec is described in JSAPI link in OP, taken from header:

typedef JSBool
(* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id,
                                 jsval *vp);

...

struct JSPropertySpec {
    const char      *name;
    int8            tinyid;
    uint8           flags;
    JSPropertyOp    getter;
    JSPropertyOp    setter;
};
A: 

Try changing the JSObject * to another pointer type to see if that reproduces the error. Is JSObject defined at the point of use? Also, maybe JSObject* needs to be in parens.

Lou Franco
No further parens should be needed here. It's not a like a pointer to member function; this is just a pointer to member.
DrPizza
A: 

I am certainly no template guru, but does this boil down to a subtle case of trying to differentiate overloads based purely on the return type?

Since C++ doesn't allow overloading of functions based on return type, perhaps the same thing applies to template parameters.

Rob Walker
Nope, overloaded functions that differ only by template arguments are permitted.
DrPizza
The "int class:*member" type is a pointer to a member int, not a member function
Ben Collins
Of course, shouldn't try thinking on Sundays! I thought it was missing some parenthesis...
Rob Walker
+3  A: 

Pretty sure VC++ has "issues" here. Comeau and g++ 4.2 are both happy with the following program:

struct X
{
    int i;
    void* p;
};

template<int X::*P>
void foo(X* t)
{
    t->*P = 0;
}

template<void* X::*P>
void foo(X* t)
{
    t->*P = 0;
}

int main()
{
    X x;
    foo<&X::i>(&x);
    foo<&X::p>(&x);
}

VC++ 2008SP1, however, is having none of it.

I haven't the time to read through my standard to find out exactly what's what... but I think VC++ is in the wrong here.

DrPizza
This seems likely :'(Anyone put a bug in Connect yet?
Simon Buchan
I'd want to read through the spec before filing this in Connect. However, figuring out whether VC++ is wrong from the spec will be non-trivial.Maybe post to comp.whatever-it-is.c++ to see if one of them can language lawyer up and post chapter and verse?
DrPizza
Well, I was meant seeing if this is a knowen issue (turns out it is). My english is the bad.https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=362170
Simon Buchan
Yup. known bug that "doesn't meet the triage bar". Hrmph.
Ben Collins
Isn't that always the case. I particularly love how they're now marked as "WontFix". Microserfs tell me "that only means WontFix for the current version", but that's bloody well not what it looks like...
DrPizza