binding to methods of parent class - is it possible?

17 views
Skip to first unread message

avasilev

unread,
Sep 20, 2011, 10:52:23 AM9/20/11
to v8-juice-js-dev
Hi all,
Another question:
I am tryingto bind an existing class that is not v8 - aware. I have
created a simple proxy-class that inherits this class and I expose
this class to v8 via ClassCreator. When I bind the methods of my
class, I have to use the signature of the parent class. This compiles,
but I get a undefined reference linker error saying that
cvv8::JSToNative<class 'my parent class'>::operator() and
cvv8::TypeName<class 'my parent class'>::Value
are not defined. Since my parent class is not v8-aware, this is
explainable. Is it generally possible to bind inherited methods of a
class?

Thanks
Alex

Stephan Beal

unread,
Sep 20, 2011, 10:59:45 AM9/20/11
to v8-juice-js-dev
On Sep 20, 4:52 pm, avasilev <alxvasi...@gmail.com> wrote:
> I am tryingto bind an existing class that is not v8 - aware. I have
> created a simple proxy-class that inherits this class and I expose
> this class to v8 via ClassCreator. When I bind the methods of my
> class, I have to use the signature of the parent class. This compiles,
> but I get a undefined reference linker error saying that

i'm surprised it even compiles, actually :/.

> are not defined. Since my parent class is not v8-aware, this is
> explainable. Is it generally possible to bind inherited methods of a
> class?

In theory, yes, but in practice i have not yet found a way to do this
using the current templates. It could be done by writing another set
of wrappers which takes the parent and derived type, but that is a
significant amount of work. At some point i will probably end up
having this problem myself, and then you can be sure it will be
fixed ;).

Something you could try, but i don't know if this will work: in the
subclass, add a "using derivedFunctionName" line for each derived
function. i haven't ever tried that, but it "might work." That's would
be an ugly workaround, though.

avasilev

unread,
Sep 20, 2011, 11:04:33 AM9/20/11
to v8-juice-js-dev
Thanks, Stephen, for both answers. I will lok for solutions for these
problems, most probably I will have to write proxy methods in the v8-
aware class to solve both problems.
Thanks again
Alex

Stephan Beal

unread,
Sep 20, 2011, 11:05:33 AM9/20/11
to v8-juic...@googlegroups.com
On Tue, Sep 20, 2011 at 5:04 PM, avasilev <alxva...@gmail.com> wrote:
Thanks, Stephen, for both answers. I will lok for solutions for these
problems, most probably I will have to write proxy methods in the v8-
aware class to solve both problems.

i would love to see the solution you end up implementing. Maybe i can genericize it behind a set of templates.
 
Thanks again

problem == 0

:)

--
----- stephan beal
http://wanderinghorse.net/home/stephan/

avasilev

unread,
Sep 21, 2011, 7:34:18 AM9/21/11
to v8-juice-js-dev
In the end, I ended up writing my own wrapper, thus I will learn
better the generic V8 API and also have a better understanding of your
code, because some things are not clear enough for me.
Basically I created a base templated class, which sets up the binding
of the native pointer to the object, registers a constructor, and
registers a name for the js class. Initially I did it similar to you,
with a TypeName class, passed to the template. But it turned out I
need to construct the derived class in the Js constructor, which would
normally be done via a virtual method, which in turn means that all
this stuff cannot be static, i.e. I need to have an instance of the
object in order to register its constructor in V8, which is not
acceptable. This lead me to try something that I thought will not work
- I did something like this:

template <class DERIVED>
class Base
{
DERIVED* New() {return new DERIVED;}
const char* getTypeName() const {return DERIVED.typeName;}
.....
};

and use it like this:

class DerivedClass: Base<DerivedClass>
{
public:
const char* typeName;
}

const char* DerivedClass::typeName = "DerivedClass";

I am not used to thinking in template conventions, and normally a base
class cannot call into the derived class except for virtual functions.
I still wonder if this approach is portable, it works on MSVC.
What do you think about it?

Greetings
Alex


On Sep 20, 6:05 pm, Stephan Beal <sgb...@googlemail.com> wrote:

avasilev

unread,
Sep 21, 2011, 7:40:44 AM9/21/11
to v8-juice-js-dev
Actually I missed some static qualifiers, to the const and the
methods :)
Also the template is <typename DERIVED> not <class DERIVED>

avasilev

