comp.lang.ada
 help / color / mirror / Atom feed
* Cairo Ada binding questions
@ 2006-09-24 16:25 Damien Carbonne
  2006-09-24 19:50 ` Dmitry A. Kazakov
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Damien Carbonne @ 2006-09-24 16:25 UTC (permalink / raw)


I have started to write an Ada binding for Cairo
(http://cairographics.org). Cairo is written in C and I know how to
write a C/Ada thin binding.

My trouble is with a thick Ada API. I will try to explain it with an
example.
Cairo has a concept of Surface class that can is specialized into PDF 
Surface, SVG surface, etc. In the C API, Surfaces (as all Cairo 
"classes") are always manipulated through pointers. As a Surface can be 
shared, the C API provides a way to increment / decrement the reference 
counter.
So, I see here two concepts : Surface and Surface Handle.

In Ada, I could have something like this :
    type Surface_Record (<>) is tagged limited private;
    type Surface_Ref is access all Surface_Record'Class;
The would be used that way:
    procedure Foo (Surface : in [out] Surface_Record'Class; ...);
And when necessary:
   procedure Bar (Surface : in Surface_Ref; ...);
or, probably better(?):
   procedure Bar (Surface : access Surface_Record['Class]; ...);

However, by doing that, I let the user manage himself reference
counting. If I want to provide a Handle for ref counting, I have several
solutions based on smart pointers:
1) provide a Handle for root class of each hierarchy.
2) provide a Handle for each class of the hierarchy.
This second solution seems quite heavy to write. One needs to maintain
two parallel hierarchies, which is not really satisfying.
In both cases, smart pointers usage may also be quite uneasy, as there
is no way to make an object look like an access in Ada. At least, this
was impossible in Ada 95 (cf. Smart Pointers article by Matthew Heaney
on AdaPower).
So we would have something like this:
    type Surface_Handle is private;
    function Ref (Handle : Surface_Handle) return Surface_Ref;
that could be used like this :
    Surface : Surface_Handle;
begin
    Foo (Ref (Surface).all, ...);
or with the renaming trick to avoid this wordy thing.

There is also another solution, that would consist in hiding the Surface
concept, providing only the Surface_Handle one.
In that case, API would be modified like this :
    procedure Foo (Surface : in Surface_Handle'Class; ...);
Direction would (almost ?) always be in.

 From user point of view, this is far easier. From engineering point of
view, I really wonder.

Searching a solution to this issue, I looked at Ada.Text_IO and saw that
File_Type is often passed as a in parameter when, IMO, it should not.
For example :
    procedure New_Line (File : File_Type; Spacing : Positive_Count := 1);
Adding a new line modifies a file. This implies that File_Type must be
implemented with an indirection. Unless there is something I have not
understood, I don't find this really satisfying.

I Think my problem is quite general and may also exist in pure Ada code.
IIRC, GtkAda adopted the very first solution: It's user responsibility
to handle memory correctly.

To summarize solutions :
1) manual memory management
   1.a) Class Hierarchy and Ref (Ada Access) concepts
   1.b) ???
2) automatic memory management
   2.a) Class Hierarchy and Handle (Root only)
   2.b) Class Hierarchy and Handle hierarchy
   2.c) Handle hierarchy
   2.d) ???

Any advice would be appreciated !

Thanks.

Damien

PS1: If a Cairo Ada binding already exists, let me know !
PS2: If this binding reaches a satisfying state and is of interest to 
anyone, I will publish it.



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Cairo Ada binding questions
  2006-09-24 16:25 Cairo Ada binding questions Damien Carbonne
@ 2006-09-24 19:50 ` Dmitry A. Kazakov
  2006-09-24 20:43 ` tmoran
  2006-09-24 21:39 ` Jeffrey R. Carter
  2 siblings, 0 replies; 4+ messages in thread
From: Dmitry A. Kazakov @ 2006-09-24 19:50 UTC (permalink / raw)


On Sun, 24 Sep 2006 18:25:14 +0200, Damien Carbonne wrote:

