首先了解我的问题的一些一般背景。
我需要使用
GNATCOLL
SQLite 绑定来绑定准备好的语句的一些参数。这些绑定期望 C 字符指针作为输入(除了其他东西)。这个要求给 Ada 带来了两个问题:
“绑定”指针指向的变量必须在准备好的语句完成之前不会消失(否则它将存储一个指向垃圾的指针)。但是,对于对相同类型的记录进行操作的查询,最好将参数绑定部分(从记录字段获取)提取到单独的过程中。如果此类过程在语句最终确定之前返回,则变量(在此类过程的堆栈上)将被删除,并且指针现在指向垃圾。
我只知道 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)使得使用对象成为一项非常艰巨的任务。我想尽可能远离这个功能。
一般来说,要进入该领域,您应该做几件事:
我根据上面提到的内容简化了你的一些代码:
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 编译器进行编译。