digitalmars.D - Using a C++ class in a D associative array
- Jacob Carlborg (56/56) Aug 20 2018 I'm playing around with using the DMD frontend as a library. I wanted to...
- kinke (17/29) Aug 20 2018 I guess the compiler uses the AA key type's TypeInfo, which is
- Jacob Carlborg (6/36) Aug 22 2018 This could be solved, I think, with having "TypeInfo.getHash" a template...
- kinke (4/8) Aug 22 2018 It could be simpler (and slower ;)) by using `m_flags &
- Jacob Carlborg (5/7) Aug 25 2018 There's Objective-C classes as well, which does not seem to have an
- Paul O'Neil (2/2) Aug 20 2018 Potentially related:
I'm playing around with using the DMD frontend as a library. I wanted to store some nodes from the AST in a set. Since there doesn't seem to be a set container that is shipped with Phobos I wrapped an associative array and added some functions to use the AA as a set. To my surprise when I did that my code started to behave strangely. It started to print out things like this: foo 0x10bc70800 bar 0x10bc70c00 I've been banging my head against the wall for several days trying to figure out why the above output was printed. I've been modifying my own code and removed as much as possible, but it didn't contained any calls to any "write" or "printf" functions. The problem seemed to appear when I put an AST node in the set (associative array). So I started to look through the AST classes and looking for overrides of "toHash". None of the AST nodes override "toHash". Then I noticed a suspicious "printf" call in a "print" method in the RootObject class in the DMD source code [1] (this class is the root of all classes in DMD). I removed the call to "printf" and now the output was gone. Trying to figure out what was calling this "print" method, that had called "printf", I added a failing assert. Thanks to the druntime printing out the stacktrace of an uncaught exception, I got this output (reduce): ??:? _d_assertp [0xd4d3311] ../../../.dub/packages/dmd-master/dmd/src/dmd/func.d:494 _ZN15FuncDeclaration5printEv [0xd41d97c] ??:? const nothrow trusted ulong object.TypeInfo_Class.getHash(scope const(void*)) [0xd4c825f] ??:? _aaGetY [0xd4e5adc] ~/.dvm/compilers/dmd-2.081.0/osx/bin/../../src/druntime/import/object.d:326 pure nothrow safe void dlp.core.set.Set!(dmd.func.FuncDeclaration).Set.put(dmd.func.FuncDeclaration) [0xd30cab9] At the third line there's a call from object.TypeInfo_Class.getHash. I looked up to see what the "getHash" method is doing in druntime [2], the method looks like this: override size_t getHash(scope const void* p) trusted const { auto o = *cast(Object*)p; return o ? o.toHash() : 0; } The method is basically calling "toHash" on the passed in object. But none of the AST nodes were overriding "toHas", hmm. Then I started to think, all the AST nodes in DMD are C++ classes (both LDC and GDC are written in C++ and need to use the AST). Without really thinking of it I had put instance of C++ classes as keys in a D associative array. This is, for some reason, calling object.TypeInfo_Class.getHash which only expects to operate on D classes. I'm guessing that somehow what is usually the "toHash" method in a D class matched up with the "print" method in the C++ class. All this just compiled without any error or warnings. No runtime exceptions or asserts were triggered. I just got a really weird behavior. [1] https://github.com/dlang/dmd/blob/c3f6320d59ec14d6fa81a18f92b59393671b346a/src/dmd/root/rootobject.d#L55-L58 [2] https://github.com/dlang/druntime/blob/430585650930797369eeb5b3b142faba10572f10/src/object.d#L1712-L1716 -- /Jacob Carlborg
Aug 20 2018
On Monday, 20 August 2018 at 22:16:09 UTC, Jacob Carlborg wrote:At the third line there's a call from object.TypeInfo_Class.getHash. I looked up to see what the "getHash" method is doing in druntime [2], the method looks like this: override size_t getHash(scope const void* p) trusted const { auto o = *cast(Object*)p; return o ? o.toHash() : 0; }I guess the compiler uses the AA key type's TypeInfo, which is available for extern(C++) classes too. The TypeInfo_Class.getHash() then uses the dynamic type via virtual call, (wrongly) assuming it's always a D class. For an extern(C++) class, it will either call another virtual function (no inherited virtual functions from Object), what you were seeing, or attempt to call... something. ;)All this just compiled without any error or warnings. No runtime exceptions or asserts were triggered. I just got a really weird behavior.This is somewhat special due to the common TypeInfo.getHash() signature for all kinds of types, and so just taking a hairy void* pointer to the byte/real/static array/AA/object/… to be hashed. Polishing C++ interop with extern(C++) classes (matching ctors/dtors, mixed class hiearchies, ability to easily allocate/construct/destruct/free on the other language side etc.) has started with v2.081 and is still on-going; there are probably more places in druntime silently assuming a D class.
Aug 20 2018
On 2018-08-21 02:07, kinke wrote:On Monday, 20 August 2018 at 22:16:09 UTC, Jacob Carlborg wrote:This could be solved, I think, with having "TypeInfo.getHash" a template taking the actual type and not void*. That template can then inspect if the passed type is a D class or any other type of class and act accordingly. -- /Jacob CarlborgAt the third line there's a call from object.TypeInfo_Class.getHash. I looked up to see what the "getHash" method is doing in druntime [2], the method looks like this: override size_t getHash(scope const void* p) trusted const { auto o = *cast(Object*)p; return o ? o.toHash() : 0; }I guess the compiler uses the AA key type's TypeInfo, which is available for extern(C++) classes too. The TypeInfo_Class.getHash() then uses the dynamic type via virtual call, (wrongly) assuming it's always a D class. For an extern(C++) class, it will either call another virtual function (no inherited virtual functions from Object), what you were seeing, or attempt to call... something. ;)All this just compiled without any error or warnings. No runtime exceptions or asserts were triggered. I just got a really weird behavior.This is somewhat special due to the common TypeInfo.getHash() signature for all kinds of types, and so just taking a hairy void* pointer to the byte/real/static array/AA/object/… to be hashed. Polishing C++ interop with extern(C++) classes (matching ctors/dtors, mixed class hiearchies, ability to easily allocate/construct/destruct/free on the other language side etc.) has started with v2.081 and is still on-going; there are probably more places in druntime silently assuming a D class.
Aug 22 2018
On Wednesday, 22 August 2018 at 19:25:40 UTC, Jacob Carlborg wrote:This could be solved, I think, with having "TypeInfo.getHash" a template taking the actual type and not void*. That template can then inspect if the passed type is a D class or any other type of class and act accordingly.It could be simpler (and slower ;)) by using `m_flags & ClassFlags.isCPPclass`.
Aug 22 2018
On 2018-08-22 23:00, kinke wrote:It could be simpler (and slower ;)) by using `m_flags & ClassFlags.isCPPclass`.There's Objective-C classes as well, which does not seem to have an entry in TypeInfo_Class.ClassFlags. -- /Jacob Carlborg
Aug 25 2018
Potentially related: https://issues.dlang.org/show_bug.cgi?id=13875
Aug 20 2018