Re: [Vala] wrapping structs / wrapping vapi classes



I'm now looking at the changes I need to make to the code generator.
(For my credentials I re-wrote the php4 swig code generator a few years ago)

Here's my test vala:

---8<--- main.vala ---8<------8<------8<------8<---
public class Action : Verbalizer {
    public override int connect(int x) { return 2; }
}
---8<------8<------8<------8<------8<------8<------8<---

and it compiles against this vapi:

---8<--- api.vapi ---8<------8<------8<------8<---
using GLib;
/* excuse the weird CCC_ prefix, it helps me see how C code is derived */
[CCode (lower_case_cprefix = "CCC_", cheader_filename = "api.h")]
namespace Api {
    [CCode (cname = "struct verbalizer_module_context", cprefix = "vmc",
    free_function = "FREE", vmt_expression="self->ops")]
    public class Verbalizer {
        public static Verbalizer fresh();
        [CCode ( instance_pos = -1 )]
        public virtual int connect(int x);
        public virtual int disconnect(int x);
    }
}
---8<------8<------8<------8<------8<------8<------8<---

and this header file

---8<--- api.h ---8<------8<------8<------8<---
#ifndef _API_H
#define API_H

struct verbalizer_ops {
        char* name;
        int (*connect)(int x);
        int (*disconnect)(int x);
};

struct verbalizer_context {
        int state;
};

struct verbalizer_module_context {
        struct verbalizer_module_context *prev, *next;
        struct verbalizer_context *ctx;
        int depth;
        const struct verbalizer_ops *ops;
        void *private_data;
};

#endif /* API_H */
---8<------8<------8<------8<------8<------8<------8<---


I'm now looking at the generated C to see which bits need changing, then
I'll track that down in the vala code generator.

The subclass Action instance definition in main.h is nearly right:
struct _Action {
    struct verbalizer_module_context parent_instance;
    ActionPrivate * priv;
};

but the first member should be a pointer, because the wrapped struct is
separately allocated, sometimes by the wrapped library. This also means
any access to parent properties that cross the vapi boundary will need a
-> instead of a . sticking in.

I realise that this damages the model of: a pointer to a subclass is a
pointer to the superclass, but I think it can be managed easily, and
gives us the ability to encapsulate structs as objects.

It also needs a vmt member called _base to hold overridden virtual
methods (see below).
This will be generated in the same way that virtual methods are written
to _ActionClass if they are virtual to this class instead of overridden
here and virtual to the parent class. (Hope that makes sense). It means
if we are a subclass of a vapi class, then for every virtual method in
the parent class, create an entry in the _base member.

The class definition is interesting. I'm not yet sure what should be
done here, but because it is subclassing a vapi class, the parent_class
should be a void*, unless a vapi CCode defines the type. I expect it
will remain null, because there is nothing for glib beyond this point.
struct _ActionClass {
    struct verbalizer_module_contextClass parent_class;
};

in main.c, the action_class_init needs changing:
static void action_class_init (ActionClass * klass) {
    action_parent_class = g_type_class_peek_parent (klass);
    CCC_VERBALIZER_CLASS (klass)->connect = action_real_connect;
}
For the immediate  subclass of a vapi class, g_type_class_peek_parent()
is no good, and like _ActionClass, it's hard to see what we could put here.
With vapi wrapped structs, overriding function pointers occurs on the
instance, not the class init, so this needs adding to the instance
constructor below

in main.c, the action_new constructor:
Action* action_new (void) {
    Action* self;
    self = ((Action*) (g_type_create_instance (TYPE_ACTION)));
    return self;
}

this is mostly fine, but it should also do:
  self->parent_instance=**VAPI_DEFINED_CONSTRUCTOR**
The parent struct instance might be allocated on the spot, or a
previously allocate struct inserting - not sure of the mechanisms for that.

it also should do the overrides that are normally in the class_init:
    self->_base->connect=CCC_VERBALIZER(self)->ops->connect;
    CCC_VERBALIZER(self)->ops->connect = action_real_connect;
(and if base.connect is called, it will invoke self->_base->connect)
but as I said earlier, there should probably be a macro to do that, e.g.:
    self->_base->connect=VERBALIZER_VMT(self)->connect;
    VERBALIZER_VMT(self)->connect = action_real_connect;

And now action_instance_init:
static void action_instance_init (Action * self) {
}

Err... looks fine, I still can't tell the difference between
instance_init and new. Maybe some of the stuff in new should have gone
in instance_init?

The invocation of an overridden method already works fine:
vmcconnect (CCC_VERBALIZER (a), 2);
as long as CCC_VERBALIZER is defined as:
#define CCC_VERBALIZER(self) (self->parent_instance)
but it is currently undefined.

finally,
static gint action_real_connect (struct verbalizer_module_context* base,
gint x) {
    Action * self;
    self = ACTION (base);
    return 2;
}

