56

Matthieu M. brought up a pattern for access-protection in this answer that i'd seen before, but never conciously considered a pattern:

class SomeKey { 
    friend class Foo;
    SomeKey() {} 
    // possibly make it non-copyable too
};

class Bar {
public:
    void protectedMethod(SomeKey);
};

Here only a friend of the key class has access to protectedMethod():

class Foo {
    void do_stuff(Bar& b) { 
        b.protectedMethod(SomeKey()); // fine, Foo is friend of SomeKey
    }
};

class Baz {
    void do_stuff(Bar& b) {
        b.protectedMethod(SomeKey()); // error, SomeKey::SomeKey() is private
    }
};

It allows more fine-granular access-control than making Foo a friend of Bar and avoids more complicated proxying patterns.

Does anyone know whether this approach already has a name, i.e., is a known pattern?

Community
  • 1
  • 1
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
  • Might be an idea to show how you would actually use those classes. –  Jul 10 '10 at 16:54
  • @Neil: Right, that may not be obvious. – Georg Fritzsche Jul 10 '10 at 17:00
  • 1
    It might be useful to make the key noncopyable, unless you want `Foo` to be able to delegate access to other classes (of course, delegation might be useful, depending on the circumstances). – James McNellis Jul 10 '10 at 17:06
  • Personally I don't like 'friend xxx' in general, but that's just me :-) – AndersK Jul 10 '10 at 17:16
  • This is quite a neat idea for granting partial friendship... I don't have a C++ compiler handy, but I wonder if you could you make protectedMethod a template specialization, so that you don't even incur the cost of the SomeKey() construction... –  Jul 10 '10 at 19:33
  • @brone: Any decent compiler will optimize it away if the definition is visible. It only needs to behave *"as if"* it happened - which is pretty trivial for an empty class :) – Georg Fritzsche Jul 10 '10 at 19:50
  • 1
    Q: could it be defeated with the following hack? char* some_junk_object=new char[...]; SomeKey* p=(SomeKey*)(some_junk_object); b.protectedMethod(*p); If all you're doing is relying on the type system, this is trivial to subvert. (I've seen a similar key system fail spectacularly in a similar way.) – Owen S. Jul 17 '10 at 19:33
  • 1
    @Owen: If the copy-ctor is accessible, yes - but then again there is no way to protect against everything in C++, there is always some hack around it. We want to protect against mistakes, not deliberate abuse. If we don't want delegation and make the copy-ctor non-public, your hack wouldn't work. – Georg Fritzsche Jul 17 '10 at 19:46
  • Ah, yes, missed the copy – that will help. Just so long as you realize the extent to which your key may or may not protect you. – Owen S. Jul 17 '10 at 21:11
  • @Owen: I'm not sure that your question is really relevant. Of course you can (most probably) bypass it like you can bypass the normal access checks by doing stupid things like #define private public. As always it is the responsability of the programmer to use his tools correctly which is mostly what you said in your last comment. – n1ckp Jul 18 '10 at 12:51
  • @nick: I'm trying to pass along that I've seen schemes like this fail _without_ going to such lengths as altering the header's contents through editing or macro magic. It miffs me that you feel a caution on the idiom being described is irrelevant to the question, whether or not you think it's a _valid_ criticism, but do with this as you see fit. – Owen S. Jul 18 '10 at 15:23
  • I opened a [follow-up question on naming](http://stackoverflow.com/questions/3324248/how-to-name-this-key-oriented-access-protection-pattern). – Georg Fritzsche Jul 24 '10 at 08:31
  • [And a follow up on generalizing it.](http://stackoverflow.com/questions/3324898/can-we-increase-the-re-usability-of-this-key-oriented-access-protection-pattern) – GManNickG Oct 11 '10 at 03:07
  • Are there any anecdotes of this idiom being optimized into no-ops on GCC? – Emile Cormier Apr 14 '15 at 02:58
  • Wonderful idiom. Thanks! – Jan Korous Sep 03 '15 at 08:32

4 Answers4

16

Thanks to your other question it looks like this pattern is now known as the "passkey" pattern.

In C++11, it gets even cleaner, because instead of calling

b.protectedMethod(SomeKey());

you can just call:

b.protectedMethod({});
Community
  • 1
  • 1
Rick Yorgason
  • 1,616
  • 14
  • 22
  • not sure, but without c++11 it could be an option to provide a `SomeKey` constructor that takes eg. an `int` to allow for a somewhat shorter `b.protectedMethod(0);` – 463035818_is_not_an_ai Nov 22 '17 at 15:52
  • Just a detail: The shorthand version `foo.bar({})` doesn't work with constructors e.g. `Foo foo({})` as it is ambiguous - the compiler doesn't know which constructor is being called, it could be a copy constructor. Or at least that's an explanation that seems reasonable to me, correct me if I'm wrong. – egst Oct 12 '18 at 17:42
  • Can someone elaborate what means {} in b.protectedMethod({}); – yakoda Jan 21 '21 at 14:06
  • @yako: `{}` for default construct first argument – Marco Kinski Mar 10 '22 at 15:58
7

It seems that this idiom like one mentioned in another SO question here. It is called Attorney-Client idiom and described in more details there.

Community
  • 1
  • 1
Haspemulator
  • 11,050
  • 9
  • 49
  • 76
  • 2
    I already linked to another answer to that question and the pattern above doesn't involve proxying through another class. There is no *"attorney"* we need to call through (they are expensive anyway), instead we simply get access by passing a *key* along. – Georg Fritzsche Jul 10 '10 at 17:39
  • @Georg: In what real sense are the attorney calls expensive? If they are stateless, static inlines that merely forward arguments, how would any size or time penalty be introduced? – Jeff Jul 10 '10 at 18:07
  • 1
    @jeff: The point on expenses was meant to be a pun regarding hourly rates ;) Still, one has to manually forward in the attorney case - you have to write more. – Georg Fritzsche Jul 10 '10 at 18:21
  • @Georg: sorry, read pun as double entente on the _call_ expense, not on method _definition_. I generally agree, but I did make a comment in the other question's thread about actual runtime expense. – Jeff Jul 10 '10 at 18:43
3

some boring man like me would make the fowllow code:

int FraudKey=0;
b.protectedMethod(reinterpret_cast<SomeKey&>(FraudKey));
thomas
  • 505
  • 3
  • 12
  • 3
    That's not really surprising in C++ - this is not supposed to protect against malicious intent. – Georg Fritzsche Sep 19 '14 at 08:59
  • Maybe making `SomeKey` inner private to `Bar` would kind of protect against such uses - or at least restrict it to the `Bar` class. But then it would require to always use `protectedMethod({})`. – Tarc Dec 29 '16 at 16:53
  • 2
    You have invoked undefined behavior. The bug exists in your code, as the caller of the function, because you are using `reinterpret_cast` to access an object by a type other than its actual type. From undefined behavior, absurdity follows. – David Stone May 12 '17 at 02:31
  • 1
    That's why the `SomeKey` copy ctor has to be private as well. That ensures that this would fail to compile, while valid uses would succeed. – monkey0506 Sep 06 '19 at 23:00
0

Its pretty close to this:

http://minorfs.wordpress.com/2013/01/18/raiicap-pattern-injected-singleton-alternative-for-c/

Basically if you consider a reference to an object of well designed class to be provide the access control you need to implement any access control policy that actually makes sense, applying this pattern to anything other than the constructor does not seem to make that much sense.

So as the article states, if you use this key in conjunction with those constructors for what access control might make sense, objects that represent significant parts of scares resources, that in C++ would generally be implemented as RAII objects, than the name RAIICap or RAII-Capability would indeed make sense.

http://www.eros-os.org/essays/capintro.html

Alternatively you could refer to it with a more general name like construct authority.

The implementation in the article is a bit to much main centered, that is, main needs to create all the authority keys. You can extend on it and make it more flexible by adding an additional public constructor for the key itself:

template <typename T>
class construct_authority {
  public:
    construct_authority(construct_authority<void> const&)
    friend int main(int,char **);
  private:
    construct_authority(){}
};

That way main could delegate the key creation to other parts of the program.

Personally I think the RAIICap name is quite appropriate for the useful part of this pattern.

A while ago I proposed that this simple template above could be added to the standard library.

https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/p_v-aYIvO1E

Unfortunately there are issues with the idea that there can be one main fingerprint that constitutes a computational root, so something like this apparently can't have a place in the standard library. Having said this, at least for the use with the constructor of RAII classes, this pattern seems to be quite useful.

user1703394
  • 128
  • 4