views:

269

answers:

4

Why does C++ have public members that anyone can call and friend declarations that expose all private members to given foreign classes or methods but offer no syntax to expose particular members to given callers?

I want to express interfaces with some routines to be invoked only by known callers without having to give those callers complete access to all privates, which feels like a reasonable thing to want. The best I could come up with myself (below) and suggestions by others so far revolve around idioms/pattern of varying indirectness, where I really just want a way to have single, simple class definitions that explicitly indicate what callers (more granularly than me, my children, or absolutely anybody) can access which members. What is the best way to express the concept below?

// Can I grant Y::usesX(...) selective X::restricted(...) access more cleanly?
void Y::usesX(int n, X *x, int m) {
  X::AttorneyY::restricted(*x, n);
}

struct X {
  class AttorneyY;          // Proxies restricted state to part or all of Y.
private:
  void restricted(int);     // Something preferably selectively available.
  friend class AttorneyY;   // Give trusted member class private access.
  int personal_;            // Truly private state ...
};

// Single abstract permission.  Can add more friends or forwards.
class X::AttorneyY {
  friend void Y::usesX(int, X *, int);
  inline static void restricted(X &x, int n) { x.restricted(n); }
};

I'm nowhere near being a software organization guru, but it feels like interface simplicity and the principle of least privilege are directly at odds in this aspect of the language. A clearer example for my desire might be a Person class with declared methods like takePill(Medicine *) tellTheTruth() and forfeitDollars(unsigned int) that only Physician, Judge, or TaxMan instances/member methods, respectively, should even consider invoking. Needing one-time proxy or interface classes for each major interface aspect sits ill with me, but please speak up if you know I'm missing something.

Answer accepted from Drew Hall: Dr Dobbs - Friendship and the Attorney-Client Idiom

The code above originally called the wrapper class 'Proxy' instead of 'Attorney' and used pointers instead of references but was otherwise equivalent to what Drew found, which I then deemed the best generally known solution. (Not to pat myself on the back too hard...) I also changed the signature of 'restricted' to demonstrate parameter forwarding. The overall cost of this idiom is one class and one friend declaration per permission set, one friend declaration per set approved caller, and one forwarding wrapper per exposed method per permission set. Most of the better discussion below revolves around the forwarding call boilerplate that a very similar 'Key' idiom avoids at the expense of less direct protection.

A: 

Something akin to the code below will allow you fine-grained control over which parts of your private state you make public through the friend keyword.

class X {
  class SomewhatPrivate {
    friend class YProxy1;

    void restricted();
  };

public:
  ...

  SomewhatPrivate &get_somewhat_private_parts() {
    return priv_;
  }

private:
  int n_;
  SomewhatPrivate priv_;
};

BUT:

  1. I don't think it's worth the effort.
  2. The need to use the friend keyword might suggest your design is flawed, perhaps there's a way you can do what you need without it. I try to avoid it but if it makes the code more readable, maintainable or reduces the need for boilerplate code I use it.

EDIT: To me the code above is (usually) an abomination that should (usually) not be used.

Staffan
The use of friendship does not imply that a design is flawed. An inappropriate use of friendship does, but from the context of this question we cannot infer that.People fear friendship because, as they claim, it violates encapsulation. Instead, when used properly, it actually benefits encapsulation. [Ref Stroustrup]
Shirik
Shirik: Hence "might suggest"
Staffan
The statement "might suggest" is entirely based on the predicate "the use of friendship exists". This correlation should not exist.
Shirik
What I'm after is not really about data hiding but about granting high-level routine access to trusted interfaces. There is absolutely no _need_ to use friend here, but I would strongly prefer to be able to control in as flat a manner as possible what can be externally accessed.
Jeff
+4  A: 

The Attorney-Client idiom may be what you're looking for. The mechanics are not too different from your member proxy class solution, but this way is more idiomatic.

Drew Hall
I know of this by name only, and what I've glanced at so far looks very promising. This may be what I'm looking for. Thanks!
Jeff
OK everybody, this is almost verbatim what I reinvented, and I think that the link deserves a read. The difference is that in the published form, the helper class is called XAttourney as opposed to X::Proxy2, and the wrapped calls use references instead of pointers. I'm going to split the difference, making members called X::YAttourney that use static calls on references. Since this is about as well researched and canonical an answer as I'm going to get for 'how', I'm accepting this and will split 'why is friend itself all or nothing' into a new question later.
Jeff
+1  A: 

You can use a pattern described in Jeff Aldger's book 'C++ for real programmers'. It has no special name but there it is referred as 'gemstones and facets'. The basic idea is as following: among your main class that contains all the logic, you define several interfaces (not real interfaces, just like them) that implements sub-parts of that logic. Each of those interface (facet in terms of book) provides access to some of logic of main class (gemstone). Also, each facet holds the pointer to gemstone instance.

What does this mean for you?

  1. You can use any facet everywhere instead of gemstone.
  2. Users of facets doesn't have to know about gemstone structure, as in could be forward-declared and used through PIMPL-pattern.
  3. Other classes can refer to facet rather to gemstone - this is the answer to your question about how to expose limited nubmer of methods to specified class.

Hope this helps. You you wish, I could post code samples here to illustrate this pattern more clearly.

EDIT: Here's the code:

class Foo1; // This is all the client knows about Foo1
class PFoo1 { 
private: 
 Foo1* foo; 
public: 
 PFoo1(); 
 PFoo1(const PFoo1& pf); 
 ~PFoo(); 
 PFoo1& operator=(const PFoo1& pf); 

 void DoSomething(); 
 void DoSomethingElse(); 
}; 
class Foo1 { 
friend class PFoo1; 
protected: 
 Foo1(); 
public: 
 void DoSomething(); 
 void DoSomethingElse(); 
}; 

