How private/protected/public affect the ABI? - c++

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.

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.

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;
}

Related

Why does c++ have this rule:Explicit instantiation definitions ignore member access specifiers: parameter types and return types may be private

According to this rule, C++ can directly destroy encapsulation under safe syntax
c++11
#include <iostream>
class A {
public:
A() = default;
private:
int data_ = 0;
};
template < int A::*Member >
class Access {
public:
friend int GetPrivateData(A& obj) {
return obj.*Member;
}
};
template class Access<&A::data_>; // explicit instantiation
int GetPrivateData(A& );
int main() {
A obj;
GetPrivateData(obj);
return 0;
}
//https://www.zhihu.com/question/521898260/answer/2876618819
c++17
class A {
public:
A(int num) : data_(num) {};
private:
int data_ = 0;
};
template <typename PtrType>
class Access {
public:
inline static PtrType ptr;
};
template <auto T>
struct PtrTaker {
struct Transferer {
Transferer() {
Access<decltype(T)>::ptr = T;
}
};
inline static Transferer tr;
};
template class PtrTaker<&A::data_>; // explicit instantiation
int main() {
A a{10};
int b = a.*Access<int A::*>::ptr;
return 0;
}
//https://www.zhihu.com/question/521898260/answer/2876618819
The two implementations have slightly different principles. The former can be regarded as exploiting linking vulnerabilities, but the latter is indeed a completely standard implementation.
And this syntax cannot be checked by a simple keyword search, because there will be a lot of normal explicit instantiation to confuse.
The behavior you are referring to is specified in [temp.spec.general]/6
This rule has been there since C++98, so since the first standardization of C++. It is not somehow new with C++17.
The rule is also necessary. Suppose you wanted to use Access<&A::data_> inside A, where A::data_ is accessible, but you then don't want Access<&A::data_> to be implicitly instantiated, only explicitly instantiated in a single translation unit. Because the explicit instantiation declaration cannot appear inside A's scope, there can't possibly be any check on accessibility. It would make such an explicit instantiation completely impossible.
The same applies to explicit and partial specializations as well.
That you can use additional techniques such as friend injection to then access the private member is not a problem, since these are not things that can happen accidentally. If the user is determined to access a private member, then there isn't anything stopping them anyway. (They could for example simply add their own class/function as a friend in the class definition, which may or may not be an ODR violation in itself, but will have no negative consequences as such in practice.)
The purpose of access control is only to make it so that the user won't accidentally use a member that they shouldn't use directly and merely instantiating with a private member as argument doesn't allow accessing the private member. That requires additional targeted work. And if a user does all that work to get to the private member, then the user is intentionally breaking the intended use of the class and it will be on them if the result is a broken program.

Do public/private/protected change the arrangement of a struct in memory?

