December 15, 2021
On Wednesday, 15 December 2021 at 12:02:08 UTC, Jan wrote:
> On Wednesday, 15 December 2021 at 11:03:27 UTC, rikki cattermole wrote:
>>
>> On 15/12/2021 11:54 PM, Jan wrote:
>>> On Wednesday, 15 December 2021 at 09:36:54 UTC, Jan wrote:
>>>> Unfortunately it's the "annoying little details" that I immediately bumped into.
>>> 
>>> Just another example: I just learned that linking against C++ DLLs is quite limited. I ran into the issue that linking in an external variable doesn't work (even though the mangled name that D chooses is correct), because DLLs work differently than static linking does
>>
>> Are you sure that on the shared library side it was marked as exported?
>>
>> If a symbol is not exported, there is no guarantee (nor reason to think) that it will be visible during runtime linking to said shared library/executable.
>>
>> This isn't unique to D, its just how linkers work.
>
> Yep, checked that. Even checked the .exp file and compared the mangled name there with the mangled name that D tries to use, they are the same.

You probably know this but just in case - unlike C++ in D variables by default have thread local storage, to link with C++ global variable you need to use __gshared storage modifier in D, it is similar to 'shared' variable that unlike 'shared' tells the compiler "I know how to synchronize it myself".

```d
module a;

struct Foo {}

extern(C++)
__gshared Foo globalFoo;
```

December 15, 2021
On Wednesday, 15 December 2021 at 10:54:45 UTC, Jan wrote:
> Someone with more in-depth knowledge told me, that Windows support in D and specifically DLL support is lacking quite a bit.

gdc and ldc have the same full support you'd expect coming from microsoft c++.

dmd doesn't though. You can make full dlls with dmd just it is more diy than automatic.

*Using* dlls of course is something that's been fully supported since day one. Either using an import lib or GetProcAddress.



December 15, 2021

On Wednesday, 15 December 2021 at 12:36:49 UTC, evilrat wrote:

>

You probably know this but just in case - unlike C++ in D variables by default have thread local storage, to link with C++ global variable you need to use __gshared storage modifier in D, it is similar to 'shared' variable that unlike 'shared' tells the compiler "I know how to synchronize it myself".

module a;

struct Foo {}

extern(C++)
__gshared Foo globalFoo;

Yeah, did that. As I said the mangled name that DMD chose is correct.

As I was told linking against functions in DLLs works perfectly, because it is identical to static linking. Linking against global/static variables supposedly differs and that's not handled correctly for DLLs. As I said, I talked to someone who worked on fixing lots of those issues a while ago and he immediately knew about this limitation.

December 15, 2021

On Wednesday, 15 December 2021 at 15:20:18 UTC, Jan wrote:

>

As I was told linking against functions in DLLs works perfectly, because it is identical to static linking. Linking against global/static variables supposedly differs and that's not handled correctly for DLLs. As I said, I talked to someone who worked on fixing lots of those issues a while ago and he immediately knew about this limitation.

I have used global variables from DLLs successfully. DMD now also has a test for C++ DLLs: https://github.com/dlang/dmd/blob/master/test/dshell/extra-files/dll_cxx/testdll.d

The variable should be declared like this in C++:
__declspec(dllexport) int value;

It can then be accessed from D with this:
extern (C++) export extern __gshared int value;

Do you have a test case for your problem?

December 15, 2021

On Wednesday, 15 December 2021 at 17:10:51 UTC, Tim wrote:

>

Do you have a test case for your problem?

C++

class Test
{
public:
  __declspec(dllexport) static int var;
};

int Test::var = 42;

D

extern(C++, class) struct Test
{
  extern (C++) export extern __gshared int var;
}

(also tried without the duplicate extern(C++) and without 'export')

DLL compiled with Visual Studio 2019. D DLL compiled with a 3 day old nightly build of DMD.

DMD says:
error LNK2019: unresolved external symbol "public: static int Test::var" (?var@Test@@2HA)

I've checked the .exp file of the DLL where the symbol is defined in, and it has the exact same name there. And yes, I correctly link against that DLL in general, other symbols located in that DLL are linked just fine by DMD.

December 15, 2021
On Wednesday, 15 December 2021 at 19:28:44 UTC, Jan wrote:
> C++
> ```cpp
> class Test
> {
> public:
>   __declspec(dllexport) static int var;
> };
>
> int Test::var = 42;
> ```
>
> D
> ```cpp
> extern(C++, class) struct Test
> {
>   extern (C++) export extern __gshared int var;
> }
> ```
>
> (also tried without the duplicate extern(C++) and without 'export')
>
> DLL compiled with Visual Studio 2019. D DLL compiled with a 3 day old nightly build of DMD.
>
> DMD says:
> `error LNK2019: unresolved external symbol "public: static int Test::var" (?var@Test@@2HA)`
>
> I've checked the .exp file of the DLL where the symbol is defined in, and it has the exact same name there. And yes, I correctly link against that DLL in general, other symbols located in that DLL are linked just fine by DMD.

I can reproduce your problem. It seems to work if var is additionally static:

extern(C++, class) struct Test
{
   extern (C++) export extern static __gshared int var;
}

It's probably a bug, that it does not work without static. The documentation says "__gshared may also be applied to member variables and local variables. In these cases, __gshared is equivalent to static, except that the variable is shared by all threads rather than being thread local." (https://dlang.org/spec/attribute.html#gshared)

December 15, 2021

On Wednesday, 15 December 2021 at 21:30:47 UTC, Tim wrote:

>

It seems to work if var is additionally static:

Ha, it works indeed!

extern export __gshared static int var;

That's a declaration worthy of C++ ! Fewer keywords would be too usable :D

Joking aside, I understood the docs such that __gshared actually is static in D, no? Also why the export when it's an extern variable?

December 15, 2021
On Wed, Dec 15, 2021 at 10:01:19PM +0000, Jan via Digitalmars-d-learn wrote:
> On Wednesday, 15 December 2021 at 21:30:47 UTC, Tim wrote:
[...]
> ```cpp
> extern export __gshared static int var;
> ```
[...]
> Joking aside, I understood the docs such that `__gshared` actually *is* `static` in D, no?

No. `static` in this case means the lifetime is the lifetime of the module. By default, a static variable would be in TLS and would have the lifetime of the thread that it was instantiated in (and there would be one instance per thread).

`__gshared` is needed to coax the compiler into making the variable global in the C/C++ sense, i.e., only 1 instance across all threads.

tl;dr: C static == D __gshared.


T

-- 
Marketing: the art of convincing people to pay for what they didn't need before which you fail to deliver after.
December 15, 2021
On Wednesday, 15 December 2021 at 22:24:42 UTC, H. S. Teoh wrote:
> `__gshared` is needed to coax the compiler into making the variable global in the C/C++ sense, i.e., only 1 instance across all threads.


it is just normally __gshared implies static automatically. it does in like every other context but i guess not in the mangler or whatever.
December 15, 2021

On Wednesday, 15 December 2021 at 22:01:19 UTC, Jan wrote:

>

Ha, it works indeed!

extern export __gshared static int var;

That's a declaration worthy of C++ ! Fewer keywords would be too usable :D

Joking aside, I understood the docs such that __gshared actually is static in D, no? Also why the export when it's an extern variable?

I agree that __gshared should imply static. That's probably a bug in the compiler.

Using extern without export would only work with static linking (on Windows). export without extern would be exporting the variable for others, like __declspec(dllexport) in C++. export and extern combined imports the variable, like __declspec(dllimport).