On 20.01.2015 13:23, Benjamin Thaut wrote:
I'm currently working on Windows DLL support which has stronger rules
than linux shared objects for which symbols actually get exported from a
shared library. But as we want to replicate the same behaviour on linux
using symbol visibility (e.g. gcc 4 -fVisibility=hidden) this issue also
applies to linux once implemented.

Currently export means two things:
- the symbol is publicy accessible (same as public)
- the symbol will be accisble across shared library boundaries


This has the following issue:

export void templateFunc(T)()
{
   someHelperFunc();
}

private void someHelperFunc()
{

}

And you don't even have to go into phobos to hit this problem. It is
already in druntime see core.time.FracSec._enforceValid

This works with the current linux shared objects because they simply
export all symbols. But once only symbols with export get exported this
breaks.

I would not mind if we export all symbols on Windows aswell. It doesn't seem to bother a lot of people for the linux version, even though it's unsafer and slower than on Windows (at least that was my experience more than 10 years ago). It might get us a first working version without adding "export" throughout the druntime/phobos source code.


The problem here is that you don't want to make someHelperFunc() export
because that would mean users could call it directly, but you want it to
be available for cross shared library calls. The cross shared library
call happens if a template is instanced from a different shared library
/ executable than the module it was originally located in.

There are two solutions for this.

1) Given that export is transitive (that means if a struct or class is
declared export every member that is _not_ private will be accessible
across shared library boundaries. This behaviour is required to make the
export protection level work on windows)

You can now do the following:

export struct SomeStruct
{
   static public void templateFunc(T)()
   {
     someHelperFunc();
   }

   static package void someHelperFunc()
   {

   }
}

Because of the transitivity someHelperFunc will be exported but still
not be callable by the user directly. This can be used to fix the issue
in core.time but may not be so well suited if you want the template to
be on module level instead of nesting it into a struct.

2) Make export an attribute. If export is no longer an protection level
but instead an attribute this issue can easily be solved by doing.

export public void templateFunc(T)()
{
   someHelperFunc();
}

export private void someHelperFunc()
{

}

But this would require grammar changes. Which are generally avoided if
possible.

I don't have a clear favorite, but the second version makes it clearer that visibility and protection are separate issues.

A note on:
> export public void templateFunc(T)()

I don't think it is well defined what exporting a template is supposed to mean. My guess: whenever an instance of the template is created, its symbols are exported. This could make for a lot of duplicate symbols across multiple DLLs, though. Maybe there should be a method of explicitly exporting/importing a template instance from another DLL, e.g.

export alias symbol = templateFunc!int;

Please note, that the problem raised above by Benjamin applies just as well to non-exported templates.

Reply via email to