2

What I want is for some class members to be sometimes private and others times public. These members are supposed to be accessible by some modules and inaccessible by others.

Imaging this class:

class Foo {
public:
    ...
private:
    ...
protected:
    ...
internal:
    int x;
};

In module X the internal is defined as:

#define internal public

and in module Y it's defined as:

#define internal private

So the real question is if this trick is acceptable by the standard or if it will change the signature of the class (or its members) in any way.

I know that friend and PIMPL are for this kind of job but friend can get extremely messy and PIMPL's performance (an indirection and the fact that you can't inline) are not acceptable for the codebase I'm working on.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 9
    Sounds like an ODR violation and therefore UB. Probably the UB does what you want though. – nwp Aug 31 '17 at 12:20
  • 2
    The visibility of members are not passed on past the compiler into the generated code, and so has no effect on the ABI, or is affected by the ABI. – Some programmer dude Aug 31 '17 at 12:23
  • 2
    As for the `internal` macro, what is the purpose of it? What problem is it supposed to solve? Do you have a use-case or requirement or a design that leads to this? This has the smell of an [XY problem](http://xyproblem.info/). Perhaps it's time to look over your requirements or design? And will the "modules" be linked together to form parts of the same program? – Some programmer dude Aug 31 '17 at 12:25
  • 1
    [\[class.mem\]/18](http://eel.is/c++draft/class.mem#18) – cpplearner Aug 31 '17 at 12:28
  • Using modules is already unspecified, no standard is going to give you a blessing. Do what works. – Hans Passant Aug 31 '17 at 12:34
  • Regarding breaking ODR, it does break it (see [basic.def.odr/6](http://eel.is/c++draft/basic.def.odr#6), especially [basic.def.odr/6.1](http://eel.is/c++draft/basic.def.odr#6.1)). – Some programmer dude Aug 31 '17 at 12:44
  • 2
    @Someprogrammerdude : unless the compiler decides to reorder members depending on their accessability ... – Martin Bonner supports Monica Aug 31 '17 at 12:47

2 Answers2

4

It is an ODR violation and hence invokes undefined behaviour. (See also basic.def.odr]/6.1 "each definition of D shall consist of the same sequence of tokens").

However, a common implementation is that public, private, protected have no impact on class layout so it will work.

You are skating on thin ice; there is nothing to stop a compiler putting all the public members first, then the protected ones, then the private ones. More to the point, in general, the order of declaration is required to be the order in memory so

struct T {char a; int b; char c};

is required to have a, then b, then c. This is to ensure C compatability. However, there is no requirement on the ordering of elements with different access (See [class.mem]/9.2 p13: "Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (Clause 11)". So given

struct T {char a; int b; private: char c};

the compiler can reorder the members and put c in the gap between a and b.

Final note to EJP and others who think these are declarations not defintions: I have given two definitions of T above; a declaration would look like struct T;.

Edit: Thanks to Fanael for the cite from the standard.

  • 2
    This is correct, see also [basic.def.odr]/6.1 "each definition of D shall consist of the same sequence of tokens" which is obviously violated if `internal` is `public` in one TU and `private` in another. –  Aug 31 '17 at 12:42
  • I thought C++ explicitly forbade compilers from reordering members. – nwp Aug 31 '17 at 12:50
  • 1
    @nwp: From the standard "Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (Clause 11)" – Martin Bonner supports Monica Aug 31 '17 at 12:53
0

The C++ originally seemed to consider that private members could be placed somewhere other than next to public members, perhaps so they could be a protected region in hardware, so it is conceivable that the public and private sections could be moved relative to each other.

It is possible to test your code without redefining public / private using tricks by Herb Stutter GOTW 76 and completed to a fully functional system with this data here litb safer private member access

Given a class as follows....

struct A {
  A(int a):a(a) { }
private:
  int a;
};

A class robber is required...

template<typename Tag, typename Tag::type M>
struct Robber { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

A utility class allowing multiple steals....

template<typename Tag, typename Member>
struct TagBase {
  typedef Member type;
  friend type get(Tag);
};

Declaring a theft intent becomes

struct A_f : TagBase<A_f, int A::*> { };
template struct Robber<A_f, &A::a>;

Then stealing the data....

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
mksteve
  • 12,614
  • 3
  • 28
  • 50