> I have started to write an Ada binding for Cairo
> (http://cairographics.org). Cairo is written in C and I know how to
> write a C/Ada thin binding.
> 
> My trouble is with a thick Ada API. I will try to explain it with an
> example.
> Cairo has a concept of Surface class that can is specialized into PDF 
> Surface, SVG surface, etc. In the C API, Surfaces (as all Cairo 
> "classes") are always manipulated through pointers. As a Surface can be 
> shared, the C API provides a way to increment / decrement the reference 
> counter.
> So, I see here two concepts : Surface and Surface Handle.
> 
> In Ada, I could have something like this :
>     type Surface_Record (<>) is tagged limited private;
>     type Surface_Ref is access all Surface_Record'Class;
> The would be used that way:
>     procedure Foo (Surface : in [out] Surface_Record'Class; ...);
> And when necessary:
>    procedure Bar (Surface : in Surface_Ref; ...);
> or, probably better(?):
>    procedure Bar (Surface : access Surface_Record['Class]; ...);
> 
> However, by doing that, I let the user manage himself reference
> counting. If I want to provide a Handle for ref counting, I have several
> solutions based on smart pointers:
> 1) provide a Handle for root class of each hierarchy.
> 2) provide a Handle for each class of the hierarchy.
> This second solution seems quite heavy to write. One needs to maintain
> two parallel hierarchies, which is not really satisfying.
> In both cases, smart pointers usage may also be quite uneasy, as there
> is no way to make an object look like an access in Ada. At least, this
> was impossible in Ada 95 (cf. Smart Pointers article by Matthew Heaney
> on AdaPower).
> So we would have something like this:
>     type Surface_Handle is private;
>     function Ref (Handle : Surface_Handle) return Surface_Ref;
> that could be used like this :
>     Surface : Surface_Handle;
> begin
>     Foo (Ref (Surface).all, ...);
> or with the renaming trick to avoid this wordy thing.
> 
> There is also another solution, that would consist in hiding the Surface
> concept, providing only the Surface_Handle one.
> In that case, API would be modified like this :
>     procedure Foo (Surface : in Surface_Handle'Class; ...);
> Direction would (almost ?) always be in.
>  From user point of view, this is far easier. From engineering point of
> view, I really wonder.

This is the solution I am using. You can take a look at:

http://www.dmitry-kazakov.de/ada/components.htm

I define operations on both the objects and the handles to. The latter
delegate to the former:

package Surface is
   type Surface_Object is abstract new Entity with private;
   type Surface_Object_Ptr is access Surface_Object'Class;
   -- Primitive operations on Surface_Object (delegatee)
   procedure Foo (Surface : in out Surface_Object) is abstract;
   . . .
private
   type Surface_Object is abstract new Entity with ...;
   package Surface_Handles is
      new Object.Handle (Surface_Object, )Surface_Object_Ptr);

In a child package:

package Surface.Handle is
   type Surface_Handle is [tagged] private;
   -- Delegators to primitive operations on Surface_Object
   procedure Foo (Surface : in out Surface_Handle);
   . . .
private
   type Surface_Handle is new Surface_Handles with null record;

There are drawbacks of this approach. Ada lacks any support for delegation,
so all delegators like Foo need to be written manually:

   procedure Foo (Surface : in out Surface_Handle) is
   begin
      Foo (Ptr (Surface).all);
   end Foo;

It is extremely boring. Then, there are also problems with visibility
rules, which make it difficult to hide the object types and the primitive
operations on them.

> Searching a solution to this issue, I looked at Ada.Text_IO and saw that
> File_Type is often passed as a in parameter when, IMO, it should not.
> For example :
>     procedure New_Line (File : File_Type; Spacing : Positive_Count := 1);
> Adding a new line modifies a file. This implies that File_Type must be
> implemented with an indirection. Unless there is something I have not
> understood, I don't find this really satisfying.

As with any kind of referential objects, there exists a question what
in-out applies to. I tend to use in-out for the cases you describe, because
Surface_Handles is considered a full substitute for Surface_Object.

> I Think my problem is quite general and may also exist in pure Ada code.
> IIRC, GtkAda adopted the very first solution: It's user responsibility
> to handle memory correctly.

I found GtkAda (to be honest, Gtk+) design quite difficult to use. You
never know when and if a new object needs Unref. In the design I have
described, there is no such problem at all. All factories return handles,
instead of raw pointers. So I can happily forget about all the stuff like
reference sink vs. unref, about freeing new objects upon exceptions etc.
BTW, the behavior on exceptions is corrupt in GtkAda: I met no exception
handler in any Gtk_New, I looked. So when Initialize propagates an
exception, then the Gtk_New calling it leaks.

> To summarize solutions :
> 1) manual memory management
>    1.a) Class Hierarchy and Ref (Ada Access) concepts
>    1.b) ???
> 2) automatic memory management
>    2.a) Class Hierarchy and Handle (Root only)
>    2.b) Class Hierarchy and Handle hierarchy
>    2.c) Handle hierarchy
>    2.d) ???