Edit: I just realized a much simpler way to ask this question:
Given the following two structs:
class Thing {public: int a; public: int b; public: int c;}
class Thing {public: int a, private: int b; public: int c;}
Are the members a, b, and c guaranteed to be in the same order in memory for both of these definitions?
Old Question
Let's say we have this C++ code in fileA.cpp:
class Thing
{
public:
int a;
double num;
Thing()
{
b = 10;
}
float getB()
{
return b;
}
private:
float b;
Thing * other;
}
void doSomething(Thing thing);
int main()
{
Thing thing;
doSomething(thing);
std::cout << thing.b;
}
And let's say we have this code in fileB.cpp:
class Thing
{
public:
int a;
double num;
Thing()
{
b = 10;
}
float getB()
{
return b;
}
float b;
private:
Thing * other;
}
void doSomething(Thing thing)
{
thing.b = 30;
}
Assuming the compiler wouldn't complain, would this code work as expected? That being, is the arrangement of a struct's data independent of whether or not certain components are public, private, or protected?
Edit: To make it more obvious, the only difference between the two definitions of Thing is the fact that float b; is private in fileA.cpp but public in fileB.cpp.
The standard makes no such guarantee. You have layout guarantees only for standard-layout classes:
A standard-layout class is a class that:
has no non-static data members of type non-standard-layout class (or array of such types) or reference,
has no virtual functions (10.3) and no virtual base classes (10.1),
has the same access control (Clause 11) for all non-static data members,
has no non-standard-layout base classes,
either has no non-static data members in the most derived class and at most one base class with
non-static data members, or has no base classes with non-static data members, and
has no base classes of the same type as the first non-static data member.
(C++14, [class] ¶7)
If a class is standard-layout, its layout is well defined (and two standard layout classes that have a layout-compatible initial sequence may read each other's layout-compatible members through a union).
However, here it is not the case, as you have different access specifiers throughout the class. In particular, it's explicitly stated that
The order of allocation of non-static data members with different access control is unspecified
(C++14, [class.mem] ¶13)
That being said, I never worked with any real world compiler that ever exploited this flexibility offered by the standard - every compiler I know uses the access specifiers for compile-time checks, but ignores them completely as far as members layout is concerned.

Access rights of a nested class of a nested class

In C++ a nested class has access rights to all members of the enclosing class. Does this also apply to a nested class of a nested class?
This code
#include <iostream>
class A
{
public:
class B
{
public:
B() { std::cout << A::x << std::endl; }
class C
{
public:
C() { std::cout << A::x << std::endl; }
};
};
private:
static const int x { 0 };
};
int main()
{
A::B b;
A::B::C c;
}
compiles without warning on g++ 7.2. However, it is unclear to me that this conforms to the standard. The standard draft (N4727 14.7) says:
A nested class is a member and as such has the same access rights as any other member.
However, in the example above C is not a member of A, it is a member of a member. Is the standard ambiguous here? Is g++ behavior portable?
However, in the example above C is not a member of A, it is a member of a member.
Yes this is well-defined behavior; the access right is transfered from B.
According to the standard [class.access]/2,
A member of a class can also access all the names to which the class has access.
And [class.mem]/1,
Members of a class are data members, member functions, nested types, enumerators, and member templates and specializations thereof.
C is a nested class of B, it's also the member of B, then C can access names what B could access to, including A::x. For the same reason, C::C is the member of C, it could access names what C could access to, so accessing A::x in C::C is fine.
The behavior is well-defined and in-line with the standard wording. What you are missing is the relevant wording of [class.access]p2, which strengthens that which you have already quoted:
A member of a class can also access all the names to which the class has
access. A local class of a member function may access the same names that the
member function itself may access.
This means that the accessibility is transitive. If C has access to the same entities as B, it also means that C has access to the entities in A, as B has access to them.
class A {
class B {
class C {
C() { A::x; /* well-defined */ }
};
};
static int x;
};

C++ Outer class access Inner class's private - why forbidden

Hello I am wondering why C++ standard allows us in nested classes to access outer class's private fields, while it forbids to access inner class's private fields from the outer class. I understand, that this example:
class OuterClass{
public:
class InnerClass{
public:
void printOuterClass(OuterClass& outer) {cout << outer.m_dataToDisplay;};
};
private:
int m_dataToDisplay;
};
is fine, because thing, that Inner class sometimes can be complicated. But I think following scenario is also fine:
class Algorithm{
public:
class AlgorithmResults{
public:
void readAlgorithmResult();
private:
void writeAlgorithmResult();
};
void calculate(AlgorithmResults& results, Arguments...){
//calculate stuff
results.writeAlgorithmResult(results);
}
};
For me this structure makes perfect sense, although it is not allowed in C++. I also noticed, that for some time both were allowed in Java, but now second example is also forbidden.
What is the reason, that first example is allowed and another is denied?
Essentially, within a scope names declared earlier in that scope are valid and can be used directly (unless they're shadowed). Code outside a scope can't directly use names declared inside the scope. E.g. code after a curly braces block, can't directly use variables declared inside that block (an example of indirect use is when the outside code has access to a pointer to a static variable inside the curly braces block).
For the second example, just make Algorithm a friend of AlgorithmResults:
class AlgorithmResults
{
friend class Algorithm;
The nested classes could access outer class's private fields, because it's a member of the outer class, just same as the other members.
[class.access.nest]/1
A nested class is a member and as such has the same access rights as any other member.
On the other hand, the outer class doesn't have special access rights on the nested class, they're just normal relationship.
The members of an enclosing class have no special access to members of a nested class; the usual access rules ([class.access]) shall be obeyed. [ Example:
class E {
int x;
class B { };
class I {
B b; // OK: E​::​I can access E​::​B
int y;
void f(E* p, int i) {
p->x = i; // OK: E​::​I can access E​::​x
}
};
int g(I* p) {
return p->y; // error: I​::​y is private
}
};
— end example ]
Counter question: Why would you want to allow it?
If you need an outer class have access to an inner class' private internals, you can befriend:
class Foo {
public:
class Frob {
friend class Foo;
int privateDataMember;
};
Foo () {
Frob frob;
frob.privateDataMember = 3735928559;
}
};
C++ has no device to unfriend, so allowing default private access to an outer class would steal you a class design tool and yield reduced default encapsulation.

Standard layout and non-copyable property

C++11, §9/7:
A standard-layout class is a class that:
has no non-static data members of type non-standard-layout class (or array of such types) or reference,
has no virtual functions and no virtual base classes,
has the same access control for all non-static data members,
has no non-standard-layout base classes,
either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
has no base classes of the same type as the first non-static data member.
So, is there a way to make a class with standard layout non-copyable? If yes, how?
Inheriting privately from boost::noncopyable will not work, because it made copy constructor private (hence not a standard layout). The boost::noncopyable's implementation is like this :
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
};
Because of the private section, it is not a standard layout class. I am also note sure if private inheritance break any standard layout rule.
#include <boost/noncopyable.hpp>
#include <iostream>
const int N = 50;
struct A
{
int data[N];
};
struct B : private boost::noncopyable
{
int data[N];
};
struct C
{
A data[10];
};
struct D : private boost::noncopyable
{
B data[10];
};
int main() {
std::cout<<sizeof(A)<<std::endl;
std::cout<<sizeof(B)<<std::endl;
std::cout<<sizeof(C)<<std::endl;
std::cout<<sizeof(D)<<std::endl;
}
The output is :
200
200
2000
2004
The example above shows that inheriting privately from boost::noncopyable changes the class into NOT standard layout compliant.
I am not sure if this is a g++ bug (I am using g++ 4.6.1), or the standard is somehow violated.
I think there is a confusion here:
the standard layout property is affected by attributes (and only attributes)
the copyable property is affected by methods (their presence, absence and accessibility)
The two concepts are orthogonal.
UPDATE:
The following display the very same behavior that boost::noncopyable:
#include <iostream>
struct foo {};
struct B : foo { int data; };
struct D : foo { B data; };
int main() {
D d;
std::cout << (char*)(&d.data) - (char*)(&d) << "\n";
}
The result is 4.
I believe this is because of:
has no base classes of the same type as the first non-static data member.
Indeed, experiment shows that introducing a int a; in D prior to data does not increases its size. I think that the fact that B inherits from foo means that data (first non-static data member) is considered to be the same type as foo (base class of D).
This leads to an ambiguity: foo* f = &d would have the same address as foo* g = &b.data; if the compiler did not introduce this padding.
There are two things you need to do to make your class non copyable:
Make the copy constructor private.
Make the assignment operator private. (Where it gets assigned another type of the same kinds as the class itself).
You don't need to inherit from some boost class just to get that behavior.
And may I add, who cares about some fancy 'standard layout' idea. Program what you need and don't succumb to this extreme space theory.