digitalmars.D.learn - Are D classes proper reference types?
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (20/20) Jun 23 2021 D classes are distinct from structs because they are intended to
- kinke (11/12) Jun 24 2021 Yes, class *refs* are always pointers. *scope* classes are
- Adam D Ruppe (5/7) Jun 24 2021 I used it for my database thing where it is supposed to be
- Stefan Koch (3/9) Jun 24 2021 The dmd frontend uses them all the time to avoid allocation
- kinke (3/13) Jun 24 2021 I was talking about not having seen a `scope class C { ... }`,
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (12/22) Jun 24 2021 But the user code cannot depend on it being stack allocated? So I
- kinke (26/45) Jun 25 2021 Well AFAIK it's mandated by the language, so an RC scheme
- kinke (5/5) Jun 25 2021 Wrt. manual non-heap allocations (stack/data segment/emplace
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (14/19) Jun 25 2021 Destruction is a bit tricky. If people rely on the destructor to
- IGotD- (8/13) Jun 25 2021 You cannot use the most significant bit as it will not work with
- IGotD- (8/15) Jun 25 2021 One thing I have found out over the years that if you want to
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (4/11) Jun 25 2021 Hm. Not sure if I follow, I think we are talking about stuffing
- IGotD- (13/16) Jun 25 2021 Then I misunderstood. If it's a counter it should be fine.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (14/25) Jun 27 2021 Which languages use fat pointers? C++ may use it (but is not
- IGotD- (14/19) Jun 27 2021 Probably about all managed languages. One common method is a that
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (16/24) Jun 27 2021 I am sceptical of this assumption. There are no reasons for a GC
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (17/41) Jun 25 2021 Yes, if people don't make assumptions about where the class ends
- kinke (16/21) Jun 25 2021 I cannot imagine how weak pointers would work without an ugly
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (24/38) Jun 26 2021 So, my understanding is that C++ `make_shared` may allocate the
- kinke (13/22) Jun 26 2021 I'm pretty sure I haven't seen a weak pointer in C++ yet. I don't
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (17/30) Jun 26 2021 Weak pointers are usually not needed, but sometimes you do need
- kinke (11/17) Jun 26 2021 Sure thing, with `extern(C++) class` of course.
- kinke (3/11) Jun 26 2021 Oh, I've just seen that unique_ptr is already implemented in
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (12/24) Jun 27 2021 That is all good, but it will lead to `extern(C++) class`
- kinke (14/17) Jun 27 2021 The assumption that all D code and all classes therein are or
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/13) Jun 27 2021 You don't have to get rid of it, just implicitly declare it for
- kinke (11/24) Jun 27 2021 It's not about classes using monitors themselves, it's about
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/13) Jun 27 2021 I wasn't aware this was a thing. If people want this they can
- kinke (8/12) Jun 27 2021 LDC isn't tied to clang at all, we just use the same backend. -
- zjh (3/4) Jun 27 2021 We need organized action,Instead of doing it blindly.
- Mathias LANG (15/23) Jun 27 2021 [Source: Itanium ABI, 2.6.2 VTT
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (7/13) Jun 27 2021 Well, that could be an issue, but it is not likely to change fast
- Mike Parker (4/5) Jun 27 2021 Slack isn't like our IRC or Discord channels. It's more async
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/7) Jun 27 2021 Thanks for the info, I might look into Slack if it doesn't
- Mathias LANG (14/25) Jun 28 2021 The issue is twofold: it requires us to follow upstream changes
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (22/34) Jun 28 2021 You don't need strict consensus, but you need at least one
D classes are distinct from structs because they are intended to be bound to a reference (pointer) and not addressed as a value (inline/copying). But for efficiency reasons, scoped classes can stack-allocate, but my understanding is that the compiler can still allocate it on the GC heap? So it is still a reference type, I guess? But how about "emplace", does "emplace" imply the the compiler cannot put it on the GC heap? Yes, I am aware that this is library construct, but still relevant. The reason I am asking this is because implementing reference counting in a clean fashion requires classes to be proper reference types, so that means disabling scoped classes and emplaced classes, and only allow it where a reference/pointer to a class never exceed a reference count of 1. The core of my question is this: is it at all reasonable for "user code" to assume that a class is not allocated on the heap? Is it reasonable to write code that assumes that it is allocated "inline" as with emplace? Because if it is, then then the advantages of having classes as reference types and making them different from structs are lost.
Jun 23 2021
On Thursday, 24 June 2021 at 06:50:44 UTC, Ola Fosheim Grøstad wrote:[...]Yes, class *refs* are always pointers. *scope* classes are deprecated (I don't think I've ever seen one); with `scope c = new Object`, you can have the compiler allocate a class *instance* on the stack for you, but `c` is still a *ref*. `emplace` doesn't allocate, you have to pass the memory explicitly. A class *instance* can also live in the static data segment (`static immutable myStaticObject = new Object;`); `extern(C++)` class instances can also live on the C++ heap/stack etc. etc.
Jun 24 2021
On Thursday, 24 June 2021 at 07:28:56 UTC, kinke wrote:*scope* classes are deprecated (I don't think I've ever seen one);I used it for my database thing where it is supposed to be destroyed reliably but also uses runtime polymorphism. I now suggest people just stick `scope(exit) .destroy(obj);` any time you use it.
Jun 24 2021
On Thursday, 24 June 2021 at 07:28:56 UTC, kinke wrote:On Thursday, 24 June 2021 at 06:50:44 UTC, Ola Fosheim Grøstad wrote:The dmd frontend uses them all the time to avoid allocation overhead for Visitors.[...](I don't think I've ever seen one); with `scope c = new Object`, you can have the compiler allocate a class *instance* on the stack for you, but `c` is still a *ref*.
Jun 24 2021
On Thursday, 24 June 2021 at 12:31:08 UTC, Stefan Koch wrote:On Thursday, 24 June 2021 at 07:28:56 UTC, kinke wrote:I was talking about not having seen a `scope class C { ... }`, *not* the `scope` storage class as in the example.On Thursday, 24 June 2021 at 06:50:44 UTC, Ola Fosheim Grøstad wrote:The dmd frontend uses them all the time to avoid allocation overhead for Visitors.[...](I don't think I've ever seen one); with `scope c = new Object`, you can have the compiler allocate a class *instance* on the stack for you, but `c` is still a *ref*.
Jun 24 2021
On Thursday, 24 June 2021 at 07:28:56 UTC, kinke wrote:Yes, class *refs* are always pointers. *scope* classes are deprecated (I don't think I've ever seen one); with `scope c = new Object`, you can have the compiler allocate a class *instance* on the stack for you, but `c` is still a *ref*.But the user code cannot depend on it being stack allocated? So I could replace the Object reference with a reference counting pointer and put the counter at a negative offset?`emplace` doesn't allocate, you have to pass the memory explicitly.This is more of a problem. I was thinking about arrays that provide an emplace method, then one could replace emplace with heap allocation. I guess it isn't really possible to make `emplace` with custom memory work gracefully with reference counting with ref count at negative offset.A class *instance* can also live in the static data segment (`static immutable myStaticObject = new Object;`);But it isn't required to? It certainly wouldn't work with reference counting if it is stored in read only memory...`extern(C++)` class instances can also live on the C++ heap/stack etc. etc.Yes, that cannot be avoided.
Jun 24 2021
On Friday, 25 June 2021 at 06:09:17 UTC, Ola Fosheim Grøstad wrote:On Thursday, 24 June 2021 at 07:28:56 UTC, kinke wrote:Well AFAIK it's mandated by the language, so an RC scheme replacing such allocations by heap ones seems like a definite step backwards - it's a useful pattern, and as Stefan pointed out, definitely in use. You could still stack-allocate but accommodate for the counter prefix in the compiler.Yes, class *refs* are always pointers. *scope* classes are deprecated (I don't think I've ever seen one); with `scope c = new Object`, you can have the compiler allocate a class *instance* on the stack for you, but `c` is still a *ref*.But the user code cannot depend on it being stack allocated? So I could replace the Object reference with a reference counting pointer and put the counter at a negative offset?It's certainly possible as it's a library thing; some existing code may assume the returned reference to point to the beginning of the passed memory though (where there'd be your counter). What you'd definitely need to adjust is `__traits(classInstanceSize)`, accomodating for the extra counter prefix. There's very likely existing code out there which doesn't use druntime's emplace[Initializer], but does it manually.`emplace` doesn't allocate, you have to pass the memory explicitly.This is more of a problem. I was thinking about arrays that provide an emplace method, then one could replace emplace with heap allocation. I guess it isn't really possible to make `emplace` with custom memory work gracefully with reference counting with ref count at negative offset.Not required to AFAIK, but if it's not statically allocated, you'd need to allocate it at runtime via some module or CRT ctor. It's probably easier to have the compiler put it into static but writable memory, so that you can mess with the counter. --- All in all, I think a more interesting/feasible approach would be abusing the monitor field of extern(D) classes for the reference counter. It's the 2nd field (of pointer size) of each class instance, directly after the vptr (pointer to vtable). I think all monitor access goes through a druntime call, so you could hook into there, disallowing any regular monitor access, and put this (AFAIK, seldomly used) monitor field to some good use.A class *instance* can also live in the static data segment (`static immutable myStaticObject = new Object;`);But it isn't required to? It certainly wouldn't work with reference counting if it is stored in read only memory...
Jun 25 2021
Wrt. manual non-heap allocations (stack/data segment/emplace etc.), you could e.g. reserve the most significant bit of the counter to denote such instances and prevent them from being free'd (and possibly finalization/destruction too; this would need some more thought I suppose).
Jun 25 2021
On Friday, 25 June 2021 at 07:17:20 UTC, kinke wrote:Wrt. manual non-heap allocations (stack/data segment/emplace etc.), you could e.g. reserve the most significant bit of the counter to denote such instances and prevent them from being free'd (and possibly finalization/destruction too; this would need some more thought I suppose).Destruction is a bit tricky. If people rely on the destructor to run when the function returns then that cannot be moved to a reference counter. For instance if they have implemented some kind of locking mechanism or transaction mechanism with classes… The most tricky one is emplace though as you have no way of releasing the memory without an extra function pointer. Regarding using high bits in the counter; What you would want is to have a cheap increment/decrement and instead take the hit when the object is released. So you might actually instead want to keep track of the allcation-status in the lower 3 bits and instead do ±8, but I am not sure how that affects different CPUs. The basic idea, would be to make it so you don't trigger destruction on 0, but when the result is/becomes negative.
Jun 25 2021
On Friday, 25 June 2021 at 07:17:20 UTC, kinke wrote:Wrt. manual non-heap allocations (stack/data segment/emplace etc.), you could e.g. reserve the most significant bit of the counter to denote such instances and prevent them from being free'd (and possibly finalization/destruction too; this would need some more thought I suppose).You cannot use the most significant bit as it will not work with some 32-bit systems. Linux with a 3G kernel position for example. Better to use the least significant bit as all allocated memory is guaranteed to be aligned. Regardless this requires compiler support for masking off this bit. Now where going into halfway fat pointer support. Then we can just use fat pointers instead and have full freedom.
Jun 25 2021
On Friday, 25 June 2021 at 17:37:13 UTC, IGotD- wrote:You cannot use the most significant bit as it will not work with some 32-bit systems. Linux with a 3G kernel position for example. Better to use the least significant bit as all allocated memory is guaranteed to be aligned. Regardless this requires compiler support for masking off this bit. Now where going into halfway fat pointer support. Then we can just use fat pointers instead and have full freedom.One thing I have found out over the years that if you want to have full versatility, have a pointer to your free function in your fat pointer. By having this you have generic method to free your object when it goes out of scope. You have the ability to use custom allocators and even change allocators on the fly. If you for some reason don't want to free your object automatically, just put zero in that field for example.
Jun 25 2021
On Friday, 25 June 2021 at 17:37:13 UTC, IGotD- wrote:You cannot use the most significant bit as it will not work with some 32-bit systems. Linux with a 3G kernel position for example. Better to use the least significant bit as all allocated memory is guaranteed to be aligned. Regardless this requires compiler support for masking off this bit.Hm. Not sure if I follow, I think we are talking about stuffing bits into the counter and not the address?Now where going into halfway fat pointer support. Then we can just use fat pointers instead and have full freedom.But fat pointers are 16 bytes, so quite expensive.
Jun 25 2021
On Friday, 25 June 2021 at 20:22:24 UTC, Ola Fosheim Grøstad wrote:Hm. Not sure if I follow, I think we are talking about stuffing bits into the counter and not the address?Then I misunderstood. If it's a counter it should be fine.But fat pointers are 16 bytes, so quite expensive.Yes, that's a tradeoff but one I'm willing to take. I'm thinking even bigger managed pointers of perhaps 32 bytes which has more metadata like the allocated size. Managed languages in general have fat pointers which we see everywhere and it is not a big deal. If you are littering pointers you perhaps should refactor your code, use an array if loads of objects of the same type. Another thing which I'm not that satisfied with D is that there is no built in method of expanding member classes into the host class like C++ which creates pointer littering and memory fragmentation.
Jun 25 2021
On Friday, 25 June 2021 at 21:05:36 UTC, IGotD- wrote:Yes, that's a tradeoff but one I'm willing to take. I'm thinking even bigger managed pointers of perhaps 32 bytes which has more metadata like the allocated size. Managed languages in general have fat pointers which we see everywhere and it is not a big deal.Which languages use fat pointers? C++ may use it (but is not required to).If you are littering pointers you perhaps should refactor your code, use an array if loads of objects of the same type.This is what I want to avoid as it makes refcounting more difficult. If D classes are reference types then they should always be referred to through a pointer. If you want to put it in an array, use a struct.Another thing which I'm not that satisfied with D is that there is no built in method of expanding member classes into the host class like C++ which creates pointer littering and memory fragmentation.Not sure what you mean by expanding? I never liked `alias this` for structs, inheritance would be simpler. Is this what you mean by expanding? I think classes in C++ are usually used more like structs in D. C++ programmers tend to avoid using virtuals, so D-style classes (C++ classes with virtual members) tend to be a smaller part of C++ codebases (but it depends on the project, obviously).
Jun 27 2021
On Sunday, 27 June 2021 at 07:48:22 UTC, Ola Fosheim Grøstad wrote:Which languages use fat pointers? C++ may use it (but is not required to).Probably about all managed languages. One common method is a that it is actually an identifier it is used in a hash table. Then you can find all sorts of meta data in there. My original point was that this consume more memory in managed languages but nobody seems to mind.Not sure what you mean by expanding? I never liked `alias this` for structs, inheritance would be simpler. Is this what you mean by expanding?When you use a struct as a member variable in another struct the data will be expanded into the host struct. If the member struct is 16 bytes then the host struct will have grow 16 bytes to accommodate that member struct. This is not the case in D with classes as classes always are allocated on the heap using dynamic allocation. This leads to more fragmentation and memory consumption.
Jun 27 2021
On Sunday, 27 June 2021 at 09:35:10 UTC, IGotD- wrote:Probably about all managed languages.I am sceptical of this assumption. There are no reasons for a GC language to require the usage of fat pointers?When you use a struct as a member variable in another struct the data will be expanded into the host struct. If the member struct is 16 bytes then the host struct will have grow 16 bytes to accommodate that member struct. This is not the case in D with classes as classes always are allocated on the heap using dynamic allocation. This leads to more fragmentation and memory consumption.Ok, I understand what you mean, but classes tend to be used for "big objects". I don't think there is anything that prevents a private class reference to be replaced by an inline representation as an optimization if no references ever leak. If you use whole program optimizations such things could also be done for public members. What is holdning D back here is the lack of a high level IR after the frontend where such global passes could improve the implementation quality. So, this is at the core of good language design; keep the model simple, but enable and allow optimizations. Too much special casing and you end up with a language that is difficult to extend and a neverending discovery of corner cases and compiler bugs.
Jun 27 2021
On Friday, 25 June 2021 at 07:01:31 UTC, kinke wrote:Well AFAIK it's mandated by the language, so an RC scheme replacing such allocations by heap ones seems like a definite step backwards - it's a useful pattern, and as Stefan pointed out, definitely in use. You could still stack-allocate but accommodate for the counter prefix in the compiler.Yes, but then I need to mark it as non-freeable.It's certainly possible as it's a library thing; some existing code may assume the returned reference to point to the beginning of the passed memory though (where there'd be your counter). What you'd definitely need to adjust is `__traits(classInstanceSize)`, accomodating for the extra counter prefix.Yes, if people don't make assumptions about where the class ends and overwrites some other object, but I suspect pointer arithmetics isn't all that common for classes in D code.There's very likely existing code out there which doesn't use druntime's emplace[Initializer], but does it manually.I guess, but the compiler could have a release note warning against this.ctor. It's probably easier to have the compiler put it into static but writable memory, so that you can mess with the counter.Another reason to add the ability to mark it as non-freeable.All in all, I think a more interesting/feasible approach would be abusing the monitor field of extern(D) classes for the reference counter. It's the 2nd field (of pointer size) of each class instance, directly after the vptr (pointer to vtable). I think all monitor access goes through a druntime call, so you could hook into there, disallowing any regular monitor access, and put this (AFAIK, seldomly used) monitor field to some good use.Yes, if you don't want to support weak pointers. I think you need two counters if you want to enable the usage of weak pointers. One reason to put it at a negative offset is that it makes it possible to make it fully compatible with shared_ptr. And then you can also have additional fields such as a weak counter or a deallocation function pointer. I don't think maintaining the D ABI is important, so one could add additional fields to the class. Maintaining core language semantics shouldn't require ABI support I think.
Jun 25 2021
On Friday, 25 June 2021 at 17:05:41 UTC, Ola Fosheim Grøstad wrote:Yes, if you don't want to support weak pointers. I think you need two counters if you want to enable the usage of weak pointers.I cannot imagine how weak pointers would work without an ugly extra indirection layer. If we're on the same page, we're talking about embedding the reference counter *directly* in the class instance, and the class ref still pointing directly to the instance. Weak pointers aren't in the language, so I don't see why they would matter here. I thought you were after replacing GC-allocated class instances by a simple RC scheme.One reason to put it at a negative offset is that it makes it possible to make it fully compatible with shared_ptr.In modern C++ code I've been looking at so far, shared_ptr was used very rarely (and unique_ptr everywhere). AFAIK, the main reason being poor performance due to the extra indirection of shared_ptr. So replacing every D class ref by a shared_ptr-analogon for interop reasons would seem very backwards to me.
Jun 25 2021
On Friday, 25 June 2021 at 23:55:40 UTC, kinke wrote:I cannot imagine how weak pointers would work without an ugly extra indirection layer. If we're on the same page, we're talking about embedding the reference counter *directly* in the class instance, and the class ref still pointing directly to the instance.So, my understanding is that C++ `make_shared` may allocate the reference chunk and the object in the same memory area, so that the reference count/weak counter is at a negative offset. That way you can free up the object while retaining the counter. (At some fragmentation cost, if the counter isn't freed.) This may give some cache advantages over separate allocation. Maybe weak could be an annotation, but I haven't given this much thought. If we think about it; You don't have to pass around weak pointers, so there is no reason for parameters to be marked weak? Only pointer-fields in structs or classes? In function bodies you would typically not want to use a weak pointer as you want to extend the lifetime of the object until the function returns. Also you could mark a class as non-weak, to save the weak counter.Weak pointers aren't in the language, so I don't see why they would matter here. I thought you were after replacing GC-allocated class instances by a simple RC scheme.One goal could be to make a class compatible with C++ or Swift on request. Both support weak pointers. You could have multiple ref-count layout schemes as long as they all are on negative offsets. Just don't mix class hierarchies. So you could mix a D class hierarchy, C++ class-hiearchy and Swift class-hierarchy in the same codebase?In modern C++ code I've been looking at so far, shared_ptr was used very rarely (and unique_ptr everywhere). AFAIK, the main reason being poor performance due to the extra indirection of shared_ptr. So replacing every D class ref by a shared_ptr-analogon for interop reasons would seem very backwards to me.In C++ ownership should in general be kept local and one should use borrowing pointers (raw pointers) for actual computations, so only use owned pointers for transfer of ownership. But that puts more of a burden on the developer.
Jun 26 2021
On Saturday, 26 June 2021 at 07:00:37 UTC, Ola Fosheim Grøstad wrote:I'm pretty sure I haven't seen a weak pointer in C++ yet. I don't look at C++ much anymore, but I suspect they are even a lot rarer than shared_ptr. Wrt. mixed class hierarchies, being able to inherit from and instantiate C++ classes in D is of some priority and mostly works today. (Let's not get into discussing multiple inheritance here, it's hardly a show-stopper for most use cases.) Is Swift a thing outside the Apple universe (I admittedly despise ;))? It's surely much better than their Objective-C crap, but still... So for rare use cases like shared_ptr/weak pointer interop, a library solution (just like they are in C++) is IMO enough.Weak pointers aren't in the language, so I don't see why they would matter here. I thought you were after replacing GC-allocated class instances by a simple RC scheme.One goal could be to make a class compatible with C++ or Swift on request. Both support weak pointers. You could have multiple ref-count layout schemes as long as they all are on negative offsets. Just don't mix class hierarchies. So you could mix a D class hierarchy, C++ class-hiearchy and Swift class-hierarchy in the same codebase?
Jun 26 2021
On Saturday, 26 June 2021 at 07:39:44 UTC, kinke wrote:I'm pretty sure I haven't seen a weak pointer in C++ yet. I don't look at C++ much anymore, but I suspect they are even a lot rarer than shared_ptr.Weak pointers are usually not needed, but sometimes you do need them and then not having them becomes a big weakness. Usually ones strives to have ownership defined in a way that makes it possible to dismantle a graph in a structured way from a single thread, and then weak pointers are not needed at all.Wrt. mixed class hierarchies, being able to inherit from and instantiate C++ classes in D is of some priority and mostly works today. (Let's not get into discussing multiple inheritance here, it's hardly a show-stopper for most use cases.)Is it possible to inherit from a C++ class and get a D subclass, and is it possible to inherit from a D class and get a C++ class?Is Swift a thing outside the Apple universe (I admittedly despise ;))? It's surely much better than their Objective-C crap, but still...The Apple universe is pretty big, we can dislike that, but that does not change the market… So the goal would be to put the ref-count on a negative offset so that the layout can match up with C++ or Swift. I don't think that implies any big disadvantages, but I could be wrong. The only way to find out is to hash out the alternatives.So for rare use cases like shared_ptr/weak pointer interop, a library solution (just like they are in C++) is IMO enough.But the best solution is to get to a place where you can hand D-objects to other languages with ease without doing a runtime conversion from one layout to another.
Jun 26 2021
On Saturday, 26 June 2021 at 13:49:25 UTC, Ola Fosheim Grøstad wrote:Is it possible to inherit from a C++ class and get a D subclass, and is it possible to inherit from a D class and get a C++ class?Sure thing, with `extern(C++) class` of course.But the best solution is to get to a place where you can hand D-objects to other languages with ease without doing a runtime conversion from one layout to another.With C++, you can today, an `extern(C++) class C` is equivalent to and mangled as C++ `C*`. You can't pass it directly to some `unique_ptr<C>` or `shared_ptr<T>` of course; an according D wrapper reflecting the C++ implementation (library-dependent) would be needed anyway for correct mangling. It'd be implemented as a templated D struct, similar to how `std::vector` works today for the MSVC runtime: https://github.com/dlang/druntime/blob/master/src/core/stdcpp/vector.d
Jun 26 2021
On Saturday, 26 June 2021 at 20:03:01 UTC, kinke wrote:With C++, you can today, an `extern(C++) class C` is equivalent to and mangled as C++ `C*`. You can't pass it directly to some `unique_ptr<C>` or `shared_ptr<T>` of course; an according D wrapper reflecting the C++ implementation (library-dependent) would be needed anyway for correct mangling. It'd be implemented as a templated D struct, similar to how `std::vector` works today for the MSVC runtime: https://github.com/dlang/druntime/blob/master/src/core/stdcpp/vector.dOh, I've just seen that unique_ptr is already implemented in https://github.com/dlang/druntime/blob/master/src/core/stdcpp/memory.d, and according to the tests, working for the MSVC++ runtime, see https://github.com/dlang/druntime/pull/2723/files.
Jun 26 2021
On Saturday, 26 June 2021 at 20:03:01 UTC, kinke wrote:On Saturday, 26 June 2021 at 13:49:25 UTC, Ola Fosheim Grøstad wrote:That is all good, but it will lead to `extern(C++) class` replacing D classes. So why not unify right away? Why wait for the inevitable?Is it possible to inherit from a C++ class and get a D subclass, and is it possible to inherit from a D class and get a C++ class?Sure thing, with `extern(C++) class` of course.With C++, you can today, an `extern(C++) class C` is equivalent to and mangled as C++ `C*`. You can't pass it directly to some `unique_ptr<C>` or `shared_ptr<T>` of course; an according D wrapper reflecting the C++ implementation (library-dependent) would be needed anyway for correct mangling. It'd be implemented as a templated D structYes, this is all good. But if you unify the layout of C++ and D classes and use the same layout as C++ shared_ptr for reference counted D classes then you can easily move back and forth between the languages. I think the presumption that development only happens in D and you only use other people's C++ code is ill advised. One may use a framework in C++ that one also extend in C++, but maybe want to use another language for the high level stuff.
Jun 27 2021
On Sunday, 27 June 2021 at 07:54:38 UTC, Ola Fosheim Grøstad wrote:That is all good, but it will lead to `extern(C++) class` replacing D classes. So why not unify right away? Why wait for the inevitable?The assumption that all D code and all classes therein are or need to be designed for C++ interop is daring. The DMD frontend uses extern(C++) classes almost exclusively, but that's just for LDC and GDC. Getting rid of the monitor field was discussed multiple times. The other major and not so trivial difference is that extern(C++) classes have no TypeInfo pointer (in the first vtable slot for extern(D) classes), which also means that dynamic casts don't work, neither in D nor in C++ (for the latter, only for instances instiantiated on the D side). [AFAIK, most C++ implementations put the - of course totally incompatible - *C++* TypeInfo into vtable slot -1.]
Jun 27 2021
On Sunday, 27 June 2021 at 08:41:27 UTC, kinke wrote:Getting rid of the monitor field was discussed multiple times.You don't have to get rid of it, just implicitly declare it for classes that use monitors? I don't think it has to be at a specific offset?The other major and not so trivial difference is that extern(C++) classes have no TypeInfo pointer (in the first vtable slot for extern(D) classes), which also means that dynamic casts don't work, neither in D nor in C++ (for the latter, only for instances instiantiated on the D side). [AFAIK, most C++ implementations put the - of course totally incompatible - *C++* TypeInfo into vtable slot -1.]But D could just extend C++ typeinfo?
Jun 27 2021
On Sunday, 27 June 2021 at 09:46:45 UTC, Ola Fosheim Grøstad wrote:On Sunday, 27 June 2021 at 08:41:27 UTC, kinke wrote:It's not about classes using monitors themselves, it's about people potentially using `synchronized (obj)` for some arbitrary class reference `obj`. Embedding it in the instance directly prevents having to use a hashtable or similar. But I'm certainly not fond of that field as it's a rare use case.Getting rid of the monitor field was discussed multiple times.You don't have to get rid of it, just implicitly declare it for classes that use monitors? I don't think it has to be at a specific offset?Sure, 'just' :D - as it 'just' takes someone to implement it (for all supported C++ runtimes). It's always the same problem, lots of talk and blabla in the forum, but hardly any real action coming out of it.The other major and not so trivial difference is that extern(C++) classes have no TypeInfo pointer (in the first vtable slot for extern(D) classes), which also means that dynamic casts don't work, neither in D nor in C++ (for the latter, only for instances instiantiated on the D side). [AFAIK, most C++ implementations put the - of course totally incompatible - *C++* TypeInfo into vtable slot -1.]But D could just extend C++ typeinfo?
Jun 27 2021
On Sunday, 27 June 2021 at 10:11:44 UTC, kinke wrote:It's not about classes using monitors themselves, it's about people potentially using `synchronized (obj)` for some arbitrary class reference `obj`.I wasn't aware this was a thing. If people want this they can just embed a mutex in the class themselves. No point in having it everywhere. You usually don't want to coordinate over one instance anyway.Sure, 'just' :D - as it 'just' takes someone to implement it (for all supported C++ runtimes). It's always the same problem,Right, but what does all supported C++ runtimes mean? I thought LDC was tied to clang, which I guess means two runtimes? If C++ doesn't use arbitrary negative offsets, then D could use those?
Jun 27 2021
On Sunday, 27 June 2021 at 12:00:41 UTC, Ola Fosheim Grøstad wrote:On Sunday, 27 June 2021 at 10:11:44 UTC, kinke wrote: Right, but what does all supported C++ runtimes mean? I thought LDC was tied to clang, which I guess means two runtimes? If C++ doesn't use arbitrary negative offsets, then D could use those?LDC isn't tied to clang at all, we just use the same backend. - It's libstdc++ and libc++ in the POSIX world (most likely including MinGW), and MSVC++ for 'native' Windows. Another difficulty is that TypeInfo_Class instances are generated by the compiler, and not in the frontend either, meaning that DMD, GDC and LDC would all need to be adapted.
Jun 27 2021
On Sunday, 27 June 2021 at 10:11:44 UTC, kinke wrote:On Sunday, 27 June 2021 at 09:46:45 UTC, Ola Fosheim GrøstadWe need organized action,Instead of doing it blindly. I always say organize.
Jun 27 2021
On Sunday, 27 June 2021 at 08:41:27 UTC, kinke wrote:On Sunday, 27 June 2021 at 07:54:38 UTC, Ola Fosheim Grøstad wrote: [AFAIK, most C++ implementations put the - of course totally incompatible - *C++* TypeInfo into vtable slot -1.]Actually my understanding is that it's part of the ABI:Each virtual table address in the VTT is the address to be assigned to the respective virtual pointer, i.e. the address past the end of the typeinfo pointer (the address of the first virtual function pointer, if there are any), not of the first vcall offset.[Source: Itanium ABI, 2.6.2 VTT Order](https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-ctor-vtt) Regarding replacing D's ABI with C++, it *might* be possible today, but: - It takes someone to care about it and want it; - It's going to be a lot of work, so it should come with lots of benefit - I don't see much as of now; - It locks us in a position where we depend on an external committee / implementation to define our ABI. In any case, if you feel like it's worth it Ola, you could start to look into what it takes (druntime/dmd wise) and start to submit PR. For dlang devs questions, I recommend to use the dlang slack.
Jun 27 2021
On Sunday, 27 June 2021 at 23:20:38 UTC, Mathias LANG wrote:- It locks us in a position where we depend on an external committee / implementation to define our ABI.Well, that could be an issue, but it is not likely to change fast or frequently so I don't think it is a high risk approach.In any case, if you feel like it's worth it Ola, you could start to look into what it takes (druntime/dmd wise) and start to submit PR.There is no point unless there is consensus. You first need to get consensus otherwise it is means throwing time into the garbage bin.For dlang devs questions, I recommend to use the dlang slack.I don't have much time for chat, I prefer async communication.
Jun 27 2021
On Monday, 28 June 2021 at 06:13:06 UTC, Ola Fosheim Grøstad wrote:I don't have much time for chat, I prefer async communication.Slack isn't like our IRC or Discord channels. It's more async like the forums.
Jun 27 2021
On Monday, 28 June 2021 at 06:25:39 UTC, Mike Parker wrote:Slack isn't like our IRC or Discord channels. It's more async like the forums.Thanks for the info, I might look into Slack if it doesn't require me to install anything. If it is an official channel you might consider adding it to the community menu or at least: https://wiki.dlang.org/Get_involved
Jun 27 2021
On Monday, 28 June 2021 at 06:13:06 UTC, Ola Fosheim Grøstad wrote:On Sunday, 27 June 2021 at 23:20:38 UTC, Mathias LANG wrote:The issue is twofold: it requires us to follow upstream changes (the case you are thinking of), but also provides us from making non backward-compatible downstream changes (meaning we can't change it as we see fit if we realize there is potential for optimization).- It locks us in a position where we depend on an external committee / implementation to define our ABI.Well, that could be an issue, but it is not likely to change fast or frequently so I don't think it is a high risk approach.Waiting on "consensus" is an easy way to avoid doing any kind of work :) I'm fairly sure most large achievements that have been undertaken by people in this community (that were not W&A) have been done without their (W&A's) blessing. People just went ahead and did it. But obviously those people cared more about getting things done than spending time discussing it on the forums.In any case, if you feel like it's worth it Ola, you could start to look into what it takes (druntime/dmd wise) and start to submit PR.There is no point unless there is consensus. You first need to get consensus otherwise it is means throwing time into the garbage bin.
Jun 28 2021
On Monday, 28 June 2021 at 07:44:25 UTC, Mathias LANG wrote:(the case you are thinking of), but also provides us from making non backward-compatible downstream changes (meaning we can't change it as we see fit if we realize there is potential for optimization).Has this ever happened?Waiting on "consensus" is an easy way to avoid doing any kind of work :)You don't need strict consensus, but you need at least one compiler team to agree that it is a worthwhile.I'm fairly sure most large achievements that have been undertaken by people in this community (that were not W&A) have been done without their (W&A's) blessing. People just went ahead and did it. But obviously those people cared more about getting things done than spending time discussing it on the forums.Was that a snide comment? Totally uncalled for, I certainly don't depend on anyones blessing to play with my own fork, but it does not affect anything outside it. Making a PR for a repo without acceptance is utterly pointless and a waste of effort. Nobody should do it. They will just end up feeling miserable about what they could instead have spent their time on (including kids and family). I am 100% confident that there has been a massive waste of effort in the D history that is due to a lack of coordination. Ranging from libraries that went nowhere to PRs that dried up and died. Individual PRs won't fix the whole. The whole can only be fixed with a plan. To get to a place where you can plan you need to form a vision. To form a vision you need to work towards consensus. You cannot fix poor organization with PRs. The PR-demanding crowd is off the rails irrational. Cut down on the excuses, start planning! (What large achievements are you speaking of, by the way?)
Jun 28 2021