Doc No:

<N 1876>

Date:

2014-09-29

Reply to:

Max Abramson <[email protected]>



Access specifiers for structures in C

Summary

This paper proposes the addition of C++ style access specifiers for data members (also known as instance variables) of structures used in the C programming language. Access specifiers public, protected, and private are used in C++, Java, and Objective C in order to encapsulate private variables and avoid name collisions. As programs grow very large, the likelihood of name collisions grows, and these programs can become difficult to maintain. This paper adds features that may be added independently or together with the features proposed in “Adding classes to C” and/or “

For reference, Objective C only allows instance variables to be private or protected, and are protected by default in classes, and C structures are not fully supported under Automatic Reference Counting.

Reusing the example from the first part of this proposal, “Adding classes…”:

typedef struct Ostrich {

public:

initOstrich(){/**/} //no-argument “{/**/}” proposed convention for //unfinished initializer

initOstrich(double speedGuess) { //overloaded initializer taking one double

double* guessPtr = malloc(sizeof(double));

(speedGuess) ? m_speedEst = speedGuess : m_speedEst = SPEED_LOOKUP;

++numRatites; //pre-increment is faster when working with objects

}

deleteOstrich() {

--numRatites; //static int numRatites, previously declared globally

}

deleteOstrich(double speedGuess) {

--numRatites; //static int numRatites, previously declared globally

free speedGuess;

}

private: //Note: only instance variables may be private or protected

int m_weight, m_height, m_strideLength;

void printValues() {/**/} //would cause error; private methods are not allowed

} Ostrich;



Example

For reference, C++ treats structures as classes for backward compatibility, but with the default access specifier changed to public. Again, the paper proposes strict conformance to the C++ standard (WG 21/N3337) in handling structures for C. New C code must operate in exactly the same way as if compiled as C++, or the program must not compile, by default.

typedef struct Runner {

const double SPEED_LOOKUP = 55;

double estRunningSpeed(double hipHeight, double strideLength){...}

protected:

double m_speedEst;

} Runner;



typedef struct Ratites {

private:

int m_numRatites;

public:

Ostrich bob; //this is the simplest way to nest structures

Runner runner; //no constructor/initializer is needed

void setNumRatites(int num) {

m_numRatites = num;

}

int getNumRatites() {

return m_numRatites;

}

} Ratites;



Wording

The proposed wording draws heavily on the wording in WG21/N3337. Note: It is the intent of this proposal that access specifiers and other features conform to the specifications given in therein. Wording additions are included as four new keywords imported from C++, with identical meaning and syntax, and a new section building a strict, conservative subset of the access specifiers defined in N3337.

6.4.1 Keywords

Add public, private, and protected to the list of reserved keywords.

Add a new sections 6.7.11, 6.7.12, and 6.7.13:

6.7.11 Member Access

1 A member of a structure can be

private; that is, its name can be used only by members of the structure in which it is declared.

protected; that is, its name can be used only by members of the structure in which it is declared, by structures derived from that structure.

public; that is, its name can be used anywhere without access restriction.

2 A member of a structure can also access all the names to which the structure itself has access. A local structure of a member function may access the same names that the member function itself may access. Access permissions are thus transitive and cumulative to nested and local structures.

3 Members of a structure defined with the keywords struct or union are public by default.

4 Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. In the case of overloaded function names, access control is applied to the function selected by overload resolution. Because access control applies to names, access control is applied to a typedef name--not the entity referred to by the typedef.

5 It should be noted that it is access to members and base structures that is controlled, not their visibility. Names of members are still visible, and implicit conversions to base structures are still considered, when those members and base structures are inaccessible. The interpretation of a given construct is established without regard to access control. If the interpretation established makes use of inaccessible member names or base structures, the construct is not constructed according to the syntax rules, diagnosable semantic rules, or may have ambiguous meaning. Note: Base structures are not yet a concern for this specification as inheritance is not yet defined, but is included here for future reference.

6 All access controls in this section affect the ability to access a struct member name from the declaration of a particular entity, including parts of the declaration preceding the name of the entity being declared and, if the entity is a structure, the definitions of members of the struct appearing outside the structure’s member specification. Note: this access also applies to implicit references to constructors, conversion functions, and destructors.

7 Here, all the uses of A::I are constructed according to the syntax rules, diagnosable semantic rules, and applies one definition because A::f, A::x, and A::Q are members of struct A. This implies, for example, that access checking on the first use of A::I must be deferred until it is determined that this use of A::I is as the return type of a member of struct A. Similarly, the use of A::B as a base-specifier is constructed according to the syntax rules, diagnosable semantic rules, and applies one definition because D is derived from A, so checking of base-specifiers must be deferred until the entire base-specifier-list has been seen.

8 The names in a default argument (8.3.6) are bound at the point of declaration, and access is checked at that point rather than at any points of use of the default argument.

6.7.12 Access specifiers

1 Member declarations can be labeled by an access-specifier:

access specifier : member-specificationopt

An access specifier specifies the access rules for members following it until the end of the structure or until another access specifier is encountered:

typedef struct American {

int boy; // American::boy is public

int girl; // American::girl is public

private:

float teen; //American::teen is private

protected:

float neighbor; //American::neighbor is protected

public:

char ism, s; //American::ism and American::s are public

} American;

2 Any number of access specifiers is allowed and no particular order is required.

3 Non-static data members of a (non-union) structure with the same access control are allocated so that later members have higher addresses within a structure object. The order of allocation of non-static data members with different access control is unspecified. Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other.

4 When a member is redeclared within its structure definition, the access specified at its redeclaration shall be the same as at its initial declaration.

struct Ore {

struct Iron;

enum Wood : int;

private:

struct Iron { }; //error: cannot change access

enum Wood: int{e0}; //error: cannot change access

};

5 Note: In a derived structure, the lookup of a base structure name will find the injected-struct-name instead of the name of the base structure in the scope in which it was declared. The injected-struct-name might be less accessible than the name of the base struct in the scope in which it was declared.



6.7.13 Nested structures

1 A nested structure is a member and as such has the same access rights as any other member. The members of an enclosing structure have no special access to members of a nested structure; the usual access rules for private, protected, and public shall be obeyed.

typedef struct Eagle {

int x; //public, by default

struct Bird { };

struct Golden { };

private:

Bird b; //OK: Eagle::Golden can access Eagle::Bird

int y;

};



int g(Golden* ptrG) {

return ptrG->y; //error: Golden::y is private

}

}Eagle;