unread,
Sep 21, 2011, 7:47:33 AM9/21/11
to v8-juice-js-dev
Jsute tested this n mingw, ti works:
#include <stdio.h>

template <typename DERIVED>
class Base
{
public:
DERIVED* New() {return new DERIVED;}
static const char* getTypeName() {return DERIVED::typeName;}
};

class DerivedClass: public Base<DerivedClass>
{
public:
static const char* typeName;
};

const char* DerivedClass::typeName = "DerivedClass";


int main()
{
DerivedClass d;
printf("%s\n", d.getTypeName());
return 0;
}


On Sep 21, 2:34 pm, avasilev <alxvasi...@gmail.com> wrote:

Stephan Beal

unread,
Sep 21, 2011, 9:13:20 AM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 1:34 PM, avasilev <alxva...@gmail.com> wrote:
In the end, I ended up writing my own wrapper, thus I will learn
better the generic V8 API and also have a better understanding of your
code, because some things are not clear enough for me.

LOL! That's why i wrote that code - to help me understand v8 better (because the developers refuse to document it, which would have saved us all collectively thousands of hours).
 
Basically I created a base templated class, which sets up the binding
of the native pointer to the object, registers a constructor, and
...acceptable. This lead me to try something that I thought will not work

- I did something like this:
...
const char* DerivedClass::typeName = "DerivedClass";

I am not used to thinking in template conventions, and normally a base
class cannot call into the derived class except for virtual functions.
I still wonder if this approach is portable, it works on MSVC.
What do you think about it?

It looks portable to me. The only significant problem we have remaining vis-a-vis MSVC (that i'm aware of!) is the static members (e.g. your typeName), because when we populate them via templates and that can cause multiple instances of the values across multiple DLLs. i do NOT think that your approach will have that problem because you are setting them "by hand" instead of having templates do it. Just be sure to add this line in your C++ files and not your header files:

const char* DerivedClass::typeName = "DerivedClass";

then you won't have the "multiple instantiation" problem that we currently have with multiple DLLs using the same templates. We have that problem because the templates have to be in the headers, and they get instantiated by all code which compiles against them. Normally this is not a problem but becomes a problem when one uses multiple DLLs with the same bound classes.

Stephan Beal

unread,
Sep 21, 2011, 9:14:42 AM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 1:40 PM, avasilev <alxva...@gmail.com> wrote:
Actually I missed some static qualifiers, to the const and the
methods :)
Also the template is <typename DERIVED> not <class DERIVED>

Those are actually 100% equivalent: typename has two different uses, and one of those is an alias for "class" in this context. i personally prefer typename but many (most?) people prefer class because it's shorter.

avasilev

unread,
Sep 21, 2011, 9:39:12 AM9/21/11
to v8-juice-js-dev
Hehe, well, I have to confess I have only basic experience with
templates, but I am just discovering the things that can be done via
templates, thanks to your code and this convenient technique that I
found. I will have a moderate use of template in the project I am
working on.

About the linking problem with the type name - I guess you want to
avoid making the user write DECLARExxx and DEFINExxx style macros in
their classes. But you already have similar macros for the typename,
so probably you can solve the problem in the same way for the static
linkage. Maybe also for the native-js instance lookup maps. I guess I
am somewhat used to using such macros from wxWidgets where event
tables are declared and defined in this way.

Greetings
Alex

On Sep 21, 4:14 pm, Stephan Beal <sgb...@googlemail.com> wrote:

avasilev

unread,
Sep 21, 2011, 9:41:17 AM9/21/11
to v8-juice-js-dev
I just found that you have similar bindings for SpiderMonkey, I have
some working experience with SpiderMonkey and I will check them out.

Stephan Beal

unread,
Sep 21, 2011, 9:41:41 AM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 3:39 PM, avasilev <alxva...@gmail.com> wrote:
Hehe, well, I have to confess I have only basic experience with
templates, but I am just discovering the things that can be done via
templates, thanks to your code and this convenient technique that I
found. I will have a moderate use of template in the project I am
working on.

They take some getting used to, but you'll learn to love them :). (i tend to over-use them, actually.)
 
About the linking problem with the type name - I guess you want to
avoid making the user write DECLARExxx and DEFINExxx style macros in
their classes. But you already have similar macros for the typename,

Right :/. The macros are basically partial workarounds for the multi-DLL problem.

Stephan Beal

