- I had it so that "finalisation" was only possible with NON-owned
vols, while your approach potentially allows Poly to use this feature now also.
Well, the vol that has the finaliser actually is an owned vol. That's because the C_pointer field contains the address of a malloc'd area that holds the C value. So when a C function returns a pointer value a new "vol" is allocated to contain the returned value. This extra level of indirection means that a vol can be a char, an int or a struct.
Ahhh... this goes along with the "there are always more levels of indirections than you think" comments in the code. ;)
I did, however, make the assumption that the "ownedness" field referred to whatever the C_pointer field was pointing to, rather than the vol holding that field itself... unless I am still confused (quite possible), this distinction is not very useful, since ALL vols themselves are actually owned by Poly?
Digging my hole deeper, what is the possible reason for vols to "be a char, an int or a struct"? I thought vols are there to manage foreign *memory* - implying pointers to [presumably but not necessarily] dynamically allocated storage... why would one want them to hold a value type?
A vol is a VALUE in C-space. It can be any of the possible C values. To be honest I still get confused about the way vols work and much of this is based on experimentation. Generally it's not necessary to understand them in order to use the FFI since the conversions (e.g. CInterface.INT) deal with a lot of this but if you need to write your own conversions then you need to understand the details. There's an example of a tree structure and building a conversion in Examples/ForeignTest.sml.
A vol can be an int, a char, a pointer etc. There are low-level conversion functions such as toCint that make vols from ML values and inverse functions such as fromCint that get ML values out. When using the (very) low level interface, CInterface.call_sym, to call a foreign function you have to build a list of vols and you get back a vol. This, though, has largely been superseded by call_sym_and_convert and the higher level call1, call2 etc functions.
A vol is always implemented as a pointer to a piece of memory that contains the value, so toCInt mallocs a piece of memory of size sizeof(int) and puts the ML value into it. In most contexts that means that the FFI has to load the value out of the memory in order to use it. It does, though, mean that a vol can be updated using CInterface.assign and that's particularly important for structs.
CInterface.alloc allocates a piece of memory and returns a vol that refers to the memory as its value. This isn't the same as having a vol that contains the address of the memory. If you use alloc to create an array or a struct and then pass the vol to a function you're passing the array or struct by value not by reference. Typically, you need to use CInterface.address to create a new vol that contains the address of the memory.
Normally vols own the piece of the memory that contain their C value. The exceptions are vols created by CInterface.offset and CInterface.deref. "offset" creates a vol that refers to a field of a struct or an array. Like any vol the value is actually a pointer to the C value but because no new memory has been allocated this doesn't "own" any memory. Similarly "deref" doesn't allocate any new memory; it assumes the memory has been allocated elsewhere.
I'm not exactly sure how this interacts with finalisation but I think it will work as expected. If a foreign function returns a value that needs finalisation it will be necessary to retain the vol it came in or create a new one and then attach the finalisation function to it. The important thing is that the ML data structure needs to keep a reference to the vol rather than, as in most cases, turning it into an ML value.
- My field ordering was different in Volatile - since pointers can be
larger than ints, I slipped this in BEFORE the
Bool Own_C_space;
I prefer to keep structs "packed" for alignment and aesthetic reasons.
Actually, this "Bool" is an int (really an unsigned or size_t) that contains the size in bytes of the area. Calling it Bool looks like a piece of legacy code.
Naturally, I did check to see what the base type of 'Bool' actually is before posting this... as it is an int, my comment stands - the pointers preceding this field could be 64 bits, while this would still be 32 in some 64-bit models (e.g., Windows x64) and preserving the 8-byte alignment in that case certainly wouldn't hurt.
That's a good point. I had forgotten about 64-bit systems. I still think, though, that the field should be a size_t value since it could be the size of an array.
... I think I prefer it curried but as sym->vol->unit. I'll even accept "finalize" since my dictionary says it's acceptable and perhaps even preferred! It looks like there are a few things that need to be cleaned up anyway so I could make these changes.
Sweet (on the curried part)! But I was kidding about the finalize / finalise thing... I know how you types like your "colour", "analyse", etc. :)
Well, I thought I'd avoid the issue and call it setFinal (perhaps to be changed to set_final for consistency)!
Regards, David