From p.singleton@keele.ac.uk  Mon May 22 23:19:38 2000
Received: from mail12.svr.pol.co.uk (mail12.svr.pol.co.uk [195.92.193.215])
	by swi.psy.uva.nl (8.9.3/8.9.3) with ESMTP id XAA11756
	for <prolog@swi.psy.uva.nl>; Mon, 22 May 2000 23:19:38 +0200 (MET DST)
Received: from modem4294967250.firearms.dialup.pol.co.uk ([195.92.2.174] helo=keele.ac.uk)
	by mail12.svr.pol.co.uk with esmtp (Exim 3.13 #0)
	id 12tzcG-0001CK-00
	for prolog@swi.psy.uva.nl; Mon, 22 May 2000 22:20:00 +0100
Message-ID: <3929A3C2.56584CF6@keele.ac.uk>
Date: Mon, 22 May 2000 22:16:50 +0100
From: Paul Singleton <p.singleton@keele.ac.uk>
Organization: SmartArts Computing Consultancy
X-Mailer: Mozilla 4.7 [en] (WinNT; I)
X-Accept-Language: en
MIME-Version: 1.0
To: SWI Prolog <prolog@swi.psy.uva.nl>
Subject: Re: non-determinism & cleanup [was: Fwd: Non-redundant wrappers?]
References: <200005181416.QAA08958@gollem.swi.psy.uva.nl> <39256DA9.DAE66520@keele.ac.uk>
Content-Type: multipart/mixed;
 boundary="------------405CC8295F73AFBA83A7EB2F"

This is a multi-part message in MIME format.
--------------405CC8295F73AFBA83A7EB2F
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Paul Singleton wrote:

> OK, this idiom isn't completely general (?), but it's common enough so I've
> implemented a generic foreign predicate
> 
>   cleanup_open_get_close(OpenPredName, GetPredName, ClosePredName, In, Out) :-
>     % a PL_FA_NONDETERMINISTIC foreign function using PL_call_predicate()
>     % on OpenPredName/2, GetPredName/2, ClosePredName/1

It's been suggested I post this, so here it is (two attachments).  I believe the
structure of the code is right, and it's usable, but it needs more work to
handle modules and exceptions, at least.

My MS VC++ 5 makefile effectively compiles it like this: your mileage may vary

  CL.EXE /LD /I "$(PL_HOME)/include" $*.c "$(PL_HOME)/lib/libpl.lib"

Paul Singleton
--------------405CC8295F73AFBA83A7EB2F
Content-Type: application/x-unknown-content-type-cfile;
 name="cleanup.c"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="cleanup.c"

I2luY2x1ZGUJPHdpbmRvd3MuaD4KI2luY2x1ZGUJPFNXSS1Qcm9sb2cuaD4KCgp0eXBlZGVm
IHN0cnVjdCB7CglyZWNvcmRfdAlzdGF0ZTsJCS8vIHJlZiB0byBTdGF0ZSBmcm9tIHRoZSAi
b3BlbigrSW4sLVN0YXRlKSIgZ29hbAoJcHJlZGljYXRlX3QJcHJlZDI7CQkvLyB0aGUgImdl
dCgrU3RhdGUsLU91dCkiIHByZWRpY2F0ZQoJcHJlZGljYXRlX3QJcHJlZDM7CQkvLyB0aGUg
ImNsb3NlKCtTdGF0ZSkiIHByZWRpY2F0ZQoJfQkJCQkJY29udGV4dDsKCgppbnQJCQljbGVh
bnVwX29wZW5fZ2V0X2Nsb3NlXzEoY29udGV4dCosIHRlcm1fdCk7CmludAkJCWNsZWFudXBf
b3Blbl9nZXRfY2xvc2VfMihjb250ZXh0Kik7CgovKgolVCBjbGVhbnVwX29wZW5fZ2V0X2Ns
b3NlKCArYXRvbSwgK2F0b20sICthdG9tLCArdGVybSwgLXRlcm0pCiAqLwpmb3JlaWduX3QK
Y2xlYW51cF9vcGVuX2dldF9jbG9zZV9wbGMoCgl0ZXJtX3QJCXRwMSwJCS8vIG5hbWUgb2Yg
dGhlICJvcGVuKCtJbiwtU3RhdGUpIiBwcmVkaWNhdGUKCXRlcm1fdAkJdHAyLAkJLy8gbmFt
ZSBvZiB0aGUgImdldCgrU3RhdGUsLU91dCkiIHByZWRpY2F0ZQoJdGVybV90CQl0cDMsCQkv
LyBuYW1lIG9mIHRoZSAiY2xvc2UoK1N0YXRlKSIgcHJlZGljYXRlCgl0ZXJtX3QJCXRpbiwJ
CS8vICJJbiIgYXJndW1lbnQgdG8gdGhlIG9wZW4vMiBjYWxsCgl0ZXJtX3QJCXRvdXQsCQkv
LyAiT3V0IiBhcmd1bWVudCBvZiBlYWNoIGdldC8yIGNhbGwKCWNvbnRyb2xfdAljdHJsCQkv
LyBhZGRlZCBieSBTV0ktUHJvbG9nIHRvIGNhcnJ5IGNvbnRleHQgZXRjLiBmcm9tIGNhbGwg
dG8gcmVkbwoJKQoJewoJY2hhcgkJKnAxOwkJLy8gbnVsbC10ZXJtaW5hdGVkIHN0cmluZyBm
cm9tIHRwMQoJY2hhcgkJKnAyOwkJLy8gbnVsbC10ZXJtaW5hdGVkIHN0cmluZyBmcm9tIHRw
MgoJY2hhcgkJKnAzOwkJLy8gbnVsbC10ZXJtaW5hdGVkIHN0cmluZyBmcm9tIHRwMwoJY29u
dGV4dAkJKmN0eHQ7CQkvLyBjYWxsLXRvLXJlZG8gY29udGV4dAoJcHJlZGljYXRlX3QJcHJl
ZDE7CQkvLyB0aGUgb3Blbi8yIHByZWRpY2F0ZQoJdGVybV90CQlhcmdzMTsJCS8vIGFyZ3Mg
b2YgdGhlIG9wZW4vMiBjYWxsCgoJc3dpdGNoICggUExfZm9yZWlnbl9jb250cm9sKGN0cmwp
ICkKCQl7CgljYXNlIFBMX0ZJUlNUX0NBTEw6CgkJaWYJKAkhUExfZ2V0X2F0b21fY2hhcnMo
dHAxLCAmcDEpCgkJCXx8CSFQTF9nZXRfYXRvbV9jaGFycyh0cDIsICZwMikKCQkJfHwJIVBM
X2dldF9hdG9tX2NoYXJzKHRwMywgJnAzKQoJCQkpCgkJCXsKCQkJUExfZmFpbDsJLy8gYmFk
IGFyZ3M6IG91Z2h0YSByYWlzZSBleGNlcHRpb24gKGluc3RhbnRpYXRpb24gb3IgdHlwZSBl
cnJvcikKCQkJfQoJCWN0eHQgPSBtYWxsb2Moc2l6ZW9mKGNvbnRleHQpKTsJCQkJLy8gb3Vn
aHRhIGNoZWNrIHN1Y2Nlc3MKCQlwcmVkMSA9IFBMX3ByZWRpY2F0ZShwMSwgMiwgTlVMTCk7
CQkJLy8gUExfcHJlZGljYXRlKCkgYWx3YXlzIHN1Y2NlZWRzCgkJY3R4dC0+cHJlZDIgPSBQ
TF9wcmVkaWNhdGUocDIsIDIsIE5VTEwpOwkvLyAoZG9lc24ndCBpbXBseSB0aGUgcmVmZXJl
bmNlZAoJCWN0eHQtPnByZWQzID0gUExfcHJlZGljYXRlKHAzLCAxLCBOVUxMKTsJLy8gcHJl
ZGljYXRlIGV4aXN0cykKCgkJYXJnczEgPSBQTF9uZXdfdGVybV9yZWZzKDIpOwoJCVBMX3B1
dF90ZXJtKCBhcmdzMSwgdGluKTsJCQkJCS8vIGxlYXZlIGFyZ3MxKzEgYXMgbmV3IHZhcmlh
YmxlICg/KQoKCQlpZiAoIFBMX2NhbGxfcHJlZGljYXRlKE5VTEwsIFBMX1FfTk9STUFMLCBw
cmVkMSwgYXJnczEpICkJLy8gaW4gY29udGV4dCBtb2R1bGUKCQkJewoJCQljdHh0LT5zdGF0
ZSA9IFBMX3JlY29yZChhcmdzMSsxKTsJCS8vIHNhdmUgc3RhdGUgaW4gInRlcm0iIGRhdGFi
YXNlCgkJCWlmICggY2xlYW51cF9vcGVuX2dldF9jbG9zZV8xKGN0eHQsdG91dCkgKQoJCQkJ
ewoJCQkJUExfcmV0cnlfYWRkcmVzcyggY3R4dCk7CQkJLy8gc2F2ZSBjb250ZXh0IGZvciBw
b3NzaWJsZSByZWRvCgkJCQl9CgkJCWVsc2UKCQkJCXsKCQkJCVBMX2ZhaWw7CQkvLyBTdGF0
ZSBpcyBjbG9zZWQsIGVyYXNlZCB3aXRoaW4gY2xlYW51cF9vcGVuX2dldF9jbG9zZV8xKCkK
CQkJCX0KCQkJfQoJCWVsc2UKCQkJewoJCQlmcmVlKCBjdHh0KTsJCS8vIG5vIFN0YXRlIHRv
IGNsb3NlIG9yIGVyYXNlCgkJCVBMX2ZhaWw7CgkJCX0KCQlicmVhazsJCQkJCS8vIHVucmVh
Y2hhYmxlIGJ1dCBnb29kIHN0eWxlICg/KQoKCWNhc2UgUExfUkVETzoKCQljdHh0ID0gUExf
Zm9yZWlnbl9jb250ZXh0X2FkZHJlc3MoY3RybCk7CgkJaWYgKCBjbGVhbnVwX29wZW5fZ2V0
X2Nsb3NlXzEoY3R4dCx0b3V0KSApCgkJCXsKCQkJUExfcmV0cnlfYWRkcmVzcyggY3R4dCk7
CgkJCX0KCQllbHNlCgkJCXsKCQkJUExfZmFpbDsKCQkJfQoJCWJyZWFrOwkJCQkJLy8gdW5y
ZWFjaGFibGUgYnV0IGdvb2Qgc3R5bGUgKD8pCgoJY2FzZSBQTF9DVVRURUQ6CQkJCS8vIG9y
IGV4Y2VwdGlvbmVkCgkJY3R4dCA9IFBMX2ZvcmVpZ25fY29udGV4dF9hZGRyZXNzKGN0cmwp
OwoJCWlmICggIWNsZWFudXBfb3Blbl9nZXRfY2xvc2VfMihjdHh0KSApCgkJCXsKCQkJLy8g
Y2xvc2UoU3RhdGUpIGZhaWxlZDogd2Fybj8gcmFpc2UgZXhjZXB0aW9uPwoJCQl9CgkJUExf
c3VjY2VlZDsKCQlicmVhazsJCQkJCS8vIHVucmVhY2hhYmxlIGJ1dCBnb29kIHN0eWxlICg/
KQoJCX0KCX0KCgovKiBwZXJzaXN0IGZvciBhIHVuaWZpYWJsZSByZXN1bHQgZnJvbSBnZXQv
MjsgcmV0dXJuIEZBTFNFIGlmIG5vbmUgZm91bmQKICovCmludApjbGVhbnVwX29wZW5fZ2V0
X2Nsb3NlXzEoCgljb250ZXh0CQkqY3R4dCwKCXRlcm1fdAkJdG91dAoJKQoJewoJdGVybV90
CQlhcmdzMiA9IFBMX25ld190ZXJtX3JlZnMoMik7CgoJUExfcmVjb3JkZWQoIGN0eHQtPnN0
YXRlLCBhcmdzMik7CglpZiAoICFQTF9jYWxsX3ByZWRpY2F0ZShOVUxMLFBMX1FfTk9STUFM
LGN0eHQtPnByZWQyLGFyZ3MyKSApCgkJewoJCWlmICggIWNsZWFudXBfb3Blbl9nZXRfY2xv
c2VfMihjdHh0KSApCgkJCXsKCQkJLy8gY2xvc2UoU3RhdGUpIGZhaWxlZDogd2Fybj8gcmFp
c2UgZXhjZXB0aW9uPwoJCQl9CgkJcmV0dXJuIEZBTFNFOwoJCX0KCWVsc2UKCWlmICggUExf
dW5pZnkoYXJnczIrMSx0b3V0KSApCgkJewoJCXJldHVybiBUUlVFOwoJCX0KCWVsc2UKCQl7
CgkJcmV0dXJuIGNsZWFudXBfb3Blbl9nZXRfY2xvc2VfMSggY3R4dCwgdG91dCk7CS8vIHRy
eSBmb3IgYW5vdGhlciBzb2x1dGlvbgoJCX0KCX0KCgovKiBjbG9zZSBhbmQgZXJhc2UgdGhl
IHN0YXRlIChGQUxTRSBpbmRpY2F0ZXMgZmFpbHVyZSBvZiBjbG9zZS8xKQogKi8KaW50CmNs
ZWFudXBfb3Blbl9nZXRfY2xvc2VfMigKCWNvbnRleHQJCSpjdHh0CgkpCgl7Cgl0ZXJtX3QJ
CWFyZzMgPSBQTF9uZXdfdGVybV9yZWYoKTsKCWludAkJCXI7CgoJUExfcmVjb3JkZWQoIGN0
eHQtPnN0YXRlLCBhcmczKTsKCXIgPSBQTF9jYWxsX3ByZWRpY2F0ZShOVUxMLFBMX1FfTk9S
TUFMLGN0eHQtPnByZWQzLGFyZzMpOwoJUExfZXJhc2UoIGN0eHQtPnN0YXRlKTsKCWZyZWUo
IGN0eHQpOwoJcmV0dXJuIHI7Cgl9CgoKc3RhdGljClBMX2V4dGVuc2lvbiBwcmVkc3BlY3Nb
XSA9Cgl7IHsgImNsZWFudXBfb3Blbl9nZXRfY2xvc2UiLCA1LCBjbGVhbnVwX29wZW5fZ2V0
X2Nsb3NlX3BsYywgUExfRkFfTk9OREVURVJNSU5JU1RJQyB9LAoJICB7IE5VTEwsICAgICAg
ICAgICAgICAgICAgICAgMCwgTlVMTCwgICAgICAgICAgICAgICAgICAgICAgIDAgfSwKCX07
CgoKaW5zdGFsbF90Cmluc3RhbGwoKQoJewoKCVBMX3JlZ2lzdGVyX2V4dGVuc2lvbnMoIHBy
ZWRzcGVjcyk7Cgl9Cgo=
--------------405CC8295F73AFBA83A7EB2F
Content-Type: application/x-perl;
 name="cleanup.pl"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="cleanup.pl"

:- meta_predicate(cleanup_open_get_close(2,2,1,+,-)).

/*  Suppose we have three deterministic predicates open/2, get/2 and close/1, where
 *
 *  open(+In, -State) :-
 *      binds State to a reference to some (typically foreign) state
 *      which represents a sequence of values determined by In
 *
 *  get(+State, -Out) :-
 *      binds Out, in successive calls, to successive values from the sequence
 *      represented by State; fails silently if no (further) value is available
 *
 *  close(+State) :-
 *	    must be called to clean up the (typically foreign) state when no more
 *      values are available or required
 *
 *  We can wrap open/2 and get/2 as a single predicate which generates values
 *  on backtracking, e.g.:
 *
 *  generate(A, B) :-
 *      open(A, X),
 *      generate_1(X, B).
 *
 *  generate_1(X, B) :-
 *      get(X, B1),    % failure indicates no more Bs
 *      (    B = B1
 *      ;    generate_1(X, B)
 *      ).
 *
 *  but we are unable, using standard Prolog primitives, to arrange that the
 *  corresponding close/1 predicate will be called as soon as a call of generate/2
 *  is cut.
 *
 *  cleanup_open_get_close/5 is a SWI-Prolog 3.3.X nondeterministic foreign function
 *  which generates values as above and _does_ call a close/1 predicate as soon as
 *  is appropriate.  It must be passed the names of three (native or foreign)
 *  predicates; it assumes that their arguments (and general behaviour) are as
 *  open/2, get/2 and close/1 above.
 *
 *  If your "open" predicate takes more than one input argument, you can assemble them
 *  into a structure for menipulation by cleanup_open_get_close/5, e.g.
 *
 *  generate_thingies( In1, In2, In3, Out) :-
 *      cleanup_open_get_close( p1, p2, p3, x(In1,In2,In3), Out).
 *
 *  p1( x(In1,In2,In3), State) :-
 *      open_thingies( In1, In2, In3, State).
 *
 *  cleanup_file_char/2 is an example of its intended use.
 *
 *  Other applications: to ensure that database "cursors" are closed after
 *  exhausting or cutting backtracking through rows of the result relation of a query,
 *  or to free handles when generating keys or values from a Windows registry.
 *
 *  Paul Singleton, May 2000
 */


% cleanup_file_char(+FileName, -Char) :-
%	this illustrates the use of the foreign function cleanup_open_get_close/5
%   to generate the successive chars of a named file and automatically close the
%   stream (via which they are retrieved) upon cut or fail;
%	the "In" argument is a filename,
%	the "State" argument is a stream,
%	the "Out" argument is a char

cleanup_file_char(FileName, Char) :-
	cleanup_open_get_close(cleanup_p1, cleanup_p2, cleanup_p3, FileName, Char).


% cleanup_p1(+In, -State) :-
%	this serves as the open/2 component of cleanup_file_char/2, and will be called
%	once per call of cleanup_file_char/2; State is an unchanging reference
%	to the (changing) state of the generator

cleanup_p1(FileName, Stream) :-
	open(FileName, read, Stream, [type(text),eof_action(eof_code)]).


% cleanup_p2(+State, -Out) :-
%	this serves as the get/2 component of cleanup_file_char/2, and will be called
%	at each call and redo of cleanup_file_char/2 to yield the
%	next char in the sequence; failure indicates that the sequence is exhausted

cleanup_p2(Stream, Char) :-
	get0(Stream, Char),
	Char \== -1.


% cleanup_p3(+State) :-
%	

cleanup_p3(Stream) :-
	close(Stream).


% cleanup_open_get_close(+OpenPredName, +GetPredName, +ClosePredName, +In, -Out) :-
%	a PL_FA_NONDETERMINISTIC foreign function using PL_call_predicate()
%	on OpenPredName/2, GetPredName/2, ClosePredName/1
%	(see comment and example above)
%
%	Things to do:
%		* make it module-respecting
%		* handle exceptions raised by OpenPredName/2, GetPredName/2, ClosePredName/1
%		* establish that use of PL_call_predicate() from PL_CUTTED is supported

:- load_foreign_library('cleanup.dll').	% for Win32; alter suffix for your platform


--------------405CC8295F73AFBA83A7EB2F--