unread,
Sep 21, 2011, 9:44:37 AM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 3:41 PM, avasilev <alxva...@gmail.com> wrote:
I just found that you have similar bindings for SpiderMonkey, I have
some working experience with SpiderMonkey and I will check them out.

Just be aware that i don't maintain that project any more (it's unmaintained). The original v8-juice type-conversion code (from which cvv8 is derived) was in fact derived from the SpiderApe wrapper. The class wrapper in SpiderApe is horrible, though. It works but it's nowhere near as efficient as the newer code.

Also please keep in mind: ClassCreator and friends are basically proof-of-concept. Most developers end up writing their own class-binding mechanism because they all have different ideas about how to do it (and there's nothing wrong with that!). Even so, the rest of cvv8 can be used to help simplify such implementations if you care to write your own binding mechanism.

avasilev

unread,
Sep 21, 2011, 10:53:01 AM9/21/11
to v8-juice-js-dev
Yes, for sure your code can be helpful not only by using it as is, but
by studying it as well.

Now I have stumbled upon a new problem, which is not related to
support of cvv8 but still related to native binding. I need to
implement a non-static class factory, basically a small class that
exposes a constructor callback, but the actual creation of objects has
to depend on a state. This is why separated the construction code from
my lovely entirely static base-class for wrapped objects into a new
factory class. However, I have the problem that the constructor
callback, which is static, needs to somehow get the 'this' pointer of
the factory. I cannot set internal fields of the constructor function
or its prototype, so I cannot find a way how to bind my native factory
object to the constructor js method. Any ideas on that?

Greetings
Alex

On Sep 21, 4:44 pm, Stephan Beal <sgb...@googlemail.com> wrote:

Stephan Beal

unread,
Sep 21, 2011, 11:53:32 AM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 4:53 PM, avasilev <alxva...@gmail.com> wrote:
factory class. However, I have the problem that the constructor
callback, which is static, needs to somehow get the 'this' pointer of
the factory. I cannot set internal fields of the constructor function
or its prototype, so I cannot find a way how to bind my native factory
object to the constructor js method. Any ideas on that?

If you ignore any and all threading issues (which SHOULDN't be a problem because v8 only allows one thread to run at a time), you could do something like this:

In your derived type:

private:
  static FactoryType * currentFactory;
  friend class FactoryType;

In your factory function:

 DerivedType::currentFactory = this;
 MyInterface * result;
 try{
    result = new MyType;
}catch(...){
  DerivedType::currentFactory = NULL;
  throw;
}
DerivedType::currentFactory = NULL;
return result;

Basically what i've done there is reserve a "state slot" for us in the constructors. This is of course completely thread-unsafe, but v8 will only allow 1 thread to run at a time and it will never pre-empt your constructors. IF your constructors call into v8 themselves then there "might" be a problem, but not if you use v8::Locker at a higher level (e.g. in your factory or the code which calls it).

i used a similar approach once to feed state info into DLL static initialization phase:

MyDLLLoader::setCurrentState(whatever);
MyDLLLoader::loadDll("mydll");
MyDLLLoader::setCurrentState(NULL);

In the DLL static init code (triggered via static object construction in the DLL's global scope) i used:

... = MyDLLLoader::getCurrentState();

-- 

Stephan Beal

unread,
Sep 21, 2011, 11:54:28 AM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 5:53 PM, Stephan Beal <sgb...@googlemail.com> wrote:
 DerivedType::currentFactory = this;
 MyInterface * result;
    result = new MyType;

Sorry, i was very inconsistent there. Replace MyType with DerivedType and assume it inherits from the base MyInterface type.

Stephan Beal

unread,
Sep 21, 2011, 12:03:49 PM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 5:53 PM, Stephan Beal <sgb...@googlemail.com> wrote:
In the DLL static init code (triggered via static object construction in the DLL's global scope) i used:

i found an example:


the plugins then do something like:


which calls a macro which looks like:


which calls back into:


Whew. That might look complicated, but in your case i think that approach could be simplified.

avasilev

unread,
Sep 21, 2011, 12:33:19 PM9/21/11
to v8-juice-js-dev
Oups, I forgot to mention something. This approach is very nice in
single threaded usage. But my situation is different. But I don't want
to take your time with these problems, because they are not really
related to cvv8, so feel free to ignore the rest of the message. You
have helped me enough already. Just in case you are interested, below
is explanation of the problem:

Basically, I will have separate isolates running in different threads,
in parallel. They will of course share the same classes. My particular
problem is the following:
I have a Socket javascript-bound class. This class, in its
constructor, takes a pointer to boolean, which is a signal for thread
termination. In this way, everything that is a blocking call monitors
this boolean location, and will abort when it is set. So in the socket
constructor, I want to pass it the address of the terminate flag of
the thread in which the context is running. So, I will need per-thread
state... I couldn't find a way to make this with the help of V8
itself, and am thinking to use a dynamic structure bound to the global
JS object, which will hold lists of all registered types (i.e.
constructors), and maps of objects for each type (in the way you do, I
just studied your code in more detail). I will also put a constructor
and destructor functor for each type, to be able to do stateful
creation and destruction.

And now I have a problem because I need to make functors of some base
type, in order to define the structure elements.

Stephan Beal

unread,
Sep 21, 2011, 12:38:34 PM9/21/11
to v8-juic...@googlegroups.com
On Wed, Sep 21, 2011 at 6:33 PM, avasilev <alxva...@gmail.com> wrote:
...related to cvv8, so feel free to ignore the rest of the message. You

have helped me enough already. Just in case you are interested, below
is explanation of the problem:

i'm glad i could help. i'm not ignoring your question, but i have NO experience with Isolates, so i can't say anything sensible in response :).

Anton Yemelyanov

unread,
Sep 21, 2011, 7:19:02 PM9/21/11
to v8-juic...@googlegroups.com
Is there any documentation / examples on isolates use?  I looked a while back but couldn't find anything.  Would really appreciate any pointers...

Anton

Stephan Beal

unread,
Sep 22, 2011, 3:06:56 AM9/22/11
to v8-juic...@googlegroups.com
On Thu, Sep 22, 2011 at 1:19 AM, Anton Yemelyanov <anton.ye...@gmail.com> wrote:
Is there any documentation / examples on isolates use?  I looked a while back but couldn't find anything.  Would really appreciate any pointers...

i've begged the devs several times to either WTFM or give me a contract to do it, but they won't to do it. 

Anton Yemelyanov

unread,
Sep 22, 2011, 3:17:49 AM9/22/11
to v8-juic...@googlegroups.com
Hence the question to Alex :)

If we'd know how, I am sure we would make some useful wrappers around this...

ASY

Brandon Harvey

unread,
Oct 6, 2012, 12:19:20 AM10/6/12
to v8-juic...@googlegroups.com
I'd like to return to the simplest version of this question, and ask if there has been any new thinking on the subject.

I have an existing, somewhat large C++ library I'm binding to JS.  There's quite a tree of classes -- in all, there may be many dozens, even a hundred.  I was hoping to bind just a few of the most useful classes to JS -- classes that tend to appear at the 'bottom' of the inheritance chain.  But the trouble is that these classes, of course, inherit quite a few methods, which is the whole point of their utility.

I've used ClassCreator to bind the classes of interest and it's gone reasonably well.  (The clang compiler in Mac OS X Mountain Lion will not accept cvv8, which is too bad. I can post about that separately if you're willing to look into it.)

What I haven't yet managed to do is to bind any of the inherited methods -- which is essential.

Let's say I have class Foo and it has a parent class Bar.  The method "Hi" is defined up in Bar.  Bar is NOT bound into ClassCreator world.  Here's an attempt to bind to Hi.

void bind_Foo (v8::Handle<v8::Object> dest )
{ typedef cv::ClassCreator<Foo> CC;
  CC & cc (CC::Instance ());
  if (cc . IsSealed () ) { // the binding was already initialized.
      cc . AddClassTo (cv::TypeName<Foo>::Value, dest );
      return;
  }
  // Else initialize the bindings...
  cc
      ("Hi", cv::MethodToInCa<Foo, int(), &Foo::Hi>::Call)
      .AddClassTo (cv::TypeName<Bar>::Value, dest );

  // cc.Inherit<Bar>();  // would this help?
}
 
I've tried inserting Bar in place of Foo in various ways. Some of these don't compile, some don't link. I have a limited grasp of template magic at this point, so I can't say I fully understand what's at issue.  

Do I need to go ahead and bite the bullet and do ClassCreator stuff for Bar, and its parent classes, and their parent classes (ad infinitum)?  It seems like this shouldn't be necessary -- the C++ compiler has all the information it needs to know that if I call .Hi() on some Foo object, it will work.  It surprises me that that isn't easily expressible here.  

Thanks!
Brandon

Reply all
Reply to author
Forward
0 new messages