I like that it thinks it is getting in a struct
verbalizer_module_context*, but in this case maybe the generic
typecaster can't be used?
#define ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ACTION,
Action))
probably we want to do:
  self = ACTION(base->private_data);
because private_data of the api.h struct is for pointing to the subclass
instance.
This would apply only for ALL subclass methods that were overriding a
vapi virtual method.

So, for the record, I'm not trying to find the right parts of the code
generator to change.

I'm sure I'll get it but a guide would be helpful in stopping me do it
wrong the first few times.

I'm planning to add alternate generator actions based on node
attributes, mostly CCode.


Sam


* Sam Liddicott wrote, On 17/07/08 07:47:
I omitted to mention the self mapping that also must occur if vapi
wrapped structs can contain virtual methods.

The wrapped struct contains a private data pointer (they generally do)
which in this case will point to the subclass instance.
The subclass instance of course has a pointer to the super class
instance, as usual, and this will point to the struct instance.

When ANY vala code calls a method which is virtual to the vapi-wrapped
class, the "self" that is passed is the super-class instance of the
first subclass - i.e. the wrapped struct instance. I guess there will
be a variable number of parent-instance de-referencing going on,
depending on the depth of inheritance.

Conversely, the override methods for any method that is virtual to the
vapi-wrapped class will receive a "self" which is the wrapped struct,
and so it will have to follow the private data pointer of the struct
to get the real self.

When a subclass calls it's own overridden method this will involve
un-neccessary referencing to the wrapped struct and back again, but I
don't see a way around it.

This is all simple to build in, and requires a CCode directive for the
vapi code to identify which struct member is the private data, to be
used to hold subclass "self"

Finally, each subclass of a vapi-wrapped object having virtual methods
will need to have a backup table of overridden methods, so that
base.method can be used.
When the instance_init overrides the vmt in the wrapped struct, the
old value will be stored and used if base.method is invoked.

I think thats all?

Sam

Sam Liddicott wrote:
These suggested changes would permit vapi files to wrap structs that
contained a virtual method table.
If they are not objectionable to the development of Vala, I intend to
provide patches.

I like how vapi classes can wrap structs as classes; I'm
investigating how these can be almost first-class classes, especially
in the case where the struct contains function pointers, effectively
a virtual method table.

Vala implements calling virtual methods with a wrapper like this:

struct _youngerClass {
olderClass parent_class;
gint (*use_the_internet) (younger* self);
};
...
gint younger_use_the_internet (younger* self) {
return YOUNGER_GET_CLASS (self)->use_the_internet (self);
}

It seems like the line:
return YOUNGER_GET_CLASS (self)->use_the_internet (self);

Could be replaced with:
return YOUNGER_GET_VMT (self)->use_the_internet (self);

where in most cases
#define YOUNGER_GET_VMT YOUNGER_GET_CLASS

but this can be overridden in the VAPI file, maybe with
vmt_expression=".."

For samba4 ntvfs struct it would become
#define YOUNGER_GET_VMT(self) self->ops

so that the expanded code becomes
return self->ops->use_the_internet (self);

which would be spot on

However we need to convince vapi files to permit virtual methods too;
this vapi:

namespace Ntvfs {
[CCode (cname = "struct ntvfs_module_context", cprefix = "",
free_function = "talloc_free", create_function="xx", ref_function =
"talloc_reference", unref_function = "talloc_free"
vmt_expression="self->ops")]
public class Ntvfs {
public virtual int something_virtual();
}
}

does nothing with regard to "virtual" and calls in a subclass (or
anything) to something_virtual are left as calls to cprefix .
"something_virtual" but I think it should render to:
"self->ops->" . cprefix . "something_virtual(self)" (where the
position of self depends on instancepos and all that stuff as it
currently does).

It would likewise be possible to override such methods, in the
instead of this in the subclass _class_init:
NTVFS_CLASS (klass)->something_virtual =
subclass_real_something_virtual;
have this in the _instance_init
YOUNGER_GET_VMT(self)->something_virtual =
subclass_real_something_virtual;

So I guess we would need a CCode directive to indicate if the vmt was
per-class or per-instance.

Are these objectionable to the direction of Vala?
I would do something likewise for properties so they could be mapped
to the members or expressions of the struct instance.

Later, I also want to add C (or vala? - probably not) code blocks to
vapi files which would be treated as inline methods (!!) to the
caller, this can encapsulate special construction steps or other
massaging which is not in the wrapped library but which ought to be
done.

Sam


_______________________________________________
Vala-list mailing list
Vala-list gnome org
http://mail.gnome.org/mailman/listinfo/vala-list

_______________________________________________
Vala-list mailing list
Vala-list gnome org
http://mail.gnome.org/mailman/listinfo/vala-list





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]