QList need to know all modifications
-
Thank you all for your various posts.
September was a long time ago now so I have moved on from this (without resolution, but that's OK now) :)
I do realise that in practice encapsulation is probably the best approach. It's just that I didn't want to do that, I wanted existing variables which are
QList
to remain asQList
rather than having to call a method to access the list and/or having to write loads of methods on the encapsulator to access theQList
member's methods. But, not surprisingly, nothing quiteIt sounds like C++ 2129 might have features I need ;-)
Just musing here. C++ inheritance and virtual methods are great. But I am always disappointed at how many methods are not virtual when I wish they were. Here, for example, if all the base modification
QList
(and other classes') methods likeappend()
etc. wereprotected virtual
I could do what I want easily. I guess implementation-wise there IS some overhead in the code generated for a virtual method over a fixed one? Or not?In my last days of using C# they introduced extension methods, whereby you can add your own new methods to existing base classes to extend them with extra functionality. If I use Python, or JavaScript, I can do that naughty "monkey patching" where I can effectively go
QList.append = myNewImplementation
, changing the actual method used forQList.append()
to my own code which does whatever and calls the original base implementation. Naughty/dangerous but nice! But not from C++. -
Ok, I'm late to the party, buuuuut :D
You know, QList a very small class, as it is a template class, in fact I think you'll only need to hook into the the cpp file and the even viewer function implementations of QListData (the infamous d-pointer) as that is the actual part that is modifying the data.
once you decide to switch to release, you just link against/ship the original QList impl
-
@JonB said in QList need to know all modifications:
Or any other way!? But please note I am not prepared to write reams of code for this, e.g.
QList
has 50+ methods, I am not going to write a new definition for all of these....Have you considered
using
with private inheritance instead new definitions?ie
template<typename T> class List: private QList<T> { public: using QList<T>::append; // pass-thru T operator[](qsizetype index) { qDebug() << "calling []"; return QList<T>::operator[](index); } };
-
@JonB said in QList need to know all modifications:
I guess implementation-wise there IS some overhead in the code generated for a virtual method over a fixed one? Or not?
Well, most of the time those functions in QList will be inlined. This opens up the possibility for a lot more optimizations. I would claim that good performance is important for something like QList. With the use of virtual methods in many cases the compiler cannot devirtualize the methodes. Sure, if you declare the variable and call functions within the same scope, nothing is lost. I would guess, however, that it is very common to hand a QList as a parameter to a function as a reference. This called function would have to keep the virtual function call and thus slow down the common case.
@JonB said in QList need to know all modifications:
If I use Python, or JavaScript, I can do that naughty "monkey patching"
Sounds if you would be a little happier with Objective-C (or Objective-C++) instead of C++. Method calls are just strings with a really fast lookup of the correct function. This makes it easy to override a method (you can even replace class implementations system-wide at runtime). I don't know if this is used for containers, though. In the end, it is still a lot like virtual functions and thus has the same drawbacks.
-
@SimonSchroeder said in QList need to know all modifications:
Method calls are just strings with a really fast lookup of the correct function.
Now hang on! Is that really true? That sounds more like an interpreter. I don't care how fast you claim a lookup might be, this must be orders of magnitude slower than compiling the address of the actual function --- and as I understand it C++ figures the actual correct one to call at compile time, following the inheritance chain at that time not at runtime. So not even one level of indirection. Unless that maybe applies to non-virtual methods only, but I thought it resolves virtual ones too.
-
@JonB said in QList need to know all modifications:
So not even one level of indirection
There is for virtual methods. See vtable https://pabloariasal.github.io/2017/06/10/understanding-virtual-tables/
But it is not based on strings :-) -
@jsulm
Yes, that's why I suspected indirection would be required invirtual
case.However, for a summary that article still did not make clear (to me) whether this is always a single level of indirection or as many multiple indirections as the inherited class hierarchy have their own overrides of a virtual method? I know what happens when class B overrides class A's virtual method and you write
instanceAsA->virtualMethod()
--- one indirection.But what when you have 10 levels of inheritance,
A -> B -> C -> D -> ...
, each of which override the virtual method.? Does the runtime implementation ofinstanceAsA->virtualMethod()
require 10 vtable/vpointer indirections, via each intermediate class, or does it go straight fromA->virtualMethod()
toJ->virtualMethod()
? That is a huge difference in practice when one has a deep class hierarchy? -
@JonB This is a good question. I'm not sure. But there is a way in gdb to inspect the vtable:
info vtbl b
b being an object. This way you could check the pointers in all the vtables and see how those were set up. If I find time I will also do this.
But it looks like the compiler puts the pointer to the "most derived function" in the vtable, so there is always one indirection. Search for "most derived function" in this thread: https://www.learncpp.com/cpp-tutorial/the-virtual-table/ -
@jsulm said in QList need to know all modifications:
the compiler puts the pointer to the "most derived function" in the vtable, so there is always one indirection.
That is what I thought, but did not know. There is a hell of a difference between a single indirection in machine code and a potentially iterative number of indirections for a deeply-nested inheritance...!
-
Each class has its own vtable (it is not per object). Each object then has a pointer to the vtable. When you have a pointer to a base class pointing to an object of a derived class, it just uses the pointer to the vtable and calls the appropriate function. Like mentioned before, this is only one indirection. (It gets more complicated with multiple inheritance.)
Concerning Objective-C: Most strings for message are compile-time known strings. This means that "string comparison" in reality is most of the time just comparison of pointers. The runtime also uses a little cache to speed up the most recent function calls.
BTW: Since we are always talking about performance here. There is a CppCon talk on Youtube "Optimizing Away C++ Virtual Functions May be Pointless" https://youtu.be/i5MAXAxp_Tw?si=ieyiW3G31UMmANV-