如何访问记录字段

问题描述 投票:0回答:1

首先了解我的问题的一些一般背景。

我需要使用

GNATCOLL
SQLite 绑定来绑定准备好的语句的一些参数。这些绑定期望 C 字符指针作为输入(除了其他东西)。这个要求给 Ada 带来了两个问题:

问题1

“绑定”指针指向的变量必须在准备好的语句完成之前不会消失(否则它将存储一个指向垃圾的指针)。但是,对于对相同类型的记录进行操作的查询,最好将参数绑定部分(从记录字段获取)提取到单独的过程中。如果此类过程在语句最终确定之前返回,则变量(在此类过程的堆栈上)将被删除,并且指针现在指向垃圾。

问题2

我只知道 Ada 中创建指针的三个实例:

new
-ing、获取指向函数/过程的指针以及获取指向变量的指针。由于绑定需要一个指针,因此我不知道如何从记录中提取这样的指针,除非我将其“手动解包”到一堆局部变量中。不用说,这是丑陋的、重复的,而且很容易出现复制粘贴错误。这也会导致生命周期问题(因为以这种方式“解压”的变量将在它们用于捕获的实际值仍然存在之前被删除。)

示例

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;

我可以提出抗辩吗

我怀疑这可能可以通过使用物体来帮助(即

new
-ing某些东西)。我还没有研究过这种方法,因为我第一次使用 Ada 的经验(我仍在学习)在收缩对象时非常消极。释放加上缺乏方便的对象生命周期管理(例如,相当于 C++ RAII)使得使用对象成为一项非常艰巨的任务。我想尽可能远离这个功能。

sqlite pointers ada
1个回答
0
投票

一般来说,要进入该领域,您应该做几件事:

  1. 标签中的字段应标记为别名。这告诉编译器您可能计划在需要时访问它。
  2. Insert_Tag 的 Tag 参数可能还需要别名,以便编译器知道该参数是通过引用传递的。编译器不应该允许您访问非别名变量的字段(别名或非别名),因为一般情况是不安全的。您可能需要将此别名属性沿调用链向上传播到声明标记的位置,并确保它也被声明为别名。但是,您仍然会遇到 Tag_Access 是库级别而您的对象不是库级别的问题,因此您将需要使用 Unchecked_Access 属性而不是 Access 属性。然而,这样做意味着您有责任确保变量的生命周期不被违反。参数上的别名标记将有助于巩固这一点。或者,您可以更改 Bind_Tag 以采用匿名访问类型,然后可以使用常规访问属性。或者甚至更好,只需使用“别名输入标记”而不是访问类型。
  3. 您只需要虚拟变量 Tc,因为您声明了 Insert_Tag 来获取 Tag 参数的常量视图。将参数设置为 out 的别名可以让您避免在这种情况下使用虚拟变量。
  4. Ada 确实为 RAII 类型提供了一些支持。 Ada.Containers.Indefinite_Holders 用于单个对象,其他容器用于对象集合。在很多情况下,您不需要进行新分配或重新分配。我同意它并没有涵盖所有内容,但是对于从上面示例中显示的类型开始,它们应该能够为您处理对象的分配/解除分配。如果您有一些很难的例子,也许我们可以提出另一个问题并进行讨论,看看我们是否能为您找到解决方案。

我根据上面提到的内容简化了你的一些代码:

type Tag is record
  Field : aliased String(1..5) := "value";
end record;

type Tag_Access is access all Tag;

package Gnade is
    type Statement is null record;
    procedure Bind_Text(Stmt : Statement; Address : System.Address; Length : Natural) is null;
end Gnade;

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

procedure Insert_Tag (T : aliased in out Tag; Stmt : Gnade.Statement) is
begin
  Bind_Tag (T, Stmt);
  --  Here, bindings are dead, so we are executing garbage
  --Gnade.Step (Db, Stmt);
end Insert_Tag;

Statement : constant Gnade.Statement := (null record);
Value : aliased Tag;

此代码可以使用我尝试过的几个在线 Ada 编译器进行编译。

© www.soinside.com 2019 - 2024. All rights reserved.