In his most recent post, Chris Rolliston points out a gotcha resulting from a change in the VCL with XE3. The post itself is quite interesting and makes some valid and significant observations. However, in the follow up comments Chris makes the observation that the old code was lazy, relied on a compiler detail and should have been “properly” re-written, rather than replacing reliance on one compiler detail with another.

I think it is worth highlighting that Chris’ observations here are – I believe – directed at the use of untyped pointers and dynamic arrays, not the use of the pointer exposed by the TList class itself, as the two things could easily be conflated when reading Chris’ post and the QC issue it refers to.

The fact that TList is implemented as a pointer to an array of pointers ceased to be a private implementation detail the day that that internal pointer (strongly typed I might add) was exposed through the public interface of the TList class.

At that point it became a contract – for good or bad.

It was certainly never a compiler implementation detail.

This was what crystallised for me that Chris was talking about the overload selection made by the compiler, and thus identified the comparison of untyped pointers and dynamic arrays as the subject of the comment, rather than the use of the list pointer in the code per se.

Otherwise I think it goes without saying that writing code that takes advantage of a contracted interface in the most optimal fashion – especially (but not necessarily only) when runtime efficiency is required – imho is not “lazy”, it’s efficient.

Indeed, I have taken advantage of this myself in the past to implement a super-fast mechanism for “cloning” TList instances by directly allocating the required memory in the destination list (setting the Count property) and then directly copying all list items in a single CopyMemory operation from one list to the other gratefully using their exposed List pointers.

Even without going to the extra lengths of trying to suppressing the zeroing of the new items triggered by setting Count directly – which would have necessitated some real dirty tricks – this is vastly more efficient than enumerating the items in one list and adding them to the other.

But I digress…

To draw a comparison from the physical world … ask yourself which is the more lazy:

  • Getting in your car and driving 3 km on public highways to get to a shop
  • Using a footpath – less obvious to a habitual car driver and perhaps less well known generally – that takes you directly to the same shop after just a 300 m walk

Is it “lazy” to take the quicker, but physically more demanding, option of walking via the footpath ?

I would say it is more lazy to get in the car and drive the more obvious but less efficient route.

And if you use your car on the basis that the council might close the footpath and leave you stranded at the shop with no way to get home, then I would say you are not just trying to justify your own laziness but are paranoid to boot! 🙂

The key here is that the footpath is official, is on the map and does not involve hacking your way with a machette through thick bush obscuring the route across private property.

It may be poorly lit and make you nervous in the dark, but carrying a flashlight can help in that situation (comment your code!). 🙂

On the other hand ….

Adding overloads to an existing class with thousands if not millions of lines of existing, dependent code without – apparently – taking the time to consider the impact of those overloads on that existing code, that is not just lazy but downright inconsiderate!

10 thoughts on “Lazy or Efficient ?

  1. It is lazy, for multiple reasons – the classic TList is a list of pointers, not integers; there is hardly any situation in which you really want to stream a pointer as a pointer, so streaming one is intrinsically a code smell; and as soon as you use a list class that doesn’t directly expose its internal storage, an intermediary variable must be used because you can’t directly pass a property as an untyped parameter.

    That said, I think it a flaw in the language that untyped pointers can be assigned to something expecting a dynamic array without even so much as a compiler hint. If I didn’t like static typing I’d be using C…

    1. As I said, the danger is in conflating the issues.

      Pointers and streaming – not good bedfellows. Using the directly exposed list pointer of a TList – sometimes the most efficient and perfectly reliable if used according to the contract.

      Worth stressing of course is that the contract says “pointer to pointers”, so treating it as a “pointer to integers” does break that contract. 🙂

  2. Are you making your 300m footpath not shaved and in briefs? Why nobody bothers to present a decent solution for writing a pointer array of TList instance to a stream that works for all versions? I have no XE3 and can’t test it myself.

    1. Rather than taking the footpath unshaved and in briefs (hey, it’s a free country – here in NZ at least – so whatever floats your boat), rather I think what you are driving [sic] at is when you try to take your car down the footpath.

      It probably won’t fit, isn’t what it is intended for and is not only dangerous but also illegal.

      As I said in my reply to Chris, using a TList to store Integers and then trying to stream them via the exposed pointer to pointers is trying to drive a car down a footpath – a bad idea. But using the pointer to pointers isn’t the problem, it’s storing integers where pointers are expected that’s the problem. That was the point that Chris made in his post, and that I agree with.

      I was merely trying to de-conflate otherwise valid use of the list pointer. 🙂

      1. I just wanted to say that the whole talk is a senseless blah-blah-blah if nobody presented a code for writing array of pointers to stream that is valid for all Delphi versions. Nothing to discuss without a code. The only code presented is the one from the bug report, it does not work on XE3.

  3. If I was going to the store, I’d take one other factor into consideration: am I planning on buying more goods than I can comfortably carry home with my own strength? If so, it might be a good idea to bring the car along even if it’s a bit out of the way to do so.

    Not sure how that fits into the analogy, though… 😛

  4. I really really don’t like overloads in existing classes. I am not convinced this was a change worth making to the RTL. But it’s done.

    I think the compiler needs more warnings, and maybe it should be possible to block certain type coercions on overloaded methods (force raw uncasted pointers passed as the parameter to a stream method, to fail, in this case, so that you fail early and and often at compile time, and NEVER EVER at runtime.)

    That is the WHOLE point of static (strong) typing.


    1. imho there’s nothing wrong with adding overloads as long as they don’t potentially change the behaviour (or completely break) existing code.

Comments are closed.