C++ Enemy Functions

Scott Meyers wrote an article in CUJ a few years ago entitled How Non-Member Functions Improve Encapsulation. I highly recommend this article, as well as Herb Sutter’s GotW #84: Monoliths “Unstrung”, in which everyone’s favorite scapegoat, std::string, is refactored using this principle.

I have just stumbled onto a rather satisfying, if obvious, extension of this work. First lets review the properties of a friend function. Recall that a friend:

  • …can access the private members of a class
  • …is not passed a this pointer
  • …does not reside in the class’s namespace

Now consider a function which:

  • cannot access the private members of a class
  • is passed a this pointer
  • does reside in the class’s namespace

With tongue-in-cheek, I call this an enemy function (alternate names are welcome).

We can add as many enemy functions as we like to a class without reducing its degree of encapsulation, because these functions only have access to the class’s public interface. Better still, since we pass a this pointer, we don’t have the inconsistency between w.eat(.564) and nap(w) (from the Meyers article). This means we can fix std::string without breaking existing code. I envision something like this:


   class Foo
   {
      public:
          // ...
      private:
          // ...
      enemy:
          void func();  // may not access private members
    }

   // ...time passes...

   Foo f;
   f.func();  //not really a member function

Update (6/4/06):
I threw caution to the wind and posted this idea to comp.lang.c++.moderated. Maxim Yegorushkin made a great suggestion: why not use a public member function in a derived class?


   namespace
   {
   class FooBase
   {
      private:
      int x;
      public:
      FooBase(int x) : x(x) {}
      int size() { return x; }
   };
   }
   class Foo : public FooBase
   {
      public:
      Foo(int x) : FooBase(x) {}
      bool empty() { return size() == 0; }
   };

This does exactly what I wanted. The only downside is that I must write forwarding functions for FooBase’s constructors. I find this much more satisfying than Scott Meyer’s class Foo / namespace FooStuff method.

Update (6/15/06):
Many who object to the subclass solution do so on the grounds that it is “misuse of inheritance”. To appease them, I’ve modified the code above to wrap FooBase in an anonymous namespace. The “world” will have no idea I misused inheritance.

Advertisements

4 Responses to “C++ Enemy Functions”


  1. 1 Bheeshmar June 1, 2006 at 8:17 am

    Interesting idea, but I propose a refinement.

    I think the language should be extended so that if you create a namespace with the same name as the class, and functions in it take an explicit “this” as a first parameter, then they can be called as members.

    Por ejemplo:


    class Foo
    {
    public:
    void bar( int n ) { n_ = n; }
    int bar() const { return n_; }
    };

    namespace Foo
    {
    int baz( Foo & foo, int n ) { foo.bar( n + 5 ); return foo.bar(); }
    }

    Foo foo;
    foo.bar( 6 );
    assert( 6 == foo.bar() );
    assert( 10 == foo.baz( 4 ) );
    assert( 10 == foo.bar() );

    My rationale for this is that namespaces are extensible and classes are not. It’s likely that you’ll think of more functions AFTER designing the class than before, so allow convenience functions (defined in terms of public methods) to be called as though they are members.

    What do you think?

  2. 2 Kevin Hickey June 1, 2006 at 11:30 am

    That’s been done. It’s called Ruby.

  3. 3 Mark June 2, 2006 at 3:42 pm

    Bheesh,

    I actually had a similar idea too (I had envisioned a completely implicit this pointer), but I didn’t blog about it.

    It rocks that the namespace can be “open” whereas the class is “closed”. This is one benefit of Scott Meyer’s “FooStuff” approach that I could not replicate with my enemy access qualifier.

    Additionally, this would be very easy to add to the language. In my original proposal, we’d have problem with any existing source codes that already used the word “enemy”. But, since your syntax is currently illegal in C++, it should be OK to support it retroactively.

    There may also be some benefits in terms of compatibility with the current rules for C++ Koenig lookup.

    In short, I think it’s a great idea.

  4. 4 Mark June 2, 2006 at 3:49 pm

    Kevin,

    I’m looking on page 356 of my Programming Ruby book (the access control section), and all I see are public, protected, and private. What am I missing?

    Maybe you’re just talking about open classes (or open objects). I’m no expert, but I’d guess these features can trace their lineage back much further than Ruby. Smalltalk? Some other dynamically-typed object-oriented language?


Comments are currently closed.




%d bloggers like this: