Moved the Objective-C wrappers and helpers out of darwintest.

This commit is contained in:
Pietro Gagliardi 2014-03-01 04:17:32 -05:00
parent 6b8a8d2d15
commit a3b01892fd
3 changed files with 190 additions and 0 deletions

46
bleh_darwin.m Normal file
View File

@ -0,0 +1,46 @@
/* 28 february 2014 */
/*
I wanted to avoid invoking Objective-C directly, preferring to do everything directly with the API. However, there are some things that simply cannot be done too well; for those situations, there's this. It does use the Objective-C runtime, eschewing the actual Objective-C part of this being an Objective-C file.
The main culprits are:
- data types listed as being defined in nonexistent headers
- 32-bit/64-bit type differences that are more than just a different typedef
Go wrapper functions (bleh_darwin.go) call these directly and take care of stdint.h -> Go type conversions.
*/
#include <objc/message.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdint.h>
#include <Foundation/NSGeometry.h>
/*
NSUInteger is listed as being in <objc/NSObjCRuntime.h>... which doesn't exist. Rather than relying on undocumented header file locations or explicitly typedef-ing NSUInteger to the (documented) unsigned long, I'll just place things here for maximum safety. I use uintptr_t as that should encompass every possible unsigned long.
*/
id _objc_msgSend_uint(id obj, SEL sel, uintptr_t a)
{
return objc_msgSend(obj, sel, (NSUInteger) a);
}
/*
These are the objc_msgSend() wrappers around NSRect. The problem is that while on 32-bit systems, NSRect is a concrete structure, on 64-bit systems it's just a typedef to CGRect. While in practice just using CGRect everywhere seems to work, better to be safe than sorry.
I use int64_t for maximum safety, as my coordinates are stored as Go ints and Go int -> C int (which is what is documented as happening) isn't reliable.
*/
#define OurRect() (NSMakeRect((CGFloat) x, (CGFloat) y, (CGFloat) w, (CGFloat) h))
id _objc_msgSend_rect(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h)
{
return objc_msgSend(obj, sel, OurRect());
}
id _objc_msgSend_rect_uint_uint_bool(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h, uintptr_t b, uintptr_t c, BOOL d)
{
return objc_msgSend(obj, sel, OurRect(), (NSUInteger) b, (NSUInteger) c, d);
}

84
objc_darwin.go Normal file
View File

@ -0,0 +1,84 @@
// 28 february 2014
package main
import (
"unsafe"
)
// #cgo LDFLAGS: -lobjc -framework Foundation
// #include <stdlib.h>
// #include "objc_darwin.h"
import "C"
func objc_getClass(class string) C.id {
cclass := C.CString(class)
defer C.free(unsafe.Pointer(cclass))
return C.objc_getClass(cclass)
}
func sel_getUid(sel string) C.SEL {
csel := C.CString(sel)
defer C.free(unsafe.Pointer(csel))
return C.sel_getUid(csel)
}
// Common Objective-C types and selectors.
var (
_NSObject = objc_getClass("NSObject")
_NSString = objc_getClass("NSString")
_alloc = sel_getUid("alloc")
_new = sel_getUid("new")
_release = sel_getUid("release")
_stringWithUTF8String = sel_getUid("stringWithUTF8String:")
_UTF8String = sel_getUid("UTF8String")
)
// some helper functions
func objc_alloc(class C.id) C.id {
return C.objc_msgSend_noargs(class, _alloc)
}
func objc_new(class C.id) C.id {
return C.objc_msgSend_noargs(class, _new)
}
func objc_release(obj C.id) {
C.objc_msgSend_noargs(obj, _release)
}
func toNSString(str string) C.id {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
return C.objc_msgSend_str(_NSString,
_stringWithUTF8String,
cstr)
}
func fromNSString(str C.id) string {
cstr := C.objc_msgSend_noargs(str, _UTF8String)
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}
/*
These are wrapper functions for the functions in bleh_darwin.m to wrap around stdint.h type casting.
*/
func objc_msgSend_rect(obj C.id, sel C.SEL, x int, y int, w int, h int) C.id {
return C._objc_msgSend_rect(obj, sel,
C.int64_t(x), C.int64_t(y), C.int64_t(w), C.int64_t(h))
}
func objc_msgSend_uint(obj C.id, sel C.SEL, a uintptr) C.id {
return C._objc_msgSend_uint(obj, sel, C.uintptr_t(a))
}
func objc_msgSend_rect_uint_uint_bool(obj C.id, sel C.SEL, x int, y int, w int, h int, b uintptr, c uintptr, d C.BOOL) C.id {
return C._objc_msgSend_rect_uint_uint_bool(obj, sel,
C.int64_t(x), C.int64_t(y), C.int64_t(w), C.int64_t(h),
C.uintptr_t(b), C.uintptr_t(c), d)
}

60
objc_darwin.h Normal file
View File

@ -0,0 +1,60 @@
/* 28 february 2014 */
/*
This includes all Objective-C runtime headers for convenience. It also creates wrappers around objc_msgSend() out of necessity.
cgo doesn't support calling variable argument list C functions, so objc_msgSend() cannot be called directly.
Furthermore, Objective-C selectors work by basically sending the arguments to objc_msgSend() verbatim across the wire. This basically means we're stuck making wrapper functions for every possible argument list. What fun!
The format should be self-explanatory.
*/
#include <objc/message.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdint.h>
inline id objc_msgSend_noargs(id obj, SEL sel)
{
return objc_msgSend(obj, sel);
}
#define m1(name, type1) \
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a) \
{ \
return objc_msgSend(obj, sel, a); \
}
#define m2(name, type1, type2) \
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a, type2 b) \
{ \
return objc_msgSend(obj, sel, a, b); \
}
#define m3(name, type1, type2, type3) \
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a, type2 b, type3 c) \
{ \
return objc_msgSend(obj, sel, a, b, c); \
}
#define m4(name, type1, type2, type3, type4) \
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a, type2 b, type3 c, type4 d) \
{ \
return objc_msgSend(obj, sel, a, b, c, d); \
}
m1(str, char *) /* TODO Go string? */
m1(id, id)
extern id _objc_msgSend_rect(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h);
m1(sel, SEL)
extern id _objc_msgSend_uint(id obj, SEL sel, uintptr_t a);
m2(id_id, id, id)
m3(id_id_id, id, id, id)
m3(sel_id_bool, SEL, id, BOOL)
extern id _objc_msgSend_rect_uint_uint_bool(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h, uintptr_t b, uintptr_t c, BOOL d);
m4(id_sel_id_id, id, SEL, id, id)