Re: [Vala] Question about vapi and CCode attributes



There are a couple different things going on here, but it is pretty
simple if you take the time to understand what is going on instead of
just copying and pasting from examples.

As you are probably aware, Vala translates everything to C, and then the
C is then compiled to object code. One of the things that makes Vala a
good choice for programming in is that software written in Vala provides
a very usable C API, making it possible to use without too much effort
from virtually every other language. In contrast, code written in almost
any other language is extremely difficult to call from C, or almost any
other languages. The big exception to this is that languages written for
a virtual machine can often interact easily with other languages written
for that same virtual machine (e.g., Scala can call Java code, C# can
call VB.NET code, etc.).

I think everyone would agree that this is an extremely valuable asset
for Vala to possess, but there are a few interesting consequences. All
of your difficulties, it seems, relate to how Vala translates things to
C and what the tools you've chosen to use expect.

Unlike .NET or Java, which are compiled to an intermediate language [1],
or interpreted languages such as JavaScript or Python, valac must
translate symbols into a single flat namespace (basically, a big list of
symbols with no concept of namespaces, classes, structs, interfaces,
etc.). Furthermore, Vala must compile to something that is usable from
C, which eliminates using the possibility of using special characters
like C++ does (Vala can't convert Namespace.Class.method to
@Namespace/Class#method, because that isn't a valid C identifier).
Instead, when Vala needs to write a method name it first takes each
parent symbol and converts it from camel case to underscores and
lowercases the result (FooBarBaz becomes foo_bar_baz), then concatenates
the resulting strings into one long identifier (FooBar.Baz.run would
become foo_bar_baz_run).

For type names, Vala will keep the camel case and simply concatenate it
all together (Foo.Bar.Baz would become FooBarBaz). There are some
special cases revolving around constructors and asynchronous functions,
but that's not really important right now. What is important is that not
only does this generate an API that works well from C, but it generates
an API that conforms to what most people would expect a C API to look
like, especially if they are used to GObject-based code.

Obviously, trying to get programmers to adhere to a single coding style
is a bit like herding cats, so there are going to be exceptions to this.
Vala is able to accommodate these use cases by use of CCode attributes
[2]. Most commonly (and simplifying a bit):

      * cprefix is primarily used for namespaces. While when left to its
        own devices valac will simply concatenate the parent names to
        generate C type names, setting the cprefix allows you to
        override this. For instance, assuming you have a class
        Foo.Bar.Baz, setting a cprefix of "Fubar" on a your Bar
        namespace will result in a C name of "FubarBaz" for the Baz
        class.
      * lower_case_cprefix will override everything up to the start of
        the method name. For instance, a method named Foo.Bar.baz would
        normally result in a C name of foo_bar_baz. However, if you set
        the lower_case_cprefix of the bar class to be "fb_" [3], the
        resulting C name would be "fb_baz".
      * cname will set the C name to exactly what you specify here. For
        instance, if you set the cname of a Foo.Bar.baz method to
        "bacon", the name of the Foo.Bar.baz function in C will be
        "bacon".

GTK+ builder was designed to work with C applications. So, when you tell
it that the callback name for it to autoconnect to should be "baz", it
is going to look for a symbol named "baz". If you have a Foo.Bar.baz
method in Vala, GTK+ builder is not going to find it (if it did, what
happens when you have both a Foo.Bar.baz and a Bar.Foo.baz?). In other
words, GTK+ builder is looking for the C symbol you specify, so you need
to either figure out how Vala is going to translate your symbol name, or
tell it how to translate your symbol name (with CCode attributes).

Once you've made sure GTK+ builder is getting the correct symbol name,
you need to worry about how that function is called. In most C APIs, the
instance data is the first argument, and Vala adheres to this
convention. If you have a Foo.Bar.baz (int x) method (where bar is a
class), you'll see a C function that looks something like this:

void foo_bar_baz (FooBar* self, int x);

The problem is, most callbacks do things a bit different--they put the
instance data last (really they don't have an instance data parameter,
they have a void* user_data which could be anything, but Vala uses for
instance or closure data. That's not really important right now,
though).

When you are connecting a callback in Vala, valac will usually
automatically create a wrapper function which translates from from the
callback style (instance data last) to the standard style (instance data
first). The only time this doesn't happen is when you cast a delegate,
so if you're going to cast a delegate be sure you know what you're
doing!

Since you are using GTK+ builder to connect your signals, Vala has no
way of knowing that it should create this wrapper, and even if it did it
would have a different name than your method (two symbols can't have the
same name). So, what you have to do is tell it that, when it generates
the C, the instance data should be the last parameter, not the first.
You do this by setting the instance_pos CCode attribute to -1... Of
course it can be things other than -1--there are loads of examples of
this in the vapis, but be sure you understand the implications of
changing it.

Finally we come to the i18n/l10n support. In order to make the API as
easy to use as possible (specifically, on a C level), gettext forces you
to define a GETTEXT_PACKAGE variable so that it can use that package by
default. For instance, if you set define GETTEXT_PACKAGE as "foo", you
can just do _("string to translate") instead of dgettext ("foo", "string
to translate"). There are several ways to do this, and what I would
suggest would depend on your build system (basically, pass
-DGETTEXT_PACKAGE="foo" to gcc somehow).

By now, it is probably obvious why the config.vapi you pasted was
causing problems. You set the lower_case_cprefix to "" for the Foo
namespace, which means that instead of the C symbol for Foo.bar
generating the C symbol "foo_bar", it will just generate "bar". Your
solution was to add a cname annotation to get the c name for your method
back to what it should be, but what you probably really want is to just
set the cname for Foo.GETTEXT_PACKAGE to "GETTEXT_PACKAGE", and leave
the Foo namespace alone.



[1] http://en.wikipedia.org/wiki/Intermediate_language
[2] http://live.gnome.org/Vala/Manual/Attributes#C_code_attributes



On Wed, 2010-10-13 at 21:14 +0200, Tomasz Jankowski wrote:
Hello!

II'm working on GTK+ based application and I decided to use Vala to make my
life easier. I was wrong (partly)...

I design windows in Glade and then load created XML files GTK+ Builder.
According to samples at Vala's homepage I added additional attributes to my
code to used callbacks autoconnect:

[CCode (instance_pos = -1)]

public void on_about_menu_item_activate (Gtk.MenuItem menu_item)
{
    AboutDialog dialog = new AboutDialog ();

    dialog.display ();
}

Later I decided to add internationalization support and because I use
Autotools I looked how it's done in http://github.com/tadeboro/Sampala . My
config.vapi:

[CCode (prefix = "", lower_case_cprefix = "", cheader_filename =
"config.h")]


namespace Foo
{

/* Package information */

public const string PACKAGE_NAME;

public const string PACKAGE_STRING;

public const string PACKAGE_VERSION;


 /* Gettext package */

public const string GETTEXT_PACKAGE;

 /* Configured paths - these variables are not present in config.h, they are

* passed to underlying C code as cmd line macros. */

public const string LOCALEDIR;  /* /usr/local/share/locale  */

public const string PKGDATADIR; /* /usr/local/share/sampala */

}

I also added flags: "--vapidir=. --pkg=config" to Vala compiler (config.vapi
is placed in the same directory that source code files are) and flags to
GCC: -include
$(top_srcdir)/config.h -DLOCALEDIR=\""$(localedir)"\"
-DPKGDATADIR=\""$(datadir)"\"

When I added config.vapi to my project it broke callbacks autoconnect (but
localization works fine), runtime errors:

(foo-gtk:24482): Gtk-WARNING **: Could not find signal handler
'foo_on_quit_menu_item_activate'

(foo-gtk:24482): Gtk-WARNING **: Could not find signal
handler foo_on_about_menu_item_activate'

etc.


I stared reading Vala documentation, searching web and finally I figured
out, that this construction works fine for me:

[CCode (instance_pos = -1, cname = "foo_on_about_menu_item_activate")]
public void on_about_menu_item_activate (Gtk.MenuItem menu_item)
{
    AboutDialog dialog = new AboutDialog ();

    dialog.display ();
}


Questions:

   1. Why config.vapi addition broke callbacks autoconnecting?
   2. Is there any other way to achieve the same result
   without writing cname attribute for each callback?
   3. What does instance_pos attribute?
_______________________________________________
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]