Hey Toby,
Sorry, this email got away from me a little ;)
I think Ben answered your question, but I just wanted to clarify a few things about value tuples.
For every ThingaMeBob constructor that returns a ref, there is also a ThingaMeBob_val constructor that returns a value (you might remember a list email about vals and refs a few weeks back). Note in the example below that copying a 'value tuple' into a 'reference tuple', is fine. So I'm not sure why you had trouble with (tref my-non-pointer-colour 0), which should work fine? What you can not do is tset! into a 'value' tuple - all value tuples (named or otherwise) are immutable. Of course you can easily just create a *new* value tuple instead of trying to tset! into an existing one.
(bind-type ThingaMeBob <float,float,float,float>)
;; Have a look at the return type
(bind-func test_bobs
(lambda ()
(let ((a (ThingaMeBob 1.0 2.0 3.0 4.0))
(b (ThingaMeBob_val 1.0 2.0 3.0 4.0)))
(tuple a b))))
(bind-func use_test_bobs
(lambda ()
(let ((ret (test_bobs))
(a (tref ret 0))
(b (tref ret 1))
;; make c from b
(c (ThingaMeBob (tref b 0) (tref b 1) (tref b 2) (tref b 3))))
;; tref works fine for all of the above
(println (tref a 0) (tref b 1) (tref c 2))
void)))
;; should print 1.0 2.0 3.0
($ (use_test_bobs))
Just on the matter of C struct value passing, there should be nothing stopping your code from compiling properly against the C lib bindings that you discussed. If you were having trouble compiling then there was something else going on.
The problem with the struct 'value' calling conventions that Ben correctly raised will not stop xtlang compilation - unfortunately you'll only know there is a problem when either (a) your code crashes or (b) you notice that you are getting crud data (yes we should force no struct values in bind-lib - someone should raise a github issue ;-) . A good way to think about the problem is that it is basically the same issue as using a library compiled with CPU extensions that you don't have. The original compiler used Jessica's fancy new CPU to pass values using 256bit SIMD registers - that you don't have. You successfully link against Jessica's library only to have the linked function call crash (or otherwise screw up your stack etc.) at runtime.
There is a bunch of lowering that xtlang *should* do to try to better support struct value passing from C. But the calling conventions are different between Unix/Windows, and between 32bit/64bit and between x86/ARM. And even then, there are still no guarantees that your compilers are going to play nice. In short struct value passing is a complete pain, and so to be honest we have been ignoring it for the most part (i.e. passing value structs between xtlang and C is off limits). Instead, as Ben mentioned you should pass your structs by ref - and most sane C libraries do this, for exactly this reason. Unfortunately occasionally this means refactoring C libraries a little - certainly a pain :(
Now, having said all this, you *are* allowed to pass struct values freely between xtlang calls - no problem. This is only an issue when crossing the C divide - in either direction. And just to reiterate C struct references are no problem - in either direction.
Just one more xtlang code example that you might not have considered previously. Remembering that named types are ThingaMeBob (returns ref) and ThingaMeBob (returns value), and that tuple (returns value) and tuple_ref (returns ref) - I know they are opposites, but they represent the *common* case.
(bind-type MyStuff <<float,float,float>,<i64,float,double>>)
(bind-func print:[void,MyStuff*]*
(lambda (obj)
(let ((o1 (tref obj 0))
(o2 (tref obj 1)))
(printout "<MyStuff:"
(tref o1 0) " " (tref o1 1) " " (tref o1 2) ","
(tref o2 0) " " (tref o2 1) " " (tref o2 2) ">"))))
(bind-func test_my_stuff
(lambda ()
(let ((a (MyStuff (tuple 1.0 2.0 3.0) (tuple 1 2.0 3.0))))
(println 'before: a)
(tset! a 0 (tuple 3.0 2.0 1.0))
(tset! a 1 (tuple 3 2.0 1.0))
(println 'after: a)
void)))
($ (test_my_stuff))
Just as a general note while I'm here, using constructors (i.e. (MyStuff ...) ) is definitely the preferred practice these days (also note how it helps out with type inference). As a general rule of thumb, if you find yourself using alloc/halloc/salloc directly, then there is probably a better way to do what you're doing. Unfortunately most of the example and library code does not abide by this, but we will hopefully find time to clean things up bit by bit.
Cheers,
Andrew.