Difference between revisions of "Access specifiers and visibility"
(→Complications) |
|||
Line 162: | Line 162: | ||
</pre> | </pre> | ||
While symbol somewhat leaked into external linking, it has special mangling and, I suppose, impossible to use without some assembler-like forging. Note that it is impossible to share Test class definition via header as Hidden symbol won't resolve within any other translation unit. | While symbol somewhat leaked into external linking, it has special mangling and, I suppose, impossible to use without some assembler-like forging. Note that it is impossible to share Test class definition via header as Hidden symbol won't resolve within any other translation unit. | ||
+ | |||
+ | == Requirements for DIP == | ||
+ | <pre> | ||
+ | Everyone here has raised some good points. But this isn't a simple issue, so I | ||
+ | suggest getting together and preparing a DIP. A DIP should address: | ||
+ | |||
+ | 1. what access means at module scope | ||
+ | 2. at class scope | ||
+ | 3. at template mixin scope | ||
+ | 4. backwards compatibility | ||
+ | 5. overloading at each scope level and the interactions with access | ||
+ | 6. I'd also throw in getting rid of the "protected" access attribute completely, | ||
+ | as I've seen debate over that being a useless idea | ||
+ | 7. there's also some debate about what "package" should mean | ||
+ | |||
+ | I.e. it should be a fairly comprehensive design addressing access, not just one | ||
+ | aspect of it. | ||
+ | </pre> | ||
+ | (c) Walter |
Revision as of 12:37, 28 January 2013
Contents
Description
Current state of affairs in D
Access specifier (protection attribute) meaning
Mostly copied from http://dlang.org/attribute.html#ProtectionAttribute
private
Private means that only members of the enclosing class can access the member, or members and functions in the same module as the enclosing class. Private members cannot be overridden.
dlang.org states that "Private module members are equivalent to static declarations in C programs." but this is wrong, they have external linkage:
module sample;
private void func1(int) { }
void func2(int) { }
$ dmd -c sample.d $ nm -a sample.o | grep func 00000000 t .text._D6sample5func1FiZv 00000000 t .text._D6sample5func2FiZv 00000000 T _D6sample5func1FiZv 00000000 T _D6sample5func2FiZv
package
Package extends private so that package members can be accessed from code in other modules that are in the same package. This applies to the innermost package only, if a module is in nested packages.
protected
Protected means that only members of the enclosing class or any classes derived from that class, or members and functions in the same module as the enclosing class, can access the member. If accessing a protected instance member through a derived class member function, that member can only be accessed for the object instance which can be implicitly cast to the same type as ‘this’. Protected module members are illegal.
public
Public means that any code within the executable can access the member.
extern
Export means that any code outside the executable can access the member. Export is analogous to exporting definitions from a DLL.
Global static
Global static storage class currently is a no-op storage class in D, global symbols with one are compiled but ignored.
Name lookup
http://dlang.org/attribute.html#ProtectionAttribute : Protection does not participate in name lookup. In particular, if two symbols with the same name are in scope, and that name is used unqualified then the lookup will be ambiguous, even if one of the symbols is inaccessible due to protection. For example:
module A;
private class Foo {}
module B;
public class Foo {}
import A;
import B;
Foo f1; // error, could be either A.Foo or B.Foo
B.Foo f2; // ok
One other obvious consequence of this - in case private symbol is only possible lookup, error message will show to it, instead of issuing unknown symbol.
What is missing
- There is currently no way in D to mark symbols for internal linkage, saying "this an implementation detail, you should not even know this one exists". This is an important module-level encapsulation tool which also somewhat guarantees that those symbols can't be linked to by accident by some other module and you are free to change them keeping binary interface same.
- Name clash between public and private symbols has also been stated as unneeded and useless feature that makes possible to break a compilation of a project by changing private name.
Complications
- Compile-time reflection, i.e. serialization libraries or @attribute scanners. Limiting access for __traits may forbid certain currently working idioms.
- Symbol leakage via aliases or function arguments.
private struct _Hidden { }
alias const(_Hidden) UseMe;
void func(_Hidden input) { }
Currently it is all valid as _Hidden symbol is perfectly accessible, just prohibited from direct usage. If some true internal linkage storage class / specifier is introduced (private or not) this case needs to be defined in smallest details. See Access_specifiers_and_visibility#Module-scope_analogies for how it is handled in C++.
Current state of affairs in C++
Access specifier (protection attribute) meaning
private
private as an access specifier is defined only for classes/structs. It does not hide symbol, but prevents usage:
class Test
{
private:
int a;
};
int main()
{
Test t; t.a = 42;
return 0;
}
test.cpp:9:15: error: 'a' is a private member of 'Test' Test t; t.a = 42; ^ test.cpp:4:13: note: declared private here int a; ^
protected
Similar to private, but allows access for descendants.
public
Default one, "if you can see symbol - you can access it"
Module-scope analogies
C++ does not have a modules in D sense. It is based on translation units (*.cpp) and headers are just copy-pasted upon include, so module-level access specifiers make no sense to C++. However, there is an "unnamed namespace" feature, that forces internal linkage for a symbol:
// sample.cpp
void func1(int) { }
namespace
{
void func2(int) { }
}
$ clang++ -c sample.cpp $ nm -a ./sample.o | grep func 00000000 T _Z5func1i
One of interesting cases - what happens when hidden symbol becomes the part of public interface? Consider this sample:
// sample.cpp
namespace
{
class Hidden
{
};
}
class Test
{
public:
void func(Hidden) { }
};
// if func() referencing Test will be skipped, then
// no traces of Test::func will be found in object file.
// Can't find part of standard that explains it.
void func(Test t)
{
t.func(Hidden());
}
$ clang++ -c sample.cpp $ nm -a sample.o | grep Hidden 00000020 t _ZN4Test4funcEN12_GLOBAL__N_16HiddenE
While symbol somewhat leaked into external linking, it has special mangling and, I suppose, impossible to use without some assembler-like forging. Note that it is impossible to share Test class definition via header as Hidden symbol won't resolve within any other translation unit.
Requirements for DIP
Everyone here has raised some good points. But this isn't a simple issue, so I suggest getting together and preparing a DIP. A DIP should address: 1. what access means at module scope 2. at class scope 3. at template mixin scope 4. backwards compatibility 5. overloading at each scope level and the interactions with access 6. I'd also throw in getting rid of the "protected" access attribute completely, as I've seen debate over that being a useless idea 7. there's also some debate about what "package" should mean I.e. it should be a fairly comprehensive design addressing access, not just one aspect of it.
(c) Walter