Re: Enum that can be 0 or null
On 06/07/2016 08:50 PM, ParticlePeter wrote: On Tuesday, 7 June 2016 at 14:31:40 UTC, Alex Parrill wrote: I don't think opCast gets called for implicit conversions; it only gets called for explicit casts. I'll test it later. It does for type bool, but I fear that's the only exception. No, opCast can't be used for implicit conversions to bool. I guess you're thinking of the `if(foo) {...}` case. That's considered an explicit conversion. `bool b = foo;` still won't work.
Re: Enum that can be 0 or null
On Tuesday, 7 June 2016 at 14:31:40 UTC, Alex Parrill wrote: I don't think opCast gets called for implicit conversions; it only gets called for explicit casts. I'll test it later. It does for type bool, but I fear that's the only exception.
Re: Enum that can be 0 or null
On Tuesday, 7 June 2016 at 04:31:56 UTC, ParticlePeter wrote: On Monday, 6 June 2016 at 20:32:23 UTC, Alex Parrill wrote: They'd be the same type, since you would define the vulkan functions to take these structures instead of pointer or integer types. It relies on a lot of assumptions about the ABI that make a raw pointer work the same as a structure containing just one pointer, which is why I did not give it much consideration. Is there a way to use opCast (just an idea, not working code) ? private struct VK_HANDLE_HELPER { const void * handle = null; alias handle this; T opCast(T)() if( is( T == uint64_t )) { return 0uL; } } const VK_NULL_HANDLE = VK_HANDLE_HELPER(); I don't think opCast gets called for implicit conversions; it only gets called for explicit casts. I'll test it later.
Re: Enum that can be 0 or null
On Monday, 6 June 2016 at 20:32:23 UTC, Alex Parrill wrote: It relies on a lot of assumptions about the ABI that make a raw pointer work the same as a structure containing just one pointer, which is why I did not give it much consideration. At least some of those assumptions could be verified at compile time: // insert at the end of the VkHandle mixin template: static assert(typeof(this).sizeof == typeof(bits).sizeof); static assert(typeof(this).alignof == typeof(bits).alignof); If you want to be extra careful, you can also use a string mixin to declare each extern(C) function with its official C parameter types, plus a D pragma(inline, true) wrapper that accepts VkDevice or whatever, instead. Of course, it's probably a lot easier to just tel your users to replace VK_NULL_HANDLE with VK_NULL_DISPATCHABLE or VK_NULL_NONDISPATCHABLE like you suggested earlier.
Re: Enum that can be 0 or null
On Monday, 6 June 2016 at 20:32:23 UTC, Alex Parrill wrote: They'd be the same type, since you would define the vulkan functions to take these structures instead of pointer or integer types. It relies on a lot of assumptions about the ABI that make a raw pointer work the same as a structure containing just one pointer, which is why I did not give it much consideration. Is there a way to use opCast (just an idea, not working code) ? private struct VK_HANDLE_HELPER { const void * handle = null; alias handle this; T opCast(T)() if( is( T == uint64_t )) { return 0uL; } } const VK_NULL_HANDLE = VK_HANDLE_HELPER();
Re: Enum that can be 0 or null
On Monday, 6 June 2016 at 18:43:33 UTC, ParticlePeter wrote: On Saturday, 21 May 2016 at 06:36:53 UTC, tsbockman wrote: ... As an example, if VK_NULL_HANDLE only ever needs to be assigned to opaque types on the D side (that is, types that serve only as an ID or address for communicating with the C side), you could do this: private struct VkNullHandle { } enum VK_NULL_HANDLE = VkNullHandle.init; mixin template VkHandle(bool dispatchable) { static if (dispatchable || (size_t.sizeof == 8)) void* bits = null; else ulong bits = 0; this(typeof(this) that) { this.bits = that.bits; } this(VkNullHandle that) { this.bits = typeof(this.bits).init; } ref typeof(this) opAssign(typeof(this) that) { this.bits = that.bits; return this; } ref typeof(this) opAssign(VkNullHandle that) { this.bits = typeof(this.bits).init; return this; } } struct VkDevice { mixin VkHandle!true; } struct VkInstance { mixin VkHandle!true; } struct VkFence { mixin VkHandle!false; } struct VkSemaphore { mixin VkHandle!false; } void main() { VkInstance a = VK_NULL_HANDLE; VkFence b = VK_NULL_HANDLE; } (DPaste: https://dpaste.dzfl.pl/8f4ce39a907f ) The above is typesafe, and can easily be made compatible with a typical C API, since C does no parameter-related name mangling. In this case I don't see how it would be possible to use your VkDevice, etc. as argument to the C functions, as they are of different type, no? They'd be the same type, since you would define the vulkan functions to take these structures instead of pointer or integer types. It relies on a lot of assumptions about the ABI that make a raw pointer work the same as a structure containing just one pointer, which is why I did not give it much consideration.
Re: Enum that can be 0 or null
On Saturday, 21 May 2016 at 06:36:53 UTC, tsbockman wrote: ... As an example, if VK_NULL_HANDLE only ever needs to be assigned to opaque types on the D side (that is, types that serve only as an ID or address for communicating with the C side), you could do this: private struct VkNullHandle { } enum VK_NULL_HANDLE = VkNullHandle.init; mixin template VkHandle(bool dispatchable) { static if (dispatchable || (size_t.sizeof == 8)) void* bits = null; else ulong bits = 0; this(typeof(this) that) { this.bits = that.bits; } this(VkNullHandle that) { this.bits = typeof(this.bits).init; } ref typeof(this) opAssign(typeof(this) that) { this.bits = that.bits; return this; } ref typeof(this) opAssign(VkNullHandle that) { this.bits = typeof(this.bits).init; return this; } } struct VkDevice { mixin VkHandle!true; } struct VkInstance { mixin VkHandle!true; } struct VkFence { mixin VkHandle!false; } struct VkSemaphore { mixin VkHandle!false; } void main() { VkInstance a = VK_NULL_HANDLE; VkFence b = VK_NULL_HANDLE; } (DPaste: https://dpaste.dzfl.pl/8f4ce39a907f ) The above is typesafe, and can easily be made compatible with a typical C API, since C does no parameter-related name mangling. In this case I don't see how it would be possible to use your VkDevice, etc. as argument to the C functions, as they are of different type, no?
Re: Enum that can be 0 or null
On Saturday, 21 May 2016 at 01:09:42 UTC, Alex Parrill wrote: On Friday, 20 May 2016 at 22:10:51 UTC, tsbockman wrote: If that's not a satisfactory answer, please show some specific examples of code that you don't know how to make work without VK_NULL_HANDLE so that I can propose a workaround. Because, as I mentioned in the OP, for VK_NULL_HANDLE to work like it does in C on 32-bit systems, it needs to be compatible with both pointer types (VkDevice, VkInstance, etc) and 64-bit integer types (VkFence, VkSemaphore, many others). That is not code. As I said, there are many possible workarounds, but it is hard to say which is best for you without actually seeing your code. As an example, if VK_NULL_HANDLE only ever needs to be assigned to opaque types on the D side (that is, types that serve only as an ID or address for communicating with the C side), you could do this: private struct VkNullHandle { } enum VK_NULL_HANDLE = VkNullHandle.init; mixin template VkHandle(bool dispatchable) { static if (dispatchable || (size_t.sizeof == 8)) void* bits = null; else ulong bits = 0; this(typeof(this) that) { this.bits = that.bits; } this(VkNullHandle that) { this.bits = typeof(this.bits).init; } ref typeof(this) opAssign(typeof(this) that) { this.bits = that.bits; return this; } ref typeof(this) opAssign(VkNullHandle that) { this.bits = typeof(this.bits).init; return this; } } struct VkDevice { mixin VkHandle!true; } struct VkInstance { mixin VkHandle!true; } struct VkFence { mixin VkHandle!false; } struct VkSemaphore { mixin VkHandle!false; } void main() { VkInstance a = VK_NULL_HANDLE; VkFence b = VK_NULL_HANDLE; } (DPaste: https://dpaste.dzfl.pl/8f4ce39a907f ) The above is typesafe, and can easily be made compatible with a typical C API, since C does no parameter-related name mangling.
Re: Enum that can be 0 or null
On Saturday, 21 May 2016 at 01:53:21 UTC, Mike Parker wrote: When binding to a C library, it's desirable to have as close to the original C API as possible so that you *can* drop C snippets and examples into D code and have them just work. Obviously, 100% compatibility is not possible, but it is possible to get pretty close most of the time. The reason that VK_NULL_HANDLE cannot (currently) be directly implemented in D it is not type safe. D's type safety is usually considered to be a positive selling point for the language; that's what I mean when I say that "you aren't *supposed* to be able to just copy-paste any random C snippet into D".
Re: Enum that can be 0 or null
On Saturday, 21 May 2016 at 02:04:23 UTC, Mike Parker wrote: On Saturday, 21 May 2016 at 01:09:42 UTC, Alex Parrill wrote: Looks like my best bet is to mark it as deprecated and point them to Vk(Type).init instead. I would prefer to do something like this: enum VK_NULL_HANDLE_0 = 0; enum VK_NULL_HANDLE_PTR = null; Then document it clearly. Alias VK_NULL_HANDLE to one of them and keep it deprecated forever. Many users are not going to read the documentation on their own initiative, so the deprecation message telling them how to solve the problem will save you from responding to the same issue again and again and again. Hm, I could do `VK_NULL_DISPATCHABLE_HANDLE = null` for the types that are always pointers and `VK_NULL_NONDISPATCHABLE_HANDLE = null ? IS_64BIT : 0` for types that are either pointers are integers depending on arch, but those names are a bit long. Your specific example wouldn't work.
Re: Enum that can be 0 or null
On Saturday, 21 May 2016 at 01:09:42 UTC, Alex Parrill wrote: Looks like my best bet is to mark it as deprecated and point them to Vk(Type).init instead. I would prefer to do something like this: enum VK_NULL_HANDLE_0 = 0; enum VK_NULL_HANDLE_PTR = null; Then document it clearly. Alias VK_NULL_HANDLE to one of them and keep it deprecated forever. Many users are not going to read the documentation on their own initiative, so the deprecation message telling them how to solve the problem will save you from responding to the same issue again and again and again.
Re: Enum that can be 0 or null
On Friday, 20 May 2016 at 22:10:51 UTC, tsbockman wrote: Just use null for pointer types, and 0 for integers. D is not C; you aren't *supposed* to be able to just copy-paste any random C snippet into D and expect it to work without modification. If that's not a satisfactory answer, please show some specific examples of code that you don't know how to make work without VK_NULL_HANDLE so that I can propose a workaround. When binding to a C library, it's desirable to have as close to the original C API as possible so that you *can* drop C snippets and examples into D code and have them just work. Obviously, 100% compatibility is not possible, but it is possible to get pretty close most of the time.
Re: Enum that can be 0 or null
On Friday, 20 May 2016 at 22:10:51 UTC, tsbockman wrote: Why do you need to? Just use null for pointer types, and 0 for integers. D is not C; you aren't *supposed* to be able to just copy-paste any random C snippet into D and expect it to work without modification. If that's not a satisfactory answer, please show some specific examples of code that you don't know how to make work without VK_NULL_HANDLE so that I can propose a workaround. Because, as I mentioned in the OP, for VK_NULL_HANDLE to work like it does in C on 32-bit systems, it needs to be compatible with both pointer types (VkDevice, VkInstance, etc) and 64-bit integer types (VkFence, VkSemaphore, many others). There is of course a workaround: since VK_NULL_HANDLE is always zero/null, Vk(Type).init will work as a replacement as long as you match up the types. But then people will try to use VK_NULL_HANDLE (as every C or C++ Vulkan tutorial or reference will instruct) and find out that either a) it doesn't work or b) it works fine on their 64 bit development system, but then cause a ton of errors when they try to compile their project for a 32-bit system. Looks like my best bet is to mark it as deprecated and point them to Vk(Type).init instead.
Re: Enum that can be 0 or null
On Friday, 20 May 2016 at 13:39:50 UTC, Alex Parrill wrote: (How) can I make a constant that is either zero or null depending on how it is used? In the general case, I don't think this is possible in D currently. (It will become possible if the proposed "multiple alias this" ever gets implemented, though: http://wiki.dlang.org/DIP66 ) However, various workarounds are possible depending on the context. Anyone have an idea on how to make this work? Why do you need to? Just use null for pointer types, and 0 for integers. D is not C; you aren't *supposed* to be able to just copy-paste any random C snippet into D and expect it to work without modification. If that's not a satisfactory answer, please show some specific examples of code that you don't know how to make work without VK_NULL_HANDLE so that I can propose a workaround.