I recently put together a system that I layered on top of RDNZL, in
order to experiment with some alternative ways of interacting with
CLR/.NET code. I'm hoping that some of the ideas could be incorporated
into RDNZL. Edi's too busy to look it over right now, and he suggested
that I offer for perusal here on the mailing list.
Aside from making type lookup a little easier, this system tries to
make your .NET-using Lisp code a bit more Lisp-like. Performance is
shabby because of the layering over RDNZL; much, if not all of this,
could eventually be fixed. Nonetheless, I've been using this system
successfully for the past few weeks as a vehicle for exploring Direct3D
programming on a WinXP system, using the LispWorks trial edition of
Lisp. In order to get reasonable performance, I had to use
CL-CLR:DEFINE-CLR-CALL (a thin veneer over DEFINE-RDNZL-CALL) for most
calls in the rendering loop.
This system *should* work with any Lisp that is supported by RDNZL.
The package includes several of Microsoft's Direct3D Tutorial programs,
translated to Lisp. If you have ASDF, RDNZL, and Direct3D installed,
you ought to be able to run the examples with a minimum of fuss.
You can download it from here: http:\\..................\cl-clr.zip
Comments are welcome. You are also encouraged to laugh at the
excessively long TODO.txt file.
--
Dan Muller
----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.
(Gruess Gott, Edi, hoffentlich war der Umzug nicht allzu anstrengend!)
Remember that problem I was having importing some types? I'm not sure
why, but on my system, System.Type.GetTypes is more picky than RDNZL
seems to expect:
;;;
;;; To demonstrate these failing tests, start with a clean Lisp image,
;;; then load RDNZL, then load this file. Assemblies can't be
;;; unloaded except by unloading their containing application domain,
;;; and you can't unload the default application domain.
;;;
;;; It's possible that the tests are sensitive to the assemblies
;;; available on my machine, but I can't find an indication in
;;; Microsoft's documentation of why that might be the case.
;;;
(in-package :cl-user)
(use-package :rdnzl)
(import-assembly "System.Windows.Forms")
;; This fails, because the system doesn't find the type.
;; Comment it out and try reloading to move on to the next test.
(new "System.Windows.Forms.Form")
;; This fails, too. Same problem.
;; Comment it out and try reloading to move on to the next test.
(import-type "System.Windows.Forms.Form")
;; I also tried direct calls to System.Type.GetType with various
;; abbreviations of the fully qualified type name, leaving off
;; trailing pieces of the full assembly name. None of these succeeded.
;; This succeeds.
(import-type "System.Windows.Forms.Form"
(load-assembly "System.Windows.Forms"))
;; This succeeds, because RDNZL now remembers what assembly it found
;; Form in.
(let ((form
;; This fails, because the system doesn't find the type.
(new "System.Windows.Forms.Form")))
(invoke form "Dispose"))
<end of test code>
I spent a lot of time poking around in RDNZL and the .NET framework,
and reading documentation. The documentation is for 2.0, FWIW, but I
was careful to look only at classes and methods that were available in
1.1. What follows is a rather lengthy discussion of some possible
improvements/fixes I've come up with. This goes rather beyond fixing
the immediate problem, but please give it some consideration.
- Have IMPORT-TYPE always retrieve and cache the assembly-qualified
name of the type.
Downside: This will include the exact version. This means that in
a delivered application involving a saved image, there is no way to
use an updated version of a used assembly. (Haven't tested for
this, though.) Might be an acceptable short-term fix, though.
- Have IMPORT-ASSEMBLY supply the assembly to IMPORT-TYPE. Good
idea, since it's currently inconsistent about this vis-a-vis
IMPORT-TYPES. But it doesn't address the problem described above.
- In MAKE-TYPE-FROM-NAME, do the following. If the name is
assembly-qualified (contains an unquoted paren), then use
System.Type.GetType() as before. Otherwise, call
System.Reflection.Assembly.GetType() on each assembly returned by
System.AppDomain.CurrentDomain.GetAssemblies(). The search should
be exhaustive in order to detect ambiguities, which should be
signaled as errors. In IMPORT-TYPE, don't accept
assembly-qualified names, and cache the actual type object
instead of the fully-qualified name. During shutdown, discard
these type object references. During initialization, do the
lookups again. (Changes to assemblies that introduce ambiguities
will cause an error during initialization.)
Downsides: Names given to RDNZL for resolution must not be
assembly-qualified. (Or, perhaps better, detect these and don't
cache them.) It is not possible for the system to deal
predictably with like-named types in multiple assemblies. All
loaded assemblies would be searched, even if they were not loaded
via RDNZL. The latter problem might be a liability for advanced
uses, e.g. involving dynamic assemblies that are being built by
the application.
I tried creating a C# console application that used two assemblies
with conflicting type names. You get an error only if you actually
reference the conflicting type name. The error documentation
explains how C# allows this problem to be worked around, using a
compiler switch that assigns a prefix to all namespaces in a given
assembly:
http://msdn2.microsoft.com/en-us/library/64wh5743.aspx
AFAIK, implementing a similar solution requires work on the
compiler's part. I have not noticed any direct support for this
aliasing in the .NET framework. So a similar solution in RDNZL
would require tracking assemblies. However, this might be
desirable in order to address the other problem mentioned above.
If RDNZL would track loaded assemblies, only those assemblies
would be searched for type name matches. If we associate an
optional alias with an assembly (e.g. via an optional argument to
LOAD-ASSEMBLY), then that assembly would only be searched if the
type name includes the prefix, and only after removing the
prefix.
As a further enhancement, I'd like to actually get rid of the
need for importing entirely. It doesn't seem unreasonable to
search for a type regardless of whether it has been imported or
not, provided that it is then always cached in *TYPE-HASH*. This
way *TYPE-HASH* builds up information about all types referenced
via RDNZL. Users can still make calls to primitives like
System.Type.GetType() if they ever want to bypass such
activity. This is also true for LOAD-ASSEMBLY, if it starts
tracking assemblies. Of course, the aliasing wouldn't work if you
bypass RDNZL.
I have prototyped some of the low-level pieces needed for these
changes, entirely in Lisp using RDNZL. I didn't want to put more
work into it unless I'm sure there's interest in including it in
RDNZL. (Or at least, I'll do the work differently if there
isn't.) I'm particularly uncertain about issues related to saving
and loading Lisp images, something I've never tried out, although
I think I understand the basic concerns there.
--
Dan Muller
----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.