[Re-routing to the mailing list.]
On Fri, 17 Feb 2006 13:21:14 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
I'll try it out tonight and let you know how it works.
Good.
I'm thinking that a cleaner solution might be an optional target-type argument to BOX, although that requires RDNZL to pick the conversion needed.
Yeah, maybe. The advantage of my workaround is that you don't have to call BOX explicitely because RDNZL calls it as needed. So, you should be able to just INVOKE a method with arguments like 3.4d2 and RDNZL will convert it to a System.Single automatically.
Or a perhaps even more generally, a way to call a method of an object or type with some arguments, but get the return value without conversion, boxed if necessary. With that, I could have called System.Convert.ToSingle and gotten a boxed Single back. Perhaps calling it INVOKE-RETURNING-CONTAINER.
But you'd have to do this for PROPERTY and FIELD as well. And you'd lose the advantages of the special reader syntax. A bit messy.
Thanks a lot for writing RDNZL. This is ever so much easier than fooling around with FFIs to call C or C++ APIs.
As I'm going along, I'm writing a few small utilities to help with various common tasks. When I've had more experience with this, I'll offer them for inclusion in RDNZL.
That would be nice.
Cheers, Edi.
Edi Weitz opined:
On Fri, 17 Feb 2006 13:21:14 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
I'm thinking that a cleaner solution might be an optional target-type argument to BOX, although that requires RDNZL to pick the conversion needed.
Yeah, maybe. The advantage of my workaround is that you don't have to call BOX explicitely because RDNZL calls it as needed. So, you should be able to just INVOKE a method with arguments like 3.4d2 and RDNZL will convert it to a System.Single automatically.
True. Although as you pointed out, you can run into trouble when both single and double floats are wanted by the same method. I think that this would work in general, because .NET would implicitly up-convert a single to a double again; but at that point some precision would be lost unnecessarily. One can also imagine situations where a method taking a single is overloaded by a method taking a double, and the latter becomes completely inaccessible -- just the opposite of what you'd want to have happen if your Lisp uses only doubles!
Also, I wonder about similar problems that could arise involving other types.
Or perhaps even more generally, a way to call a method of an object or type with some arguments, but get the return value without conversion, boxed if necessary. With that, I could have called System.Convert.ToSingle and gotten a boxed Single back. Perhaps calling it INVOKE-RETURNING-CONTAINER.
But you'd have to do this for PROPERTY and FIELD as well. And you'd lose the advantages of the special reader syntax. A bit messy.
Again, true, but I'm thinking here mainly of low-level mechanisms with which one could address any similar problems, with precise control. How to make them convenient requires some more thought.
On Fri, 17 Feb 2006 17:04:40 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
Although as you pointed out, you can run into trouble when both single and double floats are wanted by the same method.
Once could do something like this (although it's not pretty)
(defun single (float) (let ((*coerce-double-floats-to-single* t)) (box float)))
(invoke object method-name float1 (single float2))
where the first argument is supposed to be a System.Double and the second one a System.Single.
Quoth Edi Weitz: [Hide Quoted Text]
One could do something like this (although it's not pretty)
(defun single (float) (let ((*coerce-double-floats-to-single* t)) (box float)))
(invoke object method-name float1 (single float2))
where the first argument is supposed to be a System.Double and the second one a System.Single.
Ooh, good point! This will be handy for experimenting with different ways of arranging this.
I verified that your change works by passing a float where before I was passing an integer. (The method wants a single, but .NET was doing the method lookup and implicit int-to-single conversion fine, and I happened to just need an integer value there.) But the other function that was giving me fits, Mesh.Cylinder, is blowing up for some other reason. At least it's getting called now -- progress! I'm pausing to download the latest DX9 SDK, since mine was rather old. Best to fight with fresh bugs rather than old ones, I always say...
On Fri, 17 Feb 2006 21:14:02 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
But the other function that was giving me fits, Mesh.Cylinder, is blowing up for some other reason. At least it's getting called now -- progress!
Just to be sure: Are you using a version of LispWorks which can deal with foreign callbacks? This /could/ be the reason for strange things happening even if you yourself don't use a callback.
See recent discussions on this mailing list and here:
Cheers, Edi.
Edi Weitz edi-at-agharta.de |RDNZL-devel/via Sneakemail| wrote:
On Fri, 17 Feb 2006 21:14:02 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
But the other function that was giving me fits, Mesh.Cylinder, is blowing up for some other reason. At least it's getting called now -- progress!
Just to be sure: Are you using a version of LispWorks which can deal with foreign callbacks? This /could/ be the reason for strange things happening even if you yourself don't use a callback.
See recent discussions on this mailing list and here:
Thanks, but I don't think I'm running into this -- yet. I'll keep it in mind.
I was mistaken when I said that the float coercion is working. After struggling with Mesh.Cylinder to no avail, I wrote a very small test assembly in C#:
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms;
namespace ClassLibrary1 { public class Class1 { public Class1() { } public static void Yop() { MessageBox.Show("I am here! I am here!"); } public static void ShowSingle(Single f) { MessageBox.Show(f.ToString()); } public static void ShowDouble(Double f) { MessageBox.Show(f.ToString()); } } }
(I'm using VS 2005, thus .NET 2.0. Not that it should make much difference.)
Yop is used as a sanity check, and it works. I can successfully call ShowDouble with *c-d-f-t-s* nil. I can call both ShowDouble and ShowSingle with *c-d-f-t-s* t, but *both* show an incorrect result.
I haven't fetched the source for the RDNZL DLL yet, but I think that's my next step.
BTW, do you know any way (in Lispworks!) of forcing an assebmly to unload, so I don't have to restart the Lisp image whenever an assembly is changed?
Replying to myself with more information...
Dan Muller s8ctxw402-at-sneakemail.com |RDNZL-devel/via Sneakemail| wrote:
Edi Weitz edi-at-agharta.de |RDNZL-devel/via Sneakemail| wrote:
I was mistaken when I said that the float coercion is working. After struggling with Mesh.Cylinder to no avail, I wrote a very small test assembly in C#:
[code elided]
(I'm using VS 2005, thus .NET 2.0. Not that it should make much difference.)
Hrrrm. Since you're using Managed C++, I'm not able to compile the RDNZL DLL. The solution converts up to VS8 OK. But, as you probably know, Managed C++ was replaced by C++/CLR. The conversion adds the necessary compiler option to support the old syntax, but the multi-threaded run-time library setting is incompatible with it (Configuration Properties/C++/Code Generation/Runtime library). Oh well. Just providing this as an FYI.
Yop is used as a sanity check, and it works. I can successfully call ShowDouble with *c-d-f-t-s* nil. I can call both ShowDouble and ShowSingle with *c-d-f-t-s* t, but *both* show an incorrect result.
Delving deeper, I find that with *c-d-f-t-s* t, [ToString (box 5.5)] return "0", and [ToString (box 5.4)] returns something ugly that isn't 5.4. In BOX*, you call MakeDotNetContainerFromFloat, but what you actually end up passing it is a double. This is where a down-conversion needs to happen. Poking around in the LW FLI documentation, I eventually figured out a simple change that fixes this.
In port-lw.lisp, FFI-MAP-TYPE, change :float to :lisp-float. This causes any Lisp float type (only double on Windows!) to be converted to a single float in FLI calls. So this is how we get LispWorks to do the implicit down-conversion for us when calling foreign functions that take singles.
Another good test is (box (unbox 5.4)). This didn't work before, and now it does.
This doesn't fix the overall problem yet, though. This test, using the assembly I gave in the previous email, still gives bad results:
(eval-when (:compile-toplevel :load-toplevel) (asdf:operate 'asdf:load-op :rdnzl)) (use-package :rdnzl)
;; This approach doesn't work ;(import-types "ClassLibrary1")
; Do this instead? (import-type "ClassLibrary1.Class1" (load-assembly "ClassLibrary1"))
(use-namespace "ClassLibrary1") (enable-rdnzl-syntax) (defun rtest () [Yop "Class1"] [ShowDouble "Class1" 1.0] ; correct [ShowDouble "Class1" 7.267] ; correct (let ((*coerce-double-floats-to-single* t)) (declare (special *coerce-double-floats-to-single*)) [ShowDouble "Class1" 1.0] ; shows incorrectly [ShowDouble "Class1" 7.267] ; shows incorrectly [ShowSingle "Class1" 1.0] ; shows incorrectly [ShowSingle "Class1" 7.267])) ; shows incorrectly
So I'm still working on it. Probably has to with the way that return values are handled...
Dan Muller s8ctxw402-at-sneakemail.com |RDNZL-devel/via Sneakemail| wrote:
Replying to myself with more information...
And again, to retract something. Sorry for the noise...
In port-lw.lisp, FFI-MAP-TYPE, change :float to :lisp-float. This causes any Lisp float type (only double on Windows!) to be converted to a single float in FLI calls. So this is how we get LispWorks to do the implicit down-conversion for us when calling foreign functions that take singles.
This didn't fix anything, I made an error in testing. I'll shut up now until all my test cases pass...
On Sat, 18 Feb 2006 12:07:34 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
In port-lw.lisp, FFI-MAP-TYPE, change :float to :lisp-float.
Yep, thanks, that (and adding :LANGUAGE :ANSI-C) did the trick! I've uploaded 0.9.4 which should work now.
Could you please test? I'm a bit in a hurry because we're moving to a new flat on Wednesday... )
Thanks for your help, Edi.
Edi Weitz edi-at-agharta.de |RDNZL-devel/via Sneakemail| wrote:
On Sat, 18 Feb 2006 12:07:34 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
In port-lw.lisp, FFI-MAP-TYPE, change :float to :lisp-float.
Yep, thanks, that (and adding :LANGUAGE :ANSI-C) did the trick! I've uploaded 0.9.4 which should work now.
Could you please test? I'm a bit in a hurry because we're moving to a new flat on Wednesday... )
Yes, this passes my small test. Interestingly, the rounding effect is visible.
With *c-d-f-t-s* set to NIL: [ShowDouble "Class1" 7.267] -> displays 7.267
With *c-d-f-t-s* set to T: [ShowDouble "Class1" 7.267] -> displays 7.26700019836426 [ShowSingle "Class1" 7.267] -> displays 7.267
Thanks for your help, Edi.
And thank you for yours!
Good luck with the move. :)
On Sat, 18 Feb 2006 12:07:34 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
Hrrrm. Since you're using Managed C++, I'm not able to compile the RDNZL DLL. The solution converts up to VS8 OK. But, as you probably know, Managed C++ was replaced by C++/CLR. The conversion adds the necessary compiler option to support the old syntax, but the multi-threaded run-time library setting is incompatible with it (Configuration Properties/C++/Code Generation/Runtime library). Oh well. Just providing this as an FYI.
Eeek! Does this mean I can throw away RDNZL if I upgrade to the new Visual Studio? Actually, I have the CDs/DVDs already lying around here but didn't find the time to install it yet.
I'd be VERY happy if someone knowledgeable enough with Managed C++ and C++/CLR could enlighten me...
;; This approach doesn't work ;(import-types "ClassLibrary1")
(import-types "ClassLibrary1" "Class1")
works for me if the DLL is in the LispWorks folder.
(declare (special *coerce-double-floats-to-single*))
That's not necessary. The variable is globally special.
Cheers, Edi.
Edi Weitz edi-at-agharta.de |RDNZL-devel/via Sneakemail| wrote:
On Sat, 18 Feb 2006 12:07:34 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
Hrrrm. Since you're using Managed C++, I'm not able to compile the RDNZL DLL. The solution converts up to VS8 OK. But, as you probably know, Managed C++ was replaced by C++/CLR. The conversion adds the necessary compiler option to support the old syntax, but the multi-threaded run-time library setting is incompatible with it (Configuration Properties/C++/Code Generation/Runtime library). Oh well. Just providing this as an FYI.
Eeek! Does this mean I can throw away RDNZL if I upgrade to the new Visual Studio? Actually, I have the CDs/DVDs already lying around here but didn't find the time to install it yet.
I'd be VERY happy if someone knowledgeable enough with Managed C++ and C++/CLR could enlighten me...
;; This approach doesn't work ;(import-types "ClassLibrary1")
(import-types "ClassLibrary1" "Class1")
works for me if the DLL is in the LispWorks folder.
(declare (special *coerce-double-floats-to-single*))
That's not necessary. The variable is globally special.
Cheers, Edi.
On Sat, 18 Feb 2006 18:20:21 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
???
Let me try this again ... this stupid web-mail application ate the contents of my previous reply to this. :(
Edi Weitz edi-at-agharta.de |RDNZL-devel/via Sneakemail| wrote:
On Sat, 18 Feb 2006 12:07:34 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
Hrrrm. Since you're using Managed C++, I'm not able to compile the RDNZL DLL. The solution converts up to VS8 OK. But, as you probably know, Managed C++ was replaced by C++/CLR. The conversion adds the necessary compiler option to support the old syntax, but the multi-threaded run-time library setting is incompatible with it (Configuration Properties/C++/Code Generation/Runtime library). Oh well. Just providing this as an FYI.
Eeek! Does this mean I can throw away RDNZL if I upgrade to the new Visual Studio? Actually, I have the CDs/DVDs already lying around here but didn't find the time to install it yet.
I'd be VERY happy if someone knowledgeable enough with Managed C++ and C++/CLR could enlighten me...
Good news, sort of. I found out that you can use the /MD (Multi-Threaded DLL) switch instead of /MT (Multi-Threaded). At least, it compiles. The downside is that the file MSVCR80.DLL must be available at run-time.
A correction on my terminology: It's C++/CLI, not C++/CLR.
I've looked fairly extensively at C++/CLI while studying the possibility of porting some C++ code at my day job. Syntactically, it's a vast improvement over Managed C++. If you ever decide you'd like to port RDNZL to it, let me know. Schedule permitting, I'd be glad to help, or even just do it for you -- it's not a very large project.
;; This approach doesn't work ;(import-types "ClassLibrary1")
(import-types "ClassLibrary1" "Class1")
works for me if the DLL is in the LispWorks folder.
I haven't had time to figure this out yet. I did have the DLL there, otherwise it wouldn't have loaded the other way.
(declare (special *coerce-double-floats-to-single*))
That's not necessary. The variable is globally special.
Thanks!
On Sat, 18 Feb 2006 20:24:21 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
Good news, sort of. I found out that you can use the /MD (Multi-Threaded DLL) switch instead of /MT (Multi-Threaded). At least, it compiles. The downside is that the file MSVCR80.DLL must be available at run-time.
I googled a bit and found this here:
Affected User Scenario: Users will need to replace /MT with /MD for their code targeting .NET
Description: There is no support in the CRT for statically linking managed applications.
Customer Workaround: Customer needs to change /MT to /MD and link dynamically to the CRT.
Rationale: There is no support in the C Runtime Library to statically link to a managed application. All managed applications have to be dynamically linked; thus the reason to make the two compiler options conflict.
http://www.codeproject.com/managedcpp/WhidbeyBreakingChanges.asp
I've looked fairly extensively at C++/CLI while studying the possibility of porting some C++ code at my day job. Syntactically, it's a vast improvement over Managed C++. If you ever decide you'd like to port RDNZL to it, let me know. Schedule permitting, I'd be glad to help, or even just do it for you -- it's not a very large project.
Thanks for offering! I might try it myself, but most likely not before April. Feel free to work on it... :)
;; This approach doesn't work ;(import-types "ClassLibrary1")
(import-types "ClassLibrary1" "Class1")
works for me if the DLL is in the LispWorks folder.
I haven't had time to figure this out yet. I did have the DLL there, otherwise it wouldn't have loaded the other way.
Hmm, so you didn't provide any types to import. If you didn't get an error message, it means the assembly was loaded but with zero types imported.
Cheers, Edi.
On Sat, 18 Feb 2006 10:48:48 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
BTW, do you know any way (in Lispworks!) of forcing an assebmly to unload, so I don't have to restart the Lisp image whenever an assembly is changed?
No, sorry, but this sounds like a .NET question and not like a LW-specific question, doesn't it?
Edi Weitz edi-at-agharta.de |RDNZL-devel/via Sneakemail| wrote:
On Sat, 18 Feb 2006 10:48:48 -0500, "Dan Muller" s8ctxw402@sneakemail.com wrote:
BTW, do you know any way (in Lispworks!) of forcing an assebmly to unload, so I don't have to restart the Lisp image whenever an assembly is changed?
No, sorry, but this sounds like a .NET question and not like a LW-specific question, doesn't it?
I don't really know -- could involve all of RDNZL, .NET, and the Lisp implementation. Just thought you might know of a technique offhand, for some Lisp implementations, since you're sure to have run into this while working on RDNZL. <shrug>