11

Objective-C category feature allows programmer to add new method which was not defined in original class definition.

Can I archive similar functionality (language construct or some technique) on C++?

Major concern is consistent method calling syntax (. or -> operator).

eonil
  • 83,476
  • 81
  • 317
  • 516
  • Yes, If you have access to the class definition. No otherwise. – Alok Save Feb 19 '13 at 07:37
  • At run-time you don't get any language support for that. However, you can do it using e.g. a map containing function pointers. But that is then rather cumbersome and error prone. – Michael Wild Feb 19 '13 at 07:40
  • @AlokSave Do you mean modifying source code directly? It could be an option. – eonil Feb 19 '13 at 07:40
  • @MichaelWild It doesn't need to done at runtime. I'm using *Poco*, and I don't want to modify its source code at all because I don't want extra complexity on external dependency maintenance. If there's a way to add new method without modifying existing class codes, it would be good too. – eonil Feb 19 '13 at 07:45
  • 1
    That's not possible. The best you can do is to write non-member functions which take a pointer to an instance as their first parameter. – leemes Feb 19 '13 at 07:53

4 Answers4

7

Let's consider the following class to be extended:

struct A {
    int x, y;
    A(int x, int y) : x(x), y(y) {}
};

You can inherit from this class or write a wrapper class which contains an instance of this class. In most cases, inheritance is the way to go, as a wrapper class isn't an A but it wraps (contains) an A.

With C++11 move semantics, promoting an instance A to a subclass B (inheriting A) will be efficient and doesn't require to copy the instance A:

class B : public A {
public:
    B (A &&a) : A(a), someOtherMember(a.x + a.y) {}

    // added public stuff:
    int someOtherFunction() const { return someOtherMember; }

private:
    // added private stuff:
    int someOtherMember;
};

Full code with example: http://ideone.com/mZLLEu

Of course the function I added is a bit silly (and the member even more, since it doesn't respect further changes of the original members x and y), but you should get an idea of what I want to demonstrate.

Note the constructor B (A &&a) which is something I call "promote constructor" (this is not a standard term). Normally, B (B &&b) is a move constructor, which moves the contents of the provided B instance into a new B about to be constructed. I use the move semantics to move an instance of A (which has been returned by another function) into the super-class A of B.

Effectively, you can promote A to B while making it possible to use a B as an A.

In contrast to Soonts' answer, my solution also works with added virtual tables, since it doesn't rely on unsafe pointer casting.

leemes
  • 44,967
  • 21
  • 135
  • 183
  • I understood that *move ctor* creates a new object semantically, and compiler may re-use existing object if possible. And your *promote ctor* also has same chance for compiler optimization. Is my understanding correct? – eonil Feb 19 '13 at 08:55
  • 1
    `A(A &&a)` is move ctor for `A`. This will move an old `A` into a new `A`. Semantically it's a new object, but thanks to move semantics it can "steal" the contents of the old `A`. The idea of my approach is to move the old `A` into a `B`. Since `B` inherits from `A`, it has to initialize an `A` anyway. I use the move ctor of `A` in some constructor of `B` which I want to call *promote ctor* (I made this word up, since it effectively promotes an existing `A` to a `B` without touching the contents of `A` as long as its move ctor is well written). – leemes Feb 19 '13 at 09:22
  • There is a subtlety here though — consider the case where you also have e.g. `class C : public A`, with a move constructor from `A`. In that case, the object you have is either an `A`, a `B`, or a `C`. It can’t be both `B` and `C` at the same time. – al45tair May 23 '14 at 08:42
5

Another option, which may not be viewed as "clean" by some (though it is in my opinion) but still accomplishes the same thing, is using a static class. It's important to remember that when we create a member function, what's really happening behind the scenes is that the compile generates a function where the object (aka "this") is the first parameter. So, we can do the same thing to extend our class's functionality without deriving from it.

class Something
{
public:
   Something()
   ~Something()
}

// In objective-c you may call this category Something+Utils
class SomethingUtils
{
   // You can use a pointer, or a reference here, your call.
   static int GetSomethingElse(Something *something, int parameter);
}

This will accomplish the same intent as a category: You extend the functionality of your class object, and you don't have to create a new derived class. You will not be able to access private or protected member functions and variables, but you can't do that in objective-c anyway, so there's nothing lost on that front (and if you are trying to use private or protected member state, you've missed the point of categories entirely). You wouldn't be able to use the . and -> operators, but in my opinion, that's a much better compromise than deriving a new type just to add some utility methods.

