Erik@Grijjy 123 Posted January 25, 2022 In the category Fun with Generics: https://blog.grijjy.com/2022/01/25/crgp/ 6 6 Share this post Link to post
Rollo62 539 Posted January 26, 2022 (edited) 14 hours ago, Erik@Grijjy said: In the category Fun with Generics: https://blog.grijjy.com/2022/01/25/crgp/ Very interesting article, I love C++ Delphi Especially the use case samples are great, where I think this could make it possible to be used in a nested way, what I consider for some time. Something like that: var Builder := THtmlStringBuilder.Create; Builder .Html .Header.Enter .Header.Text( 'In header' ) .Header.Leave .Div.Enter .Div.Id( 'main' ) .Div.Color( clYellow ) .Div.Text( 'In div' ) .Div.Leave .Footer.Enter .Footer.Text( 'In footer' ) .Footer.Leave ; WriteLn(Builder.Data); I think that should be possible and should make sense too, I have to check that. What I'm a little unsure is, how stable and reliable this "self reference trick" is tested under different Delphi versions. Can-I-Use it without any unexpected quirks, or do I better wait and see if this is still running in the next version(s) ? Edited January 26, 2022 by Rollo62 Share this post Link to post
Erik@Grijjy 123 Posted January 26, 2022 9 hours ago, Rollo62 said: Very interesting article, I love C++ Delphi Especially the use case samples are great, where I think this could make it possible to be used in a nested way, what I consider for some time. Something like that: Thanks! Yes, the nested case is definitely possible. Your THtmlStringBuilder class would probably have to maintain some sort of state stack so you can match your Enter and Leave calls. This will also ensure you output proper (X)HTML. BTW: This pattern isn't a trick and doesn't depend on any compiler quirks. It is just a feature of generics in Delphi (and possibly other OOP languages). I remember using this pattern first with Delphi at least 5 years ago, and I don't see any reason why future compilers would change this behavior. It may not be supported by the very first (few) versions of Delphi that introduced generics, but I have no way to test that. Share this post Link to post
Rollo62 539 Posted January 26, 2022 31 minutes ago, Erik@Grijjy said: BTW: This pattern isn't a trick and doesn't depend on any compiler quirks. It is just a feature of generics in Delphi (and possibly other OOP languages). I remember using this pattern first with Delphi at least 5 years ago, and I don't see any reason why future compilers would change this behavior. Thanks for clarification, I hadn't recognized from your article that this feature is D5 compatible, sounded D11 related to me, and it looked a little alien to me anyway and ringing my alarm bells Much better that this pattern has such a long history, I was not using D5, but BCB5 at those days, so I'm still learning new features every day. 1 Share this post Link to post
David Schwartz 430 Posted January 27, 2022 (edited) Ok, after reading the article, it makes more sense. It will take some pondering to come up with legitimate use cases. The article ends with: Quote Arguably the most common use case for this pattern in the C++ world is to implement a form of static polymorphism. This form of polymorphism resolves methods calls at compile-time instead of run-time. This avoids the overhead of virtual methods and can therefore improve performance a bit. But since this requires a C++ specific template feature, it cannot be replicated in Delphi (although I would love to be proven wrong here). A while back, I was trying to translate a Swagger spec into some Delphi code, and I ran into some issues that this might solve. The Swagger spec is read at run-time and the program generates Dephi code that gets compiled later on. The problem is that the typing isn't really known at run-time when you're reading the Swagger spec. But emitting code that resolves it at a subsequent compile time without having to know it when it's emitted might solve the problem... Edited January 27, 2022 by David Schwartz 1 Share this post Link to post
Edwin Yip 154 Posted January 27, 2022 (edited) 10 hours ago, Erik@Grijjy said: It may not be supported by the very first (few) versions of Delphi that introduced generics, but I have no way to test that. FYI, XE4 can compile the tests, after removing the inline var statements. Edited January 27, 2022 by Edwin Yip Share this post Link to post
Joseph MItzen 252 Posted January 27, 2022 Doesn't using classes, abstract classes, generics, hidden properties, class constructors, and of course a typecast of sorts anyway for such a simple problem make you want to throw up your hands in despair and swear to never use static typing again? 2 Share this post Link to post
Stefan Glienke 2019 Posted January 27, 2022 9 hours ago, Joseph MItzen said: never use static typing again The issue is not static typing but the ridiculous amount of ceremony required. 1 Share this post Link to post
Erik@Grijjy 123 Posted January 27, 2022 13 hours ago, Edwin Yip said: FYI, XE4 can compile the tests, after removing the inline var statements. Great! Thanks for testing! Share this post Link to post
Edwin Yip 154 Posted January 28, 2022 This is a good technique, but speaking of generic collections, honestly, after using it in several projects, now except for dictionaries, I've gone back to using the non-generic collections from `System.Contnrs`. In order to make it comfortable using the object lists, all I have to do is to override the `Items` property for a specific class, like this: TAddressList = class(TEyObjectList) // TEyObjectList is an enhanced descendent of TObjectList protected function GetItem(Index: Integer): TAddress; procedure SetItem(Index: Integer; aObj: TAddress); public property Items[Index: Integer]: TAddress read GetItem write SetItem; default; end; Making such derived class is a 10-seconds operation with the help of MMX's class templates and Find-and-Replace. Maybe it's just me, but I really don't find too much cases where a `IList` can serve both the objects and string items (for example) well. Maybe for library writers it's good to have a collection to rule them all, but for users of libraries, specificity is a good thing. Another burden is the lame support of generics of the Delphi compiler... Share this post Link to post
David Schwartz 430 Posted January 29, 2022 (edited) On 1/27/2022 at 9:43 PM, Edwin Yip said: This is a good technique, but speaking of generic collections, honestly, after using it in several projects, now except for dictionaries, I've gone back to using the non-generic collections from `System.Contnrs`. In order to make it comfortable using the object lists, all I have to do is to override the `Items` property for a specific class, like this: TAddressList = class(TEyObjectList) // TEyObjectList is an enhanced descendent of TObjectList protected function GetItem(Index: Integer): TAddress; procedure SetItem(Index: Integer; aObj: TAddress); public property Items[Index: Integer]: TAddress read GetItem write SetItem; default; end; Making such derived class is a 10-seconds operation with the help of MMX's class templates and Find-and-Replace. Maybe it's just me, but I really don't find too much cases where a `IList` can serve both the objects and string items (for example) well. Maybe for library writers it's good to have a collection to rule them all, but for users of libraries, specificity is a good thing. Another burden is the lame support of generics of the Delphi compiler... You're saying this is simpler than saying something like: var TAddressList = TList<TAddress>; // or var TAddressList = TList<TEyObjectList>; I don't understand the last paragraph at all. What are you referring to? Edited January 29, 2022 by David Schwartz Share this post Link to post
Edwin Yip 154 Posted January 29, 2022 (edited) 1 hour ago, David Schwartz said: You're saying this is simpler than saying something like: var TAddressList = TList<TAddress>; // or var TAddressList = TList<TEyObjectList>; I don't understand the last paragraph at all. What are you referring to? Hi David, for simple use cases, the generic collections are simpler. However, please consider situations where you need to add a bunch of methods such as CopyFromLeft, CopyFromRight, Each, GetPropertyValuesByNameAsCSV, and so on, to the enhanced list class. In this case I have two options, and I'll outline the pros and cons: Inherit from the generic TObjectList<> Pros: Can eliminate some of the typecasting, but not all, without techniques such as Curiously Recurring Generic Pattern the OP is presenting. Cons: From time to time, the compiler emits strange internal error and I'll have to restart the IDE. bloated EXE sizes. Inherit from the non-generic TObjectList Pros: No strange compiler errors. No strange bloated EXE sizes. Cons: In order to eliminate all typecasting for the list users, you'll have to make a derived class for each object. That being said, I might be wrong, but my gut feeling is using the non-generic TList/TObjectList is more comfortable, especially without the need to constantly restart the IDE. By IList, sorry for not being clearer, it's from Spring4D which is excellent, so it just occurred to me. I should have said TList<>. Edited January 29, 2022 by Edwin Yip 1 Share this post Link to post
Stano 143 Posted January 29, 2022 (edited) Quote From time to time, the compiler emits strange internal error and I'll have to restart the IDE. It happens to me too. In that case, I run the build. I have small projects. Suddenly everything is fine. Up to two I don't know if this is your case either. Edited January 29, 2022 by Stano Share this post Link to post