See more at the announce forum.
In the new dmd beta, there is better Objective-C interop support. I played with it a little bit, porting an old hello world program to it. I hit some segfaults - new beta code tends to be fragile - but I nevertheless got a program to work. Take a look:
(this code is much bigger than it has to be; I don't actually understand much about programming the Mac, so I copy/pasted some old code from Jacob Carlborg and just adapted that to the new compiler. The goal: pop up a window.)
1 import core.stdc.stdio; 2 3 void main() { 4 auto delegate_ = AppDelegate.alloc.init; 5 6 NSApp.delegate_ = cast(void*) delegate_; 7 NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular); 8 NSApp.run(); 9 } 10 11 /// leaks memory 12 NSString toNSString(string str) { 13 return NSString.alloc.initWithBytes( 14 cast(immutable(ubyte)*) str.ptr, 15 str.length, 16 NSStringEncoding.NSUTF8StringEncoding 17 ); 18 } 19 20 extern (Objective-C) 21 class NSApplication : NSResponder { 22 static NSApplication shared_() @selector("sharedApplication"); 23 24 void* delegate_() @selector("delegate"); 25 void delegate_(void*) @selector("setDelegate:"); 26 27 bool setActivationPolicy(NSApplicationActivationPolicy activationPolicy) @selector("setActivationPolicy:"); 28 29 // use `int` as workaround for https://github.com/ldc-developers/ldc/issues/2387 30 void activateIgnoringOtherApps(int flag) @selector("activateIgnoringOtherApps:"); 31 32 void run() @selector("run"); 33 } 34 35 extern (Objective-C) 36 __gshared NSApplication NSApp_; 37 38 NSApplication NSApp() { 39 if(NSApp_ is null) 40 NSApp_ = NSApplication.shared_; 41 return NSApp_; 42 } 43 alias char* SEL; 44 45 extern (Objective-C) 46 class NSResponder : NSObject { 47 NSMenu menu() @selector("menu"); 48 void menu(NSMenu menu) @selector("setMenu:"); 49 } 50 51 52 extern (Objective-C) 53 class NSNotification { 54 } 55 56 extern (Objective-C) 57 class NSObject { 58 NSObject init() @selector("init"); 59 static NSObject alloc() @selector("alloc"); 60 } 61 extern (Objective-C) 62 class NSString { 63 NSString init() @selector("init"); 64 static NSString alloc() @selector("alloc"); 65 66 NSString initWithBytes( 67 const(ubyte)* bytes, 68 NSUInteger length, 69 NSStringEncoding encoding 70 ) @selector("initWithBytes:length:encoding:"); 71 } 72 73 enum NSBackingStoreType : size_t { 74 retained = 0, 75 nonretained = 1, 76 buffered = 2 77 } 78 79 extern (Objective-C) 80 class NSMenu : NSObject { 81 override NSMenu init() @selector("init"); 82 NSMenu init(NSString title) @selector("initWithTitle:"); 83 84 void setSubmenu(NSMenu menu, NSMenuItem item) @selector("setSubmenu:forItem:"); 85 void addItem(NSMenuItem newItem) @selector("addItem:"); 86 87 NSMenuItem addItem( 88 NSString title, 89 SEL selector, 90 NSString charCode 91 ) @selector("addItemWithTitle:action:keyEquivalent:"); 92 93 override static NSMenu alloc() @selector("alloc"); 94 } 95 96 extern (Objective-C) 97 class NSMenuItem : NSObject { 98 override NSMenuItem init() @selector("init"); 99 override static NSMenuItem alloc() @selector("alloc"); 100 101 NSMenuItem init( 102 NSString title, 103 void* selector, 104 NSString charCode 105 ) @selector("initWithTitle:action:keyEquivalent:"); 106 107 extern (D) final 108 NSMenuItem init(string title, const(char)* selector, string charCode) { 109 return init(title.toNSString, sel_registerName(selector), charCode.toNSString); 110 } 111 } 112 113 version(none) 114 extern (Objective-C) 115 class NSApplicationDelegate : NSObject { 116 override NSApplicationDelegate init() @selector("init"); 117 override static NSApplicationDelegate alloc() @selector("alloc"); 118 void applicationDidFinishLaunching(NSNotification notification) @selector("applicationDidFinishLaunching:"); 119 120 bool applicationShouldTerminateAfterLastWindowClosed(NSNotification notification) @selector("applicationShouldTerminateAfterLastWindowClosed:"); 121 122 } 123 124 125 extern (Objective-C) 126 enum NSApplicationActivationPolicy : ptrdiff_t { 127 /* The application is an ordinary app that appears in the Dock and may have a user interface. This is the default for bundled apps, unless overridden in the Info.plist. */ 128 regular, 129 130 /* The application does not appear in the Dock and does not have a menu bar, but it may be activated programmatically or by clicking on one of its windows. This corresponds to LSUIElement=1 in the Info.plist. */ 131 accessory, 132 133 /* The application does not appear in the Dock and may not create windows or be activated. This corresponds to LSBackgroundOnly=1 in the Info.plist. This is also the default for unbundled executables that do not have Info.plists. */ 134 prohibited 135 }; 136 137 138 extern (Objective-C) 139 enum NSStringEncoding : NSUInteger { 140 NSASCIIStringEncoding = 1, /* 0..127 only */ 141 NSNEXTSTEPStringEncoding = 2, 142 NSJapaneseEUCStringEncoding = 3, 143 NSUTF8StringEncoding = 4, 144 NSISOLatin1StringEncoding = 5, 145 NSSymbolStringEncoding = 6, 146 NSNonLossyASCIIStringEncoding = 7, 147 NSShiftJISStringEncoding = 8, /* kCFStringEncodingDOSJapanese */ 148 NSISOLatin2StringEncoding = 9, 149 NSUnicodeStringEncoding = 10, 150 NSWindowsCP1251StringEncoding = 11, /* Cyrillic; same as AdobeStandardCyrillic */ 151 NSWindowsCP1252StringEncoding = 12, /* WinLatin1 */ 152 NSWindowsCP1253StringEncoding = 13, /* Greek */ 153 NSWindowsCP1254StringEncoding = 14, /* Turkish */ 154 NSWindowsCP1250StringEncoding = 15, /* WinLatin2 */ 155 NSISO2022JPStringEncoding = 21, /* ISO 2022 Japanese encoding for e-mail */ 156 NSMacOSRomanStringEncoding = 30, 157 158 NSUTF16StringEncoding = NSUnicodeStringEncoding, /* An alias for NSUnicodeStringEncoding */ 159 160 NSUTF16BigEndianStringEncoding = 0x90000100, /* NSUTF16StringEncoding encoding with explicit endianness specified */ 161 NSUTF16LittleEndianStringEncoding = 0x94000100, /* NSUTF16StringEncoding encoding with explicit endianness specified */ 162 163 NSUTF32StringEncoding = 0x8c000100, 164 NSUTF32BigEndianStringEncoding = 0x98000100, /* NSUTF32StringEncoding encoding with explicit endianness specified */ 165 NSUTF32LittleEndianStringEncoding = 0x9c000100 /* NSUTF32StringEncoding encoding with explicit endianness specified */ 166 } 167 168 alias NSUInteger = size_t; 169 alias NSInteger = ptrdiff_t; 170 171 extern (Objective-C) 172 enum NSWindowStyleMask : size_t { 173 borderless = 0, 174 titled = 1 << 0, 175 closable = 1 << 1, 176 miniaturizable = 1 << 2, 177 resizable = 1 << 3, 178 179 /* Specifies a window with textured background. Textured windows generally don't draw a top border line under the titlebar/toolbar. To get that line, use the NSUnifiedTitleAndToolbarWindowMask mask. 180 */ 181 texturedBackground = 1 << 8, 182 183 /* Specifies a window whose titlebar and toolbar have a unified look - that is, a continuous background. Under the titlebar and toolbar a horizontal separator line will appear. 184 */ 185 unifiedTitleAndToolbar = 1 << 12, 186 187 /* When set, the window will appear full screen. This mask is automatically toggled when toggleFullScreen: is called. 188 */ 189 fullScreen = 1 << 14, 190 191 /* If set, the contentView will consume the full size of the window; it can be combined with other window style masks, but is only respected for windows with a titlebar. 192 Utilizing this mask opts-in to layer-backing. Utilize the contentLayoutRect or auto-layout contentLayoutGuide to layout views underneath the titlebar/toolbar area. 193 */ 194 fullSizeContentView = 1 << 15, 195 196 /* The following are only applicable for NSPanel (or a subclass thereof) 197 */ 198 utilityWindow = 1 << 4, 199 docModalWindow = 1 << 6, 200 nonactivatingPanel = 1 << 7, // Specifies that a panel that does not activate the owning application 201 hUDWindow = 1 << 13 // Specifies a heads up display panel 202 } 203 204 struct CGColor; 205 alias CGColorRef = CGColor*; 206 207 alias CGFloat = double; 208 209 struct NSPoint { 210 CGFloat x; 211 CGFloat y; 212 } 213 214 struct NSSize { 215 CGFloat width; 216 CGFloat height; 217 } 218 219 struct NSRect { 220 NSPoint origin; 221 NSSize size; 222 } 223 224 225 extern (Objective-C) 226 class NSWindow { 227 NSWindow init() @selector("init"); 228 229 static NSWindow alloc() @selector("alloc"); 230 231 NSWindow initWithContentRect( 232 NSRect contentRect, 233 NSWindowStyleMask style, 234 NSBackingStoreType bufferingType, 235 bool flag 236 ) @selector("initWithContentRect:styleMask:backing:defer:"); 237 238 void makeKeyAndOrderFront(void* sender) @selector("makeKeyAndOrderFront:"); 239 NSView contentView() @selector("contentView"); 240 void orderFrontRegardless() @selector("orderFrontRegardless"); 241 void center() @selector("center"); 242 void contentView(NSView view) @selector("setContentView:"); 243 244 NSString title() @selector("title"); 245 void title(NSString value) @selector("setTitle:"); 246 247 /* 248 extern (D) final 249 { 250 void title(string value) { 251 this.title = value.toNSString; 252 } 253 } 254 */ 255 } 256 257 258 extern (Objective-C) 259 class NSView { 260 NSView init() @selector("init"); 261 NSView initWithFrame(NSRect frameRect) @selector("initWithFrame:"); 262 263 void addSubview(NSView view) @selector("addSubview:"); 264 265 bool wantsLayer() @selector("wantsLayer"); 266 // use `int` as workaround for https://github.com/ldc-developers/ldc/issues/2387 267 void wantsLayer(int value) @selector("setWantsLayer:"); 268 269 CALayer layer() @selector("layer"); 270 void uiDelegate(NSObject) @selector("setUIDelegate:"); 271 } 272 273 extern (Objective-C) 274 class NSViewController : NSObject { 275 NSView view() @selector("view"); 276 void view(NSView view) @selector("setView:"); 277 278 void loadView() @selector("loadView"); 279 280 void viewDidLoad() @selector("viewDidLoad"); 281 } 282 283 284 extern (Objective-C) 285 class CALayer { 286 CGFloat borderWidth() @selector("borderWidth"); 287 void borderWidth(CGFloat value) @selector("setBorderWidth:"); 288 289 CGColorRef borderColor() @selector("borderColor"); 290 void borderColor(CGColorRef) @selector("setBorderColor:"); 291 } 292 293 extern (Objective-C) class ViewController : NSViewController { 294 override void loadView() @selector("loadView") { 295 printf("loadView\n"); 296 } 297 298 override void viewDidLoad() @selector("viewDidLoad") { 299 printf("viewDidLoad\n"); 300 } 301 302 override ViewController init() @selector("init"); 303 override static ViewController alloc() @selector("alloc"); 304 } 305 306 extern (Objective-C) class AppDelegate : NSObject { 307 NSWindow window; 308 309 override static AppDelegate alloc() @selector("alloc"); 310 override typeof(this) init() @selector("init"); 311 312 ViewController controller; 313 314 bool applicationShouldTerminateAfterLastWindowClosed(NSNotification notification) @selector("applicationShouldTerminateAfterLastWindowClosed:") { 315 return true; 316 } 317 318 void applicationDidFinishLaunching(NSNotification notification) @selector("applicationDidFinishLaunching:") { 319 NSApp.menu = mainMenu(); 320 321 immutable style = NSWindowStyleMask.resizable | 322 NSWindowStyleMask.closable | 323 NSWindowStyleMask.miniaturizable | 324 NSWindowStyleMask.titled; 325 326 auto window = NSWindow.alloc.initWithContentRect( 327 NSMakeRect(10, 10, 300, 300), 328 style, 329 NSBackingStoreType.buffered, 330 false 331 ); 332 333 //window.title = "D Rox"; 334 335 auto controller = ViewController.alloc.init; 336 window.contentView = controller.view; 337 338 window.center(); 339 340 this.window = window; 341 this.controller = controller; 342 343 window.makeKeyAndOrderFront(null); 344 NSApp.activateIgnoringOtherApps(true); 345 } 346 } 347 348 NSMenu mainMenu() { 349 auto mainMenu = NSMenu.alloc.init("MainMenu".toNSString); 350 351 auto title = "Apple"; 352 auto menu = NSMenu.alloc.init(title.toNSString); 353 354 auto item = mainMenu.addItem(title.toNSString, null, "".toNSString); 355 mainMenu.setSubmenu(menu, item); 356 357 //menu.addItem(NSMenuItem.alloc.init("Quit", "stop:", "q")); 358 359 return mainMenu; 360 } 361 362 pragma(inline, true) NSPoint NSMakePoint(CGFloat x, CGFloat y) { 363 NSPoint p; 364 p.x = x; 365 p.y = y; 366 return p; 367 } 368 369 pragma(inline, true) NSSize NSMakeSize(CGFloat w, CGFloat h) { 370 NSSize s; 371 s.width = w; 372 s.height = h; 373 return s; 374 } 375 376 pragma(inline, true) NSRect NSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h) { 377 NSRect r; 378 r.origin.x = x; 379 r.origin.y = y; 380 r.size.width = w; 381 r.size.height = h; 382 return r; 383 } 384 385 extern(C) void* sel_registerName (const(char)* str);
Compile:
dmd test.d -L-framework -LCocoa -g -debug
Note that you need the new dmd beta version for this.
And run the program! On my box at least, it popped up a window! And then I was able to close it. Yay! lol. There's code in there for the menu and some strings that used to work, but I haven't been able to fix it for the new dmd yet.
It also happens to compile quickly! Pretty exciting - this will make D, technically speaking at least, a real competitor for Objective-C and Swift. Maybe I can make this Mac semi useful to me (I kinda doubt it though, lol, I am not a fan of Macs, but I do wanna port simpledisplay.d to Cocoa anyway.)
Over the last year or two, D has gotten easier to use without a runtime. Most people talk about -betterC, but you don't actually need that.
In the past, you would have to write at least ~30 lines of code in object.d to get a program running with minimal runtime. But now, it needs virtually nothing. Give this a try (code written for 32 bit Linux):
1 // dmd -c -m32 test.d -defaultlib= 2 // ld test.o -o test -nostdlib -m elf_i386 3 4 void write(in char[] msg) { 5 auto p = msg.ptr; 6 auto l = msg.length; 7 asm { 8 mov EAX, 4; // write 9 mov EBX, 1; 10 mov ECX, p; 11 mov EDX, l; 12 int 0x80; 13 } 14 } 15 16 struct Foo { 17 ~this() { 18 write("hi\n"); 19 } 20 } 21 22 void mymain() { 23 Foo foo; 24 } 25 extern(C) 26 void _start() { 27 mymain(); 28 //assert(0, "hi"); 29 asm { mov EAX, 1; mov EBX, 0; int 0x80; } 30 }
The commented assert is to make a point: assert calls a runtime function. Without -betterC, it calls _d_assert_msg. With -betterC, it calls __assert. But in both cases, you need to implement that yourself if you want to use it, since it is a runtime function.
But, to use a minimal runtime, just make an empty object.d, and compile without a library:
$ touch object.d # makes the empty object.d file - no contents required! # then we will compile and link separately, to exclude all runtime libraries $ dmd -c -m32 test.d -defaultlib= && ld test.o -o test -nostdlib -m elf_i386 && ./test hi # and check out how static and small it is! $ ls -lh ./test -rwxr-xr-x 1 me users 1.1K Feb 25 12:24 ./test $ ldd test not a dynamic executable
Regardless, without that assert, the rest of the code works - interestingly, a struct with a dtor - with basically zero work. You just create an empty file to represent the runtime, then link without it (which is a bit more complex of a command, but excluding even the C runtime always requires a slightly special command).
This program could even be loaded by GRUB and run on bare metal. (Of course, if you do that, the asm to call into the Linux kernel to say hi will need to change.)
If you needed more of D, you can start add runtime support to your object.d and build up as you need it. And if you don't need it, no need even for a C library, you can make tiny programs now.
The old days of TypeInfo being implicitly required are in the past.
More DIP talk in the forums, but I basically ignored all that and played with the new Objective-C interop and some runtime-less D code this week.