As I said I am using 2. Class hierarchy is IMO inevitable. Under this
condition handles hierarchy becomes difficult [almost impossible] in Ada.
In my experience, a handles hierarchy would require downcasting. But if you
allowed this, then you could do the same with one handle type as well.

Possibly, it will be easier to have independent handle types and
conversions between them rather than a hierarchy. I know, it is ugly, but
at least it works.

> PS1: If a Cairo Ada binding already exists, let me know !
> PS2: If this binding reaches a satisfying state and is of interest to 
> anyone, I will publish it.

I host a small page with GtkAda contributions:

http://www.dmitry-kazakov.de/ada/gtkada_contributions.htm

Let me know when your binding will be ready.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Cairo Ada binding questions
  2006-09-24 16:25 Cairo Ada binding questions Damien Carbonne
  2006-09-24 19:50 ` Dmitry A. Kazakov
@ 2006-09-24 20:43 ` tmoran
  2006-09-24 21:39 ` Jeffrey R. Carter
  2 siblings, 0 replies; 4+ messages in thread
From: tmoran @ 2006-09-24 20:43 UTC (permalink / raw)


>My trouble is with a thick Ada API.
>...
>There is also another solution, that would consist in hiding the Surface
>concept, providing only the Surface_Handle one.
  How about letting the user deal with Surfaces, ie, his problem space,
and you deal with handles and reference counts in the computer-centric
view.  It will be more work for you, but you did say you're trying to
make a thick binding.
  With the CLAW thick binding to MS Windows, for instance, we have
type Root_Window_Type is abstract new Ada.Finalization.Controlled with private;
and the user works with various descendants of Root_Window_Type while CLAW
uses dispatching to call the routines specific to that child, and
typically uses inherited routines for things in common like reference
counting.

>Any advice would be appreciated !
  You might want to read:
http://www.rrsoftware.com/html/prodinf/triadapaper/triada.docsrc.html



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Cairo Ada binding questions
  2006-09-24 16:25 Cairo Ada binding questions Damien Carbonne
  2006-09-24 19:50 ` Dmitry A. Kazakov
  2006-09-24 20:43 ` tmoran
@ 2006-09-24 21:39 ` Jeffrey R. Carter
  2 siblings, 0 replies; 4+ messages in thread
From: Jeffrey R. Carter @ 2006-09-24 21:39 UTC (permalink / raw)


Damien Carbonne wrote:
> 
> My trouble is with a thick Ada API. I will try to explain it with an
> example.
> Cairo has a concept of Surface class that can is specialized into PDF 
> Surface, SVG surface, etc. In the C API, Surfaces (as all Cairo 
> "classes") are always manipulated through pointers. As a Surface can be 
> shared, the C API provides a way to increment / decrement the reference 
> counter.
> So, I see here two concepts : Surface and Surface Handle.

The pointers seem to be a C implementation artifact, and should probably 
be hidden from the thick binding's clients.

-- 
Jeff Carter
"C++ is like jamming a helicopter inside a Miata
and expecting some sort of improvement."
Drew Olbrich
51



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2006-09-24 21:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-09-24 16:25 Cairo Ada binding questions Damien Carbonne
2006-09-24 19:50 ` Dmitry A. Kazakov
2006-09-24 20:43 ` tmoran
2006-09-24 21:39 ` Jeffrey R. Carter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox