Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Quasar

Quasar's C++ Corner

Recommended Posts

Here is a neat thing you can do which, oddly, I cannot find any references about on the Internet.

In Smalltalk and Objective-C, it is possible to forward messages (aka method calls in C++) to other objects if the current object is incapable of handling them itself. This is a process called delegation.

C++ doesn't have any language features that seem deliberately designed for the express purpose of delegation, but it does have one that can be co-opted for it. Here's a brief demonstration.

class SomeOtherType
{
protected:
  int b;
public:
  void foo()  { printf("foo\n"); }
  int  getB() { return b; }
};

class SomeClass
{
protected:
  SomeOtherType containedObject;
public:
  void doStuff() { printf("Hi!\n"); }

  SomeOtherType *operator -> ()
  {
    return &containedObject;
  }
};

int main()
{
  SomeClass baz;

  // I can call the class's own methods normally...
  
  baz.doStuff();

  // But if I invoke operator -> , the "message" I send
  // is forwarded to a different object; in this case,
  // it goes to the containedObject of type SomeOtherType.
  
  baz->foo();
  int x = baz->getB();

  return x;
}
The one shortcoming of this against true delegation / message forwarding is that it's difficult to conditionally forward to different objects. This does however seem like an extremely useful alternative to multiple inheritance where you only need to combine the interfaces of two otherwise disparate classes (and especially when they share the same base class, and therefore the grotesqueries of virtual inheritance would be invoked).

What do you think about it?

Share this post


Link to post

Uhmmm... I'm not impressed. It seems to me that you waste the operator overload by hardwiring it to one specific type of object/action.

If the purpose was to have a class do something that may be common/expected of other classes, there are other design patterns such as Inversion of Control or Visitor which handle it better.

Well..."better" is very relative. This sort of design is better suited to "enterprise" systems which can literally make no sane assumptions about legibility or optimization, in the face of ever-changing specs. IMO they both lead to fugly code but are a more general solution to the problem you posed.

Share this post


Link to post
Maes said:

Uhmmm... I'm not impressed. It seems to me that you waste the operator overload by hardwiring it to one specific type of object/action.

If the purpose was to have a class do something that may be common/expected of other classes, there are other design patterns such as Inversion of Control or Visitor which handle it better.

Well..."better" is very relative. This sort of design is better suited to "enterprise" systems which can literally make no sane assumptions about legibility or optimization, in the face of ever-changing specs. IMO they both lead to fugly code but are a more general solution to the problem you posed.

Replace my one object with a virtual base class that can be inherited from by any number of objects, then. Say that the particular instance it is set to can be selected and is remembered as state. Whatever. Even a series of totally unrelated types could be returned by the operator function. The problem, as I mentioned, is that you cannot select the destination object *at* the point of call, as you can in Objective-C.

Don't criticize the concept solely on a simplistic example. It was meant to be illustrative, not holistic.

Share this post


Link to post

I think overloading certain operators, -> for one, just makes for code that's harder to read. Normally if you see "baz->foo()" you'd think the foo() method is being called on a baz object, and this kind of thing subverts that expectation, so that's why I wouldn't use such a construct.

Share this post


Link to post
andrewj said:

I think overloading certain operators, -> for one, just makes for code that's harder to read. Normally if you see "baz->foo()" you'd think the foo() method is being called on a baz object, and this kind of thing subverts that expectation, so that's why I wouldn't use such a construct.

What about when it's what you need to happen?

The problem in particular that I am looking for a solution to in Eternity at the moment is that I want to be able to store qstrings inside MetaTables. In order to do that I need a qstring-containing object derived from MetaObject.

However, I would like to have direct access to all of qstring's 56 methods and not have to jump through an accessor to get to them. I would, if it were not a nightmare, like the MetaObject descendant MetaQString, to behave as if it "is a" qstring.

However, doing that via multiple inheritance, which would be your first instinctual suggestion, would lead to a hideous problem - both MetaObject and qstring share a common base class - they are both ZoneObjects. This would necessitate virtual inheritance, which would, amongst the other problems it creates, make the use of static_cast alongside MetaObject's custom RTTI illegal. The current idiom looks like this, and would be blown to hell:

  while((obj = meta->getNextType(obj, METATYPE(MetaString))))
  {
     // I KNOW it is a MetaString; this is safe.
     MetaString *str = static_cast<MetaString *>(obj);
     ...
  }
I wish to avoid multiple inheritance always. I think it leads to bad things in pretty much any situation.

Share this post


Link to post

Neat trick, although a bit too "clever" for my liking. To me, this sort of demonstrates how C++ got operator overloading wrong: it shouldn't even be possible to overload operators like ->.

Worth pointing out as well, this still misses a lot of the usefulness of delegation, because in Objective C you can use it to do things like RPC calls, and have your method call forwarded to another machine. You can't really do that here without some kind of code generation tool.

Share this post


Link to post

Grotesqueries of inheritence? Not sure what you mean (not very fluent in C++) but I would have figured that in general it's simpler to just subclass and let inheritence take care of calling one of the parents' method?

Share this post


Link to post
fraggle said:

Neat trick, although a bit too "clever" for my liking. To me, this sort of demonstrates how C++ got operator overloading wrong: it shouldn't even be possible to overload operators like ->.

Worth pointing out as well, this still misses a lot of the usefulness of delegation, because in Objective C you can use it to do things like RPC calls, and have your method call forwarded to another machine. You can't really do that here without some kind of code generation tool.

Actually the main use for overloading operator -> is usually for supporting "smart pointer" classes. In those implementations, they do almost literally the same thing I am doing here: redirect the access to the object to which they internally point.

STL iterators, which either are pointers, or act like pointers depending on the implementation, generally also have it (as well as a unary operator *).

hex11: I said virtual inheritance. Look it up. It's not a good thing in general - it requires modification of your class hierarchy all the way up to the root in order to avoid duplication of instances inside a class that inherits from them more than once, requires dynamic_cast to be used, and makes your program slower.

EDIT: Here is a more concrete idea of what I have in mind:
http://eternity.mancubus.net/text/metaqstringptr.cpp.txt

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×