Re: [Vala] Assist with compile issue re Cairo.PathData



On Wed, 2013-10-16 at 09:48 +0200, Donn Ingle wrote:
On 15 October 2013 23:30, Evan Nemerson <evan coeus-group com> wrote:
You're close.  valac doesn't handle this very well because of the
anonymous structs in the C API.  It wants to assign to temporary
variables even when all you're doing is accessing fields, and since
they're structs the temporary variables aren't pointers, which will
cause the CC to complain (even though the types are compatible).  You
can use pointers to assign temporary variables in vala to work around
this:

It would be useless to say I understand that, but I get the start of a
fuzzy idea :) Heuristic: if the C library uses structs and unions and
arrays thereof, fear for my sanity!

Indeed.  Please don't take this as an example of good code.  It's a hack
to get around some problems Vala has with the API.  Anonymous structs
and unions are pretty rare in C APIs.

Furthermore, the fact that the C example abuses the relationship between
arrays and pointers is probably a bit confusing for someone without a
lot of experience with C.

I will use your code as an edit/run/redo lesson.

        while ( i < path.num_data ) {
          unowned Cairo.PathData? data = path.data[i];
I'm not sure why the data var was declared, it's not used.

Yeah, you can get rid of it.

          Cairo.PathDataHeader* header = &(path.data[i].header);
This I kind of get from a little reading of docs. (header is a
pointer, it's being given the address of some other thing.)

Do I have to perform a "delete header;" within the loop? At end of loop? At all?

No.  You only use delete if you own the value the pointer points to.
It's extremely impolite to delete other people's data, and will usually
result in a segfault.

          switch (header->type) {
            case Cairo.PathDataType.MOVE_TO:
              Cairo.PathDataPoint* point = &(path.data[i + 1].point);
Same delete questions on "point".

              do_move_to_things (point.x, point.y);
              break;
          }
          i += header.length;
        }

Sundry questions if you're in the mood:
unowned Cairo.PathData? data = path.data[i];
According to 'pointers' section in the Vala tut, this could be written as:
Cairo.PathData* data = path.data[i];
Is that so? (i.e. dropping the 'unowned', which is still a mystery to me.)

Yes, but in general you shouldn't use pointers in Vala unless you have a
very good reason.  "unowned" is pretty easy to understand, see
https://wiki.gnome.org/Vala/ReferenceHandling

The reason I used a pointer for the point variable is to avoid some
problematic C code which valac will generate due to the fact that
Cairo.PathDataPoint is really an anonymous struct.  If I could have used
an unowned reference I would have.

How about that nullable character ... Cairo.PathData*? = :-O

Pointers can be null already, so that doesn't really make sense.  And
it's a syntax error.  Theoretically I suppose Cairo.PathData?* could
make sense, but it's also a syntax error.

Can one declare vars before the loop, like:
Cairo.PathDataPoint* point;
And then within the loop:
point = &(path.data...);

You could, but unless you actually need to use the last point for
something else later on it's probably not a good idea.

In general you should declare variables in the smallest scope you can
without incurring an unacceptable performance penalty in order to
minimize memory usage.  If I had declared them outside of the loop they
would still be accessible outside the loop, and still taking up memory.
This isn't really a big deal for a pointer or an unowned reference to a
class, but imagine something like reading data from files:

        GLib.FileStream stream;
        foreach ( unowned string filename in filenames ) {
          stream = GLib.FileStream.open (filename, "r");
          do_stuff ();
        }

Now you have a file stream sitting around which will not be closed until
it goes out of scope, which could be a while (depending on how much
other stuff you have in the function).  If you put stream inside the
loop then it will automatically be closed immediately after you're done.

Of course, there are situations where you want to keep something around
between loop iterations because creating/destroying it is expensive.
For example, reading or writing chunks of a file:

        while ( !stream.eof () ) {
          uint8[] buf = new uint8[1024 * 1024];
          size_t bytes_read = stream.read (buf);
          do_stuff ();
        }

Allocating and freeing a 1MB buffer for each iteration isn't a good
idea, but you also don't want to naively keep it outside the loop and
wait to free it much longer than needed.  What you can do here is create
a new scope to surround both your variables and the loop:

        {
          uint8[] buf = new uint8[1024 * 1024];
          while ( !stream.eof () ) {
            size_t bytes_read = stream.read (buf);
            do_stuff ();
          }
        }

If creating/destroying a variable is basically free (like simpletype
structs such as size_t, as well as pointers and unowned references),
keep the variable inside the loop.  If it's expensive but reusable, but
it outside the loop but inside another small scope.  Only put stuff
completely outside if you need to use the last value for something
outside the loop.


-Evan

Attachment: signature.asc
Description: This is a digitally signed message part



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