Adam Eskreis
  • 71
  • 1
  • 3
  • This is syntactically ugly, but I have to agree this is the most practical option. Even I adapt @leemes' solution, but I would write some utility class like this, and will wrap it up. – eonil Mar 22 '14 at 04:14
  • 5
    A slight variation that's more stylistically appealing in some respects is to use a namespace instead. – Nate Chandler Apr 06 '14 at 03:14
  • I would also add Eonil, that while you may view it as syntactically ugly (I personally don't), it is syntactically clear. It is implicitly obvious that this is a class extension rather than a core method, an issue obj-c categories suffer from (you must follow the method to the header file to realize it is a category). As for the ugliness, I won't debate that, because we will likely never see eye to eye on that one :) – Adam Eskreis Apr 07 '14 at 16:35
1

C++ has the inheritance for that. In addition, I've used the following trick a few times to extend classes generated by #import "progid:..." directive:

// This one is part of external framework, or auto-generated, or dllimport, or #import, etc..
class A
{
    protected double m_x;
};

// This one is the extension class. Make sure you only add non-virtual methods here.
// Static methods and static data members are OK as well.
class __declspec( novtable ) B: public A
{
public:
    double getSquare(){ return m_x * m_x; }
    __declspec( property( get = getSquare ) ) double square;
};

// Usage example
double someFunc( A& arg )
{
    B& b = static_cast<B&>( arg ); // Note we've not constructed any instance of B, just casted.
    return b.square;
}
Soonts
  • 20,079
  • 9
  • 57
  • 130
  • Oh actually I just figured out similar (maybe same?) technique, and posted a new [question](http://stackoverflow.com/questions/14952504/is-it-possible-to-downcast-an-object-to-a-subclass-which-does-not-define-extra-v). Can I treat this technique is safe by standard? – eonil Feb 19 '13 at 07:59
  • 5
    `__declspec` and `property` are not part of the ISO standard C++. – Bo Persson Feb 19 '13 at 08:04
  • I'm absolutely sure it worked in Visual Studio 2005 and 2008. I don't know is it standard, and to be honest I don't care. "Standard C++" is just another leaky abstraction. To make sure your C++ code builds with compilers A, B and C, you must develop and test it that way.. – Soonts Feb 19 '13 at 08:04
  • @Soonts I agree with you that if some feature X is supported by compilers A, B, C but not in the standard is better to be used than a feature Y which is in the standard and not implemented in the compilers you want to support. However, the features in your code are MSVC only, which is a huge restriction. – leemes Feb 19 '13 at 08:15
  • Unfortunately, I am working on and for Linux/BSD family OS, and my code is expected to have maximum compatibility over many platforms as possible... – eonil Feb 19 '13 at 09:08
  • 3
    Well, this `property` thing seems to be just a handy wrapper for getters and setters. Effectively, `b.square` is just syntactical suggar for `b.getSquare()` when it is being read. The other thing (`novtable`) seems to be an assertion that there shouldn't be a vtable being used; if you write virtual functions, I guess you will get a compiler error, since they require a vtable. The problem with this approach is that the memory layout of `B` has to start with `A` which isn't the case if either vtables or members (technically, a vtable pointer is something like a member) are being added. – leemes Feb 19 '13 at 09:25
0

I got a nearly consistent calling convention with an idea I talked about in a lightning talk a year or so ago:

(the intro makes less sense without he commentary - wait til it gets to the C++ bit).

Note that the material was not intended to be taken seriously - although some, inevitably, have ;-)

philsquared
  • 22,403
  • 12
  • 69
  • 98