I think struct/contract
is probably the main way to do it.
Far more detail: That way, I think the procedures people see still satisfy struct-accessor-procedure?
etc., and the constructor’s binding is still associated with static information about the structure type. This information lets people set up impersonators and declare inheriting structure types, which are both good to expose if you want people to be able to define contract combinators that recognize and refine instances of your type. (Only subtypes of your type can have instances equal?
to your type’s instances, and contracts generally try to preserve equal?
even as they augment the value with error-checking.)
That said, at first I found it pretty surprising I was exposing all those capabilities from my libraries. I’ve gone to some trouble to hide that information behind plain procedures that I provide with contract-out
. I feel like it’s cost me a lot in terms of verbosity compared to just using struct-out
.
I think one “meta” observation from this discussion is that Racket is flexible enough to support pretty much whatever you want to do. You can decide where the contracts are placed. Independently, you can decide how to organize your code (depending on your preference for proximity of definitions, contracts, tests, even documentation).
So I mean it’s good to have general advice, especially for people doing “general-purpose” programs and/or just learning Racket. That’s super helpful!
And each project and/or team should agree on some conventions, for sanity and consistency.
But ultimately it’s a decision you or your team or your community make, as opposed to something dictated by Racket.
Also, whatever approach you choose, with macros you can calibrate this between super explicit and repetitive “housekeeping” code at one extreme, and super DRY and concise and “magical” at the other extreme, or anywhere in between. :smile:
feels “best practice” is usually less about computer science and math, and usually much more about “how do a bunch of primates cooperate in a way that is productive and healthy”.
@kellysmith12.21 you can put contracts on structs with contract-out
(oh, that’s right! I had seen that feature of contract-out
before and forgot about it)
Hi all,
I’m getting a problem with ffi/unsafe
when calling out to a foreign function using the (_ptr o ...)
construct. The signature is not particularly interesting - it’s simply a (_fun _pointer (_ptr o my-struct) -> _void)
where my-struct
is defined by define-cstruct
but I’m getting the following contract violation from the code generated by _fun
.
ptr-ref: contract violation
expected: cpointer?
given: #<thread-resume-evt>
argument position: 1st
other arguments...:
#<ctype>
context...:
If I do it “manually”, the following signature, returns a #<thread-resume-evt>
instead of the expected _cpointer
type.
(_fun
_physical-device
[properties : _? = (malloc _physical-device-properties)]
[_pointer = properties] ->
_void ->
properties)
I’m at a loss at how to debug this; are there any resources which I could use to try and narrow down the problem? I’m on Racket 7.5, BC (I think that’s the default for 7.5?).
Upgrading to 7.8 didn’t help - the output is still the same
After poking around it further - I think it’s an issue with my code not defining certain structs correctly - I think that the cstruct
definition is incorrect (some fields should be fixed-length arrays). Mucking around with cstruct gives a different type. I’m surprised this is the outcome though.
If you give the foreign function a pointer to memory that is allocated too small, then pretty much anything can happen. For example, the first two bytes of a nearby allocated object might get set to 128 and 0, which would make the object look like a thread-resume-evt
on a little-endian platform.