โŒ About FreshRSS

Normal view

There are new articles available, click to refresh the page.
Before yesterdayNews from the Ada programming language world

Access-to-variable designates constant when trying to pass a pointer to vector element

This question is a follow-up to How to get access to a record field .

The more complex (now) code involves binding a whole vector of records. Something along the lines below:

type Tag is record
  Field : String := "value";
end record;

type Tag_Access is access all Tag;

package Tags_Vectors is new Indefinite_Vectors
  (Index_Type   => Positive,
   Element_Type => Wheel_Tag);

procedure Bind_Tag (T : in out Tag_Access; Stmt : Gnade.Statement) is
begin
    Gnade.Bind_Text (Stmt, T.Field'Address, T.Field'Length);
end Bind_Tag;

procedure Insert_Tags is
  Stmt : Gnade.Statement;
  Tags : Tags_Vectors.Vector;
  --  Make necessary arrangements to populate the Stmt and Tags
  C : Tags_Vectors.Cursor := Tags.First;
begin
  while Tags_Vectors.Has_Element (C) loop
         Bind_Tag (Stmt, Tags_Vectors.Element (C)'Access);
         Tags_Vectors.Next (C);
  end loop;
  Gnade.Step (Db, Stmt);
end Insert_Tag;

I'm not sure what kind of thing Tags_Vector.Element (C) returns. Syntactically, at least, it seems Ada doesn't object to this having an access attribute. But, I don't understand the error (which side does it think is the variable and which side is the constant?) Why is it bad that access to variable designates a constant? (Is it trying to say that I might be changing the value of a constant? -- but I never wanted any of those things to be constants...)

How to get access to a record field

Some general context for my problem first.

I need to bind some arguments of a prepared statement using GNATCOLL SQLite bindings. These bindings expect C character pointer as an input (beside other things). This requirements creates two problems on Ada's end:

Problem 1

The variable pointed to by the "bound" pointer must not perish until the prepared statement is finalized (otherwise it will store a pointer to garbage). But, for queries that operate on the same type of record it would be desirable to extract the part of binding of arguments (which are obtained from the record fields) into a separate procedure. If such procedure returns before the statement is finalized the variables (on the stack of such procedure) will be deleted, and pointers now point to garbage.

Problem 2

I only know of three instances in Ada of creating pointers: new-ing, taking a pointer to a function / procedure and taking a pointer to a variable. Since the bindings want a pointer, I don't know how to extract such a pointer from a record unless I "manually unpack" it into a bunch of local variables. Needless to say this is ugly, repetitive and very prone to copy-and-paste error. This also leads to the lifetime issues (since variables "unpacked" in such a way will be deleted before the actual value they are used to capture still exists.)

Example

type Tag is record
  Field : String := "value";
end record;

type Tag_Access is access all Tag;

procedure Bind_Tag (T : Tag_Access; Stmt : Gnade.Statement) is
  --  This variable will vanish before the statement is executed
  Field : aliased constant String := T.Field;
begin
    Gnade.Bind_Text (Stmt, Field'Address, Field'Length);
end Bind_Tag;

procedure Insert_Tag (T : Tag) is
  --  Necessary connection initialization and building of prepared statement
  Tc : Tag := T;  --  Creating a useless variable only to please the compiler
  Ta : Tag_Access := Tc'Access;
begin
  Bind_Tag (Ta, Stmt);
  --  Here, bindings are dead, so we are executing garbage
  Gnade.Step (Db, Stmt);
end Insert_Tag;

If I may enter a plea

I suspect this may be helped by using objects (i.e. new-ing something). I haven't researched this approach because my first experience with Ada (I'm still learning) was very negative when contracting objects. Deallocation combined with absence of convenient object lifetime management (eg. equivalent of C++ RAII) makes using objects a very daunting task. I would like to stay away from this functionality as much as possible.


Edit

I found a way out of this particular conundrum: turns out SQlite can be instructed to make copies when binding strings. This isn't ideal, but at least I can get the strings into the database.

This doesn't mean that the question is solved though. I'd still like to know a more general way of dealing with record fields. Something that in eg. C would be accomplished by taking a pointer to the struct and then adding the size of the fields preceding the field of interest and adding that the the pointer.

Import C function with void* parameter

I want to import a C function in Ada.

There is the C part :

void Register(const void *ctxt)
{
   saved_ctxt = ctxt; // This is a global variable 
}

void Send_Trace(const void *ctxt,
                const char *msg)
{
    if (saved_ctxt == ctxt)
    {
        // Do stuff with msg
    }
}

Now I want to use this function in a Ada program. There is my code :

type T_void is tagged null record;
package C renames Interfaces.C;

procedure Send_Trace_From_C(ctxt : in T_void;
                            msg  : in String)
is
    pragma Convention(C, T_void);

    procedure send_trace (A: out T_void; B : C.Strings.char_ptr);
    pragma import (C, Send_Trace, "Send_Trace");

    Char_ptr : C.Strings.char_ptr := C.Strings.New_String(msg);
begin
    send_trace (ctxt, Char_ptr);
end Send_Trace_From_C;

But I have errors :

pragma "convention" argument must be in same declarative part

warning "send_trace" involves a tagged type which does not correspond to any C type

How can I use a *void in Ada ?

โŒ
โŒ