Tue, 02 Jan 2001 19:40:11 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
PS: It could be argued that `Ptr ()' can always be used instead of `Addr' and that therefore we should do away with `Addr' altogether. Opinions?
I agree that Addr uses should be replaced by Ptr and FunPtr (well, I thought it was obvious!).
Yup, I agree with all this. We should deprecate Addr in the interests of having as few interfaces as possible.
newForeignObj has been converted to Ptr, but since ForeignObjs themselves were later replaced with ForeignPtrs, it makes sense to let newForeignObj use Addr again as in previous released versions of ghc. For withForeignObj it could be done for consistency too (it's new so it does not really matter).
Sounds reasonable.
I would let freeHaskellFunPtr accept and ignore nullFunPtr, since I once had to write when (ptr /= nullFunPtr) $ freeHaskellFunPtr ptr and it can be seen as consistent with free.
This makes sense if nullFunPtr is really a valid value for a FunPtr. The FFI spec doesn't say anything about f.e.d. functions possibly returning nullFunPtr. I've always thought that C's free() accepting NULL as a valid argument was a questionable decision (although I have to admit it's convenient at times). Cheers, Simon
On Tue, 2 Jan 2001, Simon Marlow wrote:
I would let freeHaskellFunPtr accept and ignore nullFunPtr, since I once had to write when (ptr /= nullFunPtr) $ freeHaskellFunPtr ptr and it can be seen as consistent with free.
This makes sense if nullFunPtr is really a valid value for a FunPtr. The FFI spec doesn't say anything about f.e.d. functions possibly returning nullFunPtr.
They don't return nullFunPtr. My case was seting in a C struct a function pointer made by f.e.d. or a null function pointer, for a C interface which takes an optional user function (db3). Later the function pointer must be freed if it was set, and there is nothing to be freed if its absence was marked by nullFunPtr. Null function pointers are a non-portable concept - ANSI C does not have them, only null data pointers. But many existing C interfaces use them, and even the ability to cast between data pointers and function pointers is described as a common non-standard extension in ISO/ANSI C99. -- Marcin 'Qrczak' Kowalczyk
On 02-Jan-2001, Marcin 'Qrczak' Kowalczyk <mk167280@zodiac.mimuw.edu.pl> wrote:
Null function pointers are a non-portable concept - ANSI C does not have them, only null data pointers. But many existing C interfaces use them,
You're mistaken -- ANSI/ISO C definitely does have null function pointers. So it's not suprising that many existing C interfaces use them.
and even the ability to cast between data pointers and function pointers is described as a common non-standard extension in ISO/ANSI C99.
That's true. ANSI/ISO C does not guarantee that a data pointer will fit in a function pointer or vice versa. If you want something that could be either a data pointer or a function pointer, you need to use a union: union { void *data; void (*func)(void); } -- Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit | of excellence is a lethal habit" WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Fergus Henderson <fjh@cs.mu.oz.au> wrote,
On 02-Jan-2001, Marcin 'Qrczak' Kowalczyk <mk167280@zodiac.mimuw.edu.pl> wrote:
and even the ability to cast between data pointers and function pointers is described as a common non-standard extension in ISO/ANSI C99.
That's true. ANSI/ISO C does not guarantee that a data pointer will fit in a function pointer or vice versa.
If you want something that could be either a data pointer or a function pointer, you need to use a union:
union { void *data; void (*func)(void); }
This means that we should not provide castFunPtrToPtr :: FunPtr a -> Ptr b castPtrToFunPtr :: Ptr a -> FunPtr b Has anybody ever used these casts? As long as `FunPtr' is used for f.e.d., I don't see much use in these casts. It certainly doesn't make much sense to peek and poke an address produced by f.e.d. So, I propose to drop these two casts. Objections? Cheers, Manuel
Wed, 03 Jan 2001 12:27:20 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
This means that we should not provide
castFunPtrToPtr :: FunPtr a -> Ptr b castPtrToFunPtr :: Ptr a -> FunPtr b
It does not mean that, because existing C libraries are usually non-portable and some do require using such casts, however meaningless they could be.
Has anybody ever used these casts?
Yes: me, for the readline library.
As long as `FunPtr' is used for f.e.d., I don't see much use in these casts. It certainly doesn't make much sense to peek and poke an address produced by f.e.d.
But it's used to pass either a function pointer cast to a data pointer or a real data pointer to a function, together with an enumeration value indicating which one is this. Ugly, but that's life. Another example is dlopen. We could remove these casts, but they will be used anyway: they would have to be produced by wrapping a C function (relying on the implementation of both in terms of Addr forever might be not the best practice, especially if function pointers on IA-64 are 16 bits - I'm for 90% sure about this). -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
On 03-Jan-2001, Marcin 'Qrczak' Kowalczyk <qrczak@knm.org.pl> wrote:
Wed, 03 Jan 2001 12:27:20 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
This means that we should not provide
castFunPtrToPtr :: FunPtr a -> Ptr b castPtrToFunPtr :: Ptr a -> FunPtr b
It does not mean that, because existing C libraries are usually non-portable and some do require using such casts, however meaningless they could be.
Well, if they are left, then at very least they should be very clearly documented as being non-portable.
Has anybody ever used these casts?
Yes: me, for the readline library.
Could you be more specific? Which part of the readline library? Why was the cast needed?
But it's used to pass either a function pointer cast to a data pointer or a real data pointer to a function, together with an enumeration value indicating which one is this. Ugly, but that's life.
Another example is dlopen.
I think you mean dlsym(). -- Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit | of excellence is a lethal habit" WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Wed, 3 Jan 2001 21:12:54 +1100, Fergus Henderson <fjh@cs.mu.oz.au> pisze:
Well, if they are left, then at very least they should be very clearly documented as being non-portable.
They should, they aren't. Perl internally relies on the fact that memsetting all bits to 0 produces null pointers (at least in one place). Libraries use identifiers from the namespace reserved to the C implementation. Libraries use Posix functions, Single Unix Spec. functions, BSD functions, Linux functions etc. without documenting what interfaces they require.
Yes: me, for the readline library.
Could you be more specific?
typedef struct {...} KEYMAP_ENTRY; typedef KEYMAP_ENTRY *Keymap; typedef int Function(); int rl_generic_bind (int type, char *keyseq, char *data, Keymap map) If type == ISFUNC: data should be a value of type Function *, cast to char *. It will be applied to two int values. If type == ISMACR: data should be a string. If type == ISKMAP: data should be a value of type Keymap, cast to char *. Function *rl_function_of_keyseq (char *keyseq, Keymap map, int *type) Returns data as set by rl_generic_bind, cast to Function * this time, and sets type appropriately.
Why was the cast needed?
Because the library is designed and written in a horrible way.
Another example is dlopen.
I think you mean dlsym().
Sure. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
On 03-Jan-2001, Marcin 'Qrczak' Kowalczyk <qrczak@knm.org.pl> wrote:
Wed, 3 Jan 2001 21:12:54 +1100, Fergus Henderson <fjh@cs.mu.oz.au> pisze:
Well, if they are left, then at very least they should be very clearly documented as being non-portable.
They should, they aren't. Perl internally [...]
By "they", I mean the Haskell routines under discussion. Your examples of dlsym() and readline are sufficiently convincing that I agree that these conversion functions should be left in. But they should be documented properly. (The fact that Perl and other software has undocumented non-portabilities doesn't imply that we should be lax in documenting our own stuff!) -- Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit | of excellence is a lethal habit" WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
On Wed, 3 Jan 2001, Fergus Henderson wrote:
By "they", I mean the Haskell routines under discussion.
OK, I always thought that Haskell's FFI docs should document what they assume about the C implementation which is not guaranteed by ISO/ANSI. For example that pointers are physically amorphic, i.e. that sizeof (int *) == sizeof (char *), and that void poke1 (int **p, int *x) {*p = x;} void poke2 (int **p, int *x) {*(char **) p = (char *) x;} are equivalent, etc. - this follows from the existance of instance Storable (Ptr a) foreign export dynamic is not implementable portably at all. This is a different kind of assumption - Haskell's FFI assumes that there exist a way do implement this on the C implementation in question. Usually it is processor-specific (unless the C implementation provides some generic means which would allow that - I haven't heard of it and gcc does not provide them). -- Marcin 'Qrczak' Kowalczyk
qrczak@knm.org.pl (Marcin 'Qrczak' Kowalczyk) wrote,
Wed, 3 Jan 2001 21:12:54 +1100, Fergus Henderson <fjh@cs.mu.oz.au> pisze:
Yes: me, for the readline library.
Could you be more specific?
typedef struct {...} KEYMAP_ENTRY; typedef KEYMAP_ENTRY *Keymap; typedef int Function();
int rl_generic_bind (int type, char *keyseq, char *data, Keymap map)
If type == ISFUNC: data should be a value of type Function *, cast to char *. It will be applied to two int values. If type == ISMACR: data should be a string. If type == ISKMAP: data should be a value of type Keymap, cast to char *.
Function *rl_function_of_keyseq (char *keyseq, Keymap map, int *type)
Returns data as set by rl_generic_bind, cast to Function * this time, and sets type appropriately.
I was actually thinking about situations like this and wondering whether it wouldn't be cleaner to have a Haskell binding as follows: foreign import rl_generic_bind_func :: CInt -> Ptr CChar -> FunPtr (Int -> Int -> IO ())) -> Ptr KeyMap -> IO Int bindFunc :: String -> (Int -> Int -> IO ()) -> KeyMap -> IO bindFunc ... = do ... rl_generic_bind_func ISFUNC ... ... foreign import rl_generic_bind_macr :: CInt -> Ptr CChar -> Ptr CChar -> Ptr KeyMap -> IO Int <and so on> But I think, you are right: Why try to be cleaner than the underlying C library? It would probably not gain anything and may make our life harder. Fergus Henderson <fjh@cs.mu.oz.au> wrote,
Your examples of dlsym() and readline are sufficiently convincing that I agree that these conversion functions should be left in. But they should be documented properly. (The fact that Perl and other software has undocumented non-portabilities doesn't imply that we should be lax in documenting our own stuff!)
I agree with this, too. Cheers, Manuel
I would let freeHaskellFunPtr accept and ignore nullFunPtr, since I once had to write when (ptr /= nullFunPtr) $ freeHaskellFunPtr ptr and it can be seen as consistent with free.
This sort of behaviour is comon in C but I think this is mostly because C has such a weak type system. In the Win32 library, we took some care to distinguish between values which couldn't possibly be null (whether null pointers, null window handles, null font handles or whatever) using definitions like this newtype Window = Window Addr newtype Font = Font Int32 and values which could possibly be null: type MbWindow = Maybe Window type MbFont = Maybe Font This additional information made it easier to understand function types (because a bit more of the C documentation had migrated into the Haskell type), caught programming errors early, etc. This approach wouldn't be feasible in C because C's type system isn't expressive enough to express (and check!) these types. (Well, maybe you could use unions but it would be so clumsy as to be unusable.) Which is all just my long-winded way of saying I agree with Simon that we should not do this. Even if f.e.d. could return nullFunPtr, we should not do this because we can express it better using a Maybe. Alastair
Wed, 3 Jan 2001 15:31:43 -0700, Alastair Reid <reid@cs.utah.edu> pisze:
Which is all just my long-winded way of saying I agree with Simon that we should not do this. Even if f.e.d. could return nullFunPtr, we should not do this because we can express it better using a Maybe.
I retrieve the value from a C struct, so I can't just use Maybe. I must explicitly compare with nullFunPtr, which is not a big deal, but IMHO the only sensible thing freeHaskellFunPtr could do with a null pointer is to ignore it, rather than crashing. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
"Alastair Reid" <reid@cs.utah.edu> wrote,
I would let freeHaskellFunPtr accept and ignore nullFunPtr, since I once had to write when (ptr /= nullFunPtr) $ freeHaskellFunPtr ptr and it can be seen as consistent with free.
This sort of behaviour is comon in C but I think this is mostly because C has such a weak type system. [..] Which is all just my long-winded way of saying I agree with Simon that we should not do this. Even if f.e.d. could return nullFunPtr, we should not do this because we can express it better using a Maybe.
I think, I agree with this. Manuel
participants (6)
-
Alastair Reid -
Fergus Henderson -
Manuel M. T. Chakravarty -
Marcin 'Qrczak' Kowalczyk -
qrczak@knm.org.pl -
Simon Marlow