Access specifiers and visibility

From D Wiki
Revision as of 11:48, 28 January 2013 by Dicebot (talk | contribs) (Created page with "== Description == == Current state of affairs in D == === Access specifier (protection attribute) meaning === Mostly copied from http://dlang.org/attribute.html#ProtectionAt...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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.

Name lookup

ttp://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:

What is missing

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.

Complications

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.