October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner |
On 14.10.2015 13:39, Szymon Gatner wrote:
> On Tuesday, 13 October 2015 at 20:10:22 UTC, Rainer Schuetze wrote:
>>
>>
>> On 13.10.2015 21:44, ZombineDev wrote:
>>> [...]
>>
>> The library issues are the same for 32-bit and 64-bit.
>>
>>> [...]
>>
>> Yes, but there is some magic involved when linking against the VS2015
>> CRT. To use symbols like snprintf and sscanf, you must also pass
>> legacy_stdio_definitions.lib to the linker, which is done
>> automatically if you use dmd as a frontend to the linker, but not cl.
>>
>> Symbols _minfo_beg, _minfo_end, _deh_beg and _deh_end are only emitted
>> by the compiler if you compile main, WinMain or DllMain.
>> Unfortunately, compiling D's main also generates a C-style main, so
>> it's not so easy to get these symbols if you need main in C/C++.
>>
>> I would currently recommend to write main in D, and call into C/C++
>> from there.
>
> I am trying (as with every new release ;)) to link static D library to
> existing C++ project and I am having same issue:
>
> Error 2 error LNK2019: unresolved external symbol _snprintf
> referenced in function
> _D2gc6config13__T5parseHTfZ5parseFNbNiAxaKAxaKfZb
> C:\Users\Bravo\documents\visual studio
> 2012\Projects\ConsoleApplication1\ConsoleApplication1\phobos32mscoff.lib(config_48f_452.obj)
> ConsoleApplication1
I just noticed that the magic symbol translation _snprintf -> __snprintf isn't included without linking the internal function init_msvc (which is normally done by d_run_main which is called by the generated C main).
The current workaround here is to add
extern "C" void __cdecl init_msvc();
to the C source and call it before rt_init. I think we should move it into rt_init inside druntime.
|
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to ZombineDev | On 14.10.2015 13:46, ZombineDev wrote: > On Tuesday, 13 October 2015 at 20:10:22 UTC, Rainer Schuetze wrote: >> Yes, but there is some magic involved when linking against the VS2015 >> CRT. To use symbols like snprintf and sscanf, you must also pass >> legacy_stdio_definitions.lib to the linker, which is done >> automatically if you use dmd as a frontend to the linker, but not cl. >> >> Symbols _minfo_beg, _minfo_end, _deh_beg and _deh_end are only emitted >> by the compiler if you compile main, WinMain or DllMain. >> Unfortunately, compiling D's main also generates a C-style main, so >> it's not so easy to get these symbols if you need main in C/C++. >> >> I would currently recommend to write main in D, and call into C/C++ >> from there. > > Thanks for the answer. > > Just for the reference, under VS2015 x86 Native Tools Command Prompt I > tried to link a C++ main function that only calls rt_init() and > rt_term() like this: > > cl main.cpp C:\D\dmd2\windows\lib32mscoff\phobos32mscoff.lib "C:\Program > Files (x86)\Microsoft Visual Studio > 14.0\VC\lib\legacy_stdio_definitions.lib" > > I got: > main.cpp > phobos32mscoff.lib(sections_win64_6de_5a5.obj) : error LNK2019: > unresolved external symbol __minfo_beg referenced in function > _D2rt14sections_win6414getModuleInfosFZAyPS6object10ModuleInfo > phobos32mscoff.lib(sections_win64_6de_5a5.obj) : error LNK2019: > unresolved external symbol __minfo_end referenced in function > _D2rt14sections_win6414getModuleInfosFZAyPS6object10ModuleInfo > main.exe : fatal error LNK1120: 2 unresolved externals > > Which, based on your answer, is expected. > > I'm looking into integrating D into an existing C++ build and I need to > statically link the D library. So my next questions are: > 1) Where can I find legacy_stdio_definitions.lib? > P.S.: It's included in VS (C:\Program Files (x86)\Microsoft Visual > Studio 14.0\VC\lib\legacy_stdio_definitions.lib) Your C++ project should have the library paths setup correctly to find it if you just add it to the additional input libraries. > 2) How can I workaround the problem that _minfo* and _deh* are not > generated because my main is in C++? > As Szymon noticed, you can compile an empty main into the static D library. The additional C main that is generated by DMD won't cause ambiguities as long as your C main is not also in a static library. |
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rainer Schuetze | On Wednesday, 14 October 2015 at 12:35:30 UTC, Rainer Schuetze wrote: > > I just noticed that the magic symbol translation _snprintf -> __snprintf isn't included without linking the internal function init_msvc (which is normally done by d_run_main which is called by the generated C main). > > The current workaround here is to add > > extern "C" void __cdecl init_msvc(); > > to the C source and call it before rt_init. I think we should move it into rt_init inside druntime. This indeed helps, thanks! There is new problem: creating a class instance on D side, then passing it to C++ and then back to D causes casting errors. My Example (based on Adam Ruppe's example): C++ file: #include <iostream> #include <stdio.h> extern "C" int rt_init(); extern "C" void rt_term(); extern "C++" int sumInD(int a, int b); extern "C++" void printHelloInD(); class Operation { public: virtual ~Operation() {} virtual int execute(int a, int b) = 0; }; class Add : public Operation { int execute(int a, int b) override { return a + b; } }; extern "C++" void useOperation(Operation* t); extern "C++" Operation* getSubtract(); extern "C++" void freeSubtract(Operation* cat); struct DRuntime { DRuntime() { if (!rt_init())\ { throw std::runtime_error("D Initialization failed"); } } ~DRuntime() { rt_term(); } }; void main() { DRuntime druntime; Add add; useOperation(&add); Operation* sub = getSubtract(); auto value = sub->execute(5, 3); freeSubtract(sub); } D library: module lib; import std.stdio; import core.stdc.stdlib; int main() { return 0; } extern(C++) interface Operation { void _destructorDoNotUse(); int execute(int a, int b); } class Subtract : Operation { extern(C++) void _destructorDoNotUse() {} extern(C++) int execute(int a, int b) {return a - b;} } extern(C++) Operation getSubtract() { import std.conv; enum size = __traits(classInstanceSize, Subtract); auto memory = malloc(size)[0 .. size]; return emplace!Subtract(memory); } extern (C++) int sumInD(int a, int b) { return a + b; } extern (C++) void printHelloInD() { writeln("Hello From D!"); << ===== throws exception (bug reported) } extern(C++) void freeSubtract(Operation animal) { auto cat = cast(Subtract) animal; <<====== cast yields null if(cat !is null) { destroy(cat); free(cast(void*) cat); } } extern(C++) void useOperation(Operation t) { auto res = t.execute(1, 2); } Everything works fine except the marked line. Subtract instance seems to be created correctly on D side as it returns valid result in the execute() call on C++ side but then when trying to free the instance on D side again, downcast from Operation to Subtract fails resulting in null. |
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner | On Wednesday, 14 October 2015 at 11:54:22 UTC, Szymon Gatner wrote: > On Wednesday, 14 October 2015 at 11:46:27 UTC, ZombineDev wrote: > >> 2) How can I workaround the problem that _minfo* and _deh* are not generated because my main is in C++? >> > > Just add a file with int main() in D library to fix this. Thanks! I didn't realize that I could add an empty main in D and force the linker to use the C++ main just by ordering the arguments to the linker :D For 32-bit under VS2015 x86 Native Tools Command Prompt: dmd -lib -m32mscoff my_d_lib.d cl main.cpp my_d_lib.lib C:\D\dmd2\windows\lib32mscoff\phobos32mscoff.lib "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\legacy_stdio_definitions.lib" And for 64-bit under VS2015 x64 Native Tools Command Prompt: dmd -lib -m64 my_d_lib.d cl main.cpp my_d_lib.lib C:\D\dmd2\windows\lib64\phobos64.lib "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64\legacy_stdio_definitions.lib" Both works for me. Anyway, I still don't think that forcing the users to use this workaround is a great idea. |
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rainer Schuetze | On Wednesday, 14 October 2015 at 12:39:46 UTC, Rainer Schuetze wrote:
>
> As Szymon noticed, you can compile an empty main into the static D library. The additional C main that is generated by DMD won't cause ambiguities as long as your C main is not also in a static library.
Thanks for the advice (I just found that myself). Why this workaround is not needed on Linux?
|
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner |
On 14.10.2015 14:57, Szymon Gatner wrote:
> extern(C++)
> void freeSubtract(Operation animal) {
> auto cat = cast(Subtract) animal; <<====== cast yields null
> if(cat !is null) {
> destroy(cat);
> free(cast(void*) cat);
> }
> }
>
> extern(C++)
> void useOperation(Operation t) {
> auto res = t.execute(1, 2);
> }
>
> Everything works fine except the marked line. Subtract instance seems to
> be created correctly on D side as it returns valid result in the
> execute() call on C++ side but then when trying to free the instance on
> D side again, downcast from Operation to Subtract fails resulting in null.
>
To get compatible class layout, the D compiler has to omit it's class info entry in the vtable of C++ classes. In addition D doesn't know about C++ RTTI (I don't know if this is planned to add), so it cannot do the dynamic cast from Operation to Subtract.
I have not tried, but calling the virtual destructor instead of destroy() might just work?
|
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rainer Schuetze | On Wednesday, 14 October 2015 at 13:12:00 UTC, Rainer Schuetze wrote:
>
>
> On 14.10.2015 14:57, Szymon Gatner wrote:
>> extern(C++)
>> void freeSubtract(Operation animal) {
>> auto cat = cast(Subtract) animal; <<====== cast yields null
>> if(cat !is null) {
>> destroy(cat);
>> free(cast(void*) cat);
>> }
>> }
>>
>> extern(C++)
>> void useOperation(Operation t) {
>> auto res = t.execute(1, 2);
>> }
>>
>> Everything works fine except the marked line. Subtract instance seems to
>> be created correctly on D side as it returns valid result in the
>> execute() call on C++ side but then when trying to free the instance on
>> D side again, downcast from Operation to Subtract fails resulting in null.
>>
>
> To get compatible class layout, the D compiler has to omit it's class info entry in the vtable of C++ classes. In addition D doesn't know about C++ RTTI (I don't know if this is planned to add), so it cannot do the dynamic cast from Operation to Subtract.
>
> I have not tried, but calling the virtual destructor instead of destroy() might just work?
Mint that this code comes from Adam Ruppe's book, and it is suppose to be working code. Also mind that Subtract is D class and instantiated on D side, it is only briefly passed to C++ for usage.
|
October 14, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Szymon Gatner | On 14.10.2015 15:26, Szymon Gatner wrote: >> >> To get compatible class layout, the D compiler has to omit it's class >> info entry in the vtable of C++ classes. In addition D doesn't know >> about C++ RTTI (I don't know if this is planned to add), so it cannot >> do the dynamic cast from Operation to Subtract. >> >> I have not tried, but calling the virtual destructor instead of >> destroy() might just work? > > Mint that this code comes from Adam Ruppe's book, and it is suppose to > be working code. Also mind that Subtract is D class and instantiated on > D side, it is only briefly passed to C++ for usage. Check the disassembly: the compiler is not even trying to do a conversion, it just loads null. There is no usable type information in a C++ interface. If you know the object "animal" is of type Subtract, you can get the object pointer by manually subtracting the pointer difference from the passed pointer: auto ptr = cast(void*)animal; ptr -= cast(void*)cast(Operation)cast(Subtract)ptr - ptr; auto cat = cast(Subtract) ptr; |
October 15, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kagamin | On Tuesday, 13 October 2015 at 16:07:50 UTC, Kagamin wrote:
> What doesn't work for you?
>
> extern int f();
> struct T(F){}
> T!f t; //Error: template instance T!(f) does not match template declaration T(F)
> struct A(alias F){}
> A!f a; //works
I don't remember. I fixed the issues a few weeks ago using various hackarounds. The main area that problems pop up is in metaprogramming where you don't know what you're working with (could be a member function, free function, template function, etc.). It's very hard to deal with optional parens generically, and you have to take special care to ensure that the compiler doesn't think you're calling a function when that's not what you meant to do.
|
October 15, 2015 Re: Beta D 2.069.0-b1 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On 10/9/15 10:37 PM, Adam D. Ruppe wrote:
> On Saturday, 10 October 2015 at 02:31:51 UTC, Martin Nowak wrote:
>> nothing to warrant the invasive language feature @property is.
>
> There's no reason for @property to be invasive. ALL it needs to do is
> handle that one case, it shouldn't even be used anywhere else.
> Everything else is trivial or irrelevant.
>
> And the beauty of this is it won't even break any code.
I honestly couldn't care less about this.
What I don't like is arbitrary setter property abuse. e.g. somerange.find = needle;
My opinion:
Empty parens can always be omitted on functions, but need to be present in order to call delegates/function pointers.
@property function on struct/class method enables the instance.method = value; syntax. Otherwise, it's rejected (you must use instance.method(value); syntax). If I were defining it from scratch, I'd say you HAVE to use first syntax if it's marked @property, but I'm OK with allowing the normal syntax on @property methods.
Since we have @property, slapping it on a getter that returns a delegate/function pointer enables the behavior you want (why not?). &instance.prop I'm unsure what it should mean. I'm leaning towards that it means get a delegate to the property method itself, not the result of calling the property. This makes it behave differently than a field, but it already does this today.
@property on module level functions operate like methods, but allow UFCS calls. So:
0 params -> Obviously, like a getter (see above).
1 params -> Allow func = val syntax. Other syntaxes still allowed. Calling val.func syntax, if returning a delegate, I think should require the double parens, since IMO @property is for the setter syntax, not a description of a UFCS call.
2 params -> Allow firstParam.func = secondParam syntax. Other syntaxes still allowed.
-Steve
|
Copyright © 1999-2021 by the D Language Foundation