PFoo1::PFoo1() : foo(new Foo1) 
{} 

PFoo1::PFoo(const PFoo1& pf) : foo(new Foo1(*(pf
{} 

PFoo1::~PFoo() 
{ 
 delete foo; 
} 

PFoo1& PFoo1::operator=(const PFoo1& pf) 
{ 
 if (this != &pf) { 
  delete foo; 
  foo = new Foo1(*(pf.foo)); 
 } 
 return *this; 
} 

void PFoo1::DoSomething() 
{ 
 foo->DoSomething(); 
} 

void PFoo1::DoSomethingElse() 
{ 
 foo->DoSomethingElse(); 
} 

Foo1::Foo1() 
{ 
} 

void Foo1::DoSomething() 
{ 
 cout << “Foo::DoSomething()” << endl; 
} 

void Foo1::DoSomethingElse() 
{ 
 cout << “Foo::DoSomethingElse()” << endl; 
} 

EDIT2: Your class Foo1 could be more complex, for example, it contains two another methods:

void Foo1::DoAnotherThing() 
{ 
 cout << “Foo::DoAnotherThing()” << endl; 
} 

void Foo1::AndYetAnother() 
{ 
 cout << “Foo::AndYetAnother()” << endl; 
} 

And they're accessible via class PFoo2

class PFoo2 { 
    private: 
     Foo1* foo; 
    public: 
     PFoo2(); 
     PFoo2(const PFoo1& pf); 
     ~PFoo(); 
     PFoo2& operator=(const PFoo2& pf); 

     void DoAnotherThing(); 
     void AndYetAnother(); 
    };
void PFoo1::DoAnotherThing() 
    { 
     foo->DoAnotherThing(); 
    } 

    void PFoo1::AndYetAnother() 
    { 
     foo->AndYetAnother(); 
    } 

Those methods are not in PFoo1 class, so you cannot access them through it. In this way you can split the behavior of Foo1 to two (or more) facets PFoo1 and PFoo2. Those facets classes could be used in different places, and their caller shoudn't be aware of Foo1 implementation. Maybe it's not what you really want, but what you want is impossible for C++, and this is a work-aroud, but maybe too verbose...

Haspemulator
Thanks for the post, but I'm not sure I'm clear on this pattern's purpose. It seems like vanilla pImpl-style implementation hiding, where I just want a flat but selective interface description. Could you expound a bit on its nuances?
Jeff
Sure. See the edit2.
Haspemulator
+6  A: 

I have been using a very simple pattern for a while now. Not sure if it's got a name or anything, but it's exactly for the problem you described that I finally came up with it, after reading for a while about it on the net.

The idea is very simply, each class declares a Key:

// foo.h
class Foo;

class FooKey { friend class Foo; FooKey() {} };

Then any class wishing to grant specific access to Foo instances, just has to declare a method requiring a FooKey object as parameter.

// bar.h
class FooKey;

class Bar
{
public:
  void mySpecialMethod(int a, FooKey const&);
};

It's not perfect, of course, since then mySpecialMethod has a FooKey object and can thus call any method that would require it. But we're in C++, so we can strive to protect against Murphy but can't do much against Machiavelli.

It's very easy, and once you get into the habit of creating a key class each time you create a class, then it's trivial to add a restricted method any time you wish :)

EDIT:

Taking the comments into account, I thought about the forwarding issue.

It seems that using pass by value and preventing copying would effectively twart any attempt to misuse the key:

class Foo;

class FooKey: boost::noncopyable { friend class Foo; FooKey() {} };

Usage:

void method(FooKey);

void Foo::test() { method(FooKey()); }

No forwarding:

void super(FooKey a) { method(a); } // compiler complains as FooKey not copyable

And we can expect the compiler to apply optimizations since FooKey is an empty class. In fact I seem to recally that a gdb stack trace didn't show the parameter at all, but I may be wrong.

Matthieu M.
Simple and to the point, i like it.
Georg Fritzsche
I have to wonder why you're passing the FooKey by reference. ;) Seems an odd thing to do with an empty, unused object. Other than that, I like the idea, and I've done the same before on an ad-hoc basis, but this is the first time I really consider it as a general pattern. +1
jalf
Jeff
@Jeff: Note that `FooKey` can only be constructed (and thus passed to the method) by its friends due to its constructor being private. Compared to the Attorney idiom / proxying its *much* more concise.
Georg Fritzsche
I opened [a question](http://stackoverflow.com/questions/3220009/is-this-key-oriented-access-protection-pattern-a-known-idiom) regarding the naming of this pattern.
Georg Fritzsche
@Georg: It's definitely more concise, but that seems mitigated somewhat by needing to add extra parameters to restricted methods and quite possibly to explicitly prohibit FooKey copying, etc.. Passing nominally empty classes or references to them also seems to require some trivial amount of overhead, while the static attorney approach seems that it could be fully streamlined out at compilation.
Jeff
@Matthieu: I think that the attorney approach is what I really want, but this is definitely a very good solution as well. Thanks for the input, +1 vote.
Jeff
@Jeff: I'd say every decent compiler should optimize empty classes away for pass-by-value where you don't start to take its address or similar. But i agree that i downplayed the intrusiveness somewhat.
Georg Fritzsche
Matthieu, we got two new questions if you're interested: [naming](http://stackoverflow.com/questions/3324248/how-to-name-this-key-oriented-access-protection-pattern) and [improving re-usability](http://stackoverflow.com/questions/3324898/can-we-increase-the-re-usability-of-this-key-oriented-access-protection-pattern).
Georg Fritzsche