As part of my planning to build a new Object Pascal Language/Compiler that aims to be a better C++ replacement than Object Pascal is currently, I have been thinking a lot about what exactly is wrong and missing in the languages. The goal of this new language I am creating is to create parity between the two, fixing the problems with both while also paying homage to the legacy and history of both languages. Here’s my assessment. This assessment may be edited as I continue to organize my thoughts around the matter. Lets start with the least mature of the languages, Object Pascal…
Object Pascal
Object Pascal has suffered stagnation, primarily due to neglect. There hasn’t been good, enthusiastic stewardship of the language over the years. There have been promises and baby steps in the new directions, but the promises have been half-met at best and the newest language features still feel very “beta”. So let’s examine some of these newer, promised, language features and what went horribly wrong with them:
Records, I mean, Classes, I mean, Records, I mean Objects
Object pascal cannot decide whether to encourage you to use classes or records for many programming tasks. The two constructs, therefore, have features that are implemented specifically for each, but neither construct has all the features you’d want. The primary decision you make when choosing whether to use classes or records is how you want your memory to be managed. If you want the object to be on the stack or cast from foreign memory, you use a record, if you want it to be on the heap, you generally use a class. But in a perfect world, these two memory models should not preclude or exclude whatever feature set you’re allowed to build into your designs. The C++ equivalent of a class does not force the programmer into one memory model vs. another.
Many Delphi programmers may consider the use of classes to be the more modern way of thinking and consider records to be obsolete. But they are still used very widely by anyone who wants a quick way to map memory into a stucture, which happens an awful lot in world. If you want to work with block-formatted files, you use records… if you want to represent the pixels in a bitmap, you use records… if you want to represent the samples of a WAV file, you use records… or the header to a UDP/TCP based protocol or ethernet stack… records again. The record-style manner of working with memory is very relevant in computing today and will likely remain so to anyone who works at all with low-level stuff and records, as a language construct, shouldn’t be neglected. Can classes even be packed? I don’t think they can be.
Recent versions of Delphi, have added lots of new features to records, including operator overloading (which has problems which I’ll get into later), private/public declarations, methods, private types, and statics… essentially making records have many/most of the features of classes…. Yet operator overloading never made it into classes, and records never got inheritance, polymorphism, interface support, generics, and custom constructors/destructors.
Interfaces
Interfaces were introduced into Delphi a long long time ago, primarily for the purpose of supporting COM objects. The biggest problem with them is that, these days, software architects want to use interfaces to identify aspects and constraints within objects outside the COM context, yet Delphi still basically thinks of interfaces entirely in the COM context. It requires that objects using interfaces implement IUnknown and in doing so, marries you into a completely different reference-counted object lifetime management paradigm which can be a pain in the ass at times. Classes support interfaces, but records don’t, and this limits a lot of design/architecture possibilities which I’ll get into in a bit.
Generics
Generics were implemented very half-assed in Delphi and possibly even more half-assed in Free Pascal. They are sorta there, but you’ll very quickly run into lots of scenarios where you simply can’t build a generic to do what you want/need. In fact, I rarely get to use them outside of just building generic lists, which basically saves me a cast from an untyped pointer and performs a bit of extra type checking for me, not that big of a deal, but if it helps me eliminate a run-time error, I will use it. Since records don’t support inheritance, that means also that records don’t support interfaces, and if records don’t support interfaces, then they can’t be used to express constraints in generic design patterns and, to be honest, I’m not sure how the hell to use records in generics, but I have seen it done with lists… but in those scenarios, it is like we’re only basically saving ourselves a cast or two… again… not all that useful.
A design pattern that I really wanted to be able to build but couldn’t in Delphi required me to use generic records. Imagine for a moment a Bitmap object where the memory representation of each pixel is decided by a type parameter. Wouldn’t it be convenient to be able to build a bitmap by specifying a pixel type along with it, and then simply cast a bit of memory as the appropriate pixel types to quickly allow accurate manipulation of the image? The outer object could be responsible for implementing your line drawingm algorithms, etc, while not having to worry about the pixel format. Most Delphi implementations of this kind of format handling involve case statements and custom memory read/writes depending on the format… But in the ideal world you could call TMyBitmap<TRGBAPixel>.create to build a bitmap of 32-bit alpha pixels, or TMyBitmap<TXRGBPixel>.create to represent HDR pixels, or TMyBitmap<TA64R64G64B64> for a super-accurate pixel format using 64-bit floats for each color channel. Bear in mind that keeping memory formats in-tact and packed for these pixel types is very important, particularly if you’re going to use SSE optimizations. I actually have a record type that I built that uses floating point RGBA values and performs color math using SSE registers, but I couldn’t use it from the outer generic because I am not allowed to define constraints that are common to all the pixel types, nor inherit the pixel types from a common ancestror.
It is quite nice to be able to load the whole record into an SSE register and allow the processor to operate on the R,G,B, and A simultaneously with SSE instructions, but this design pattern is not allowed in Delphi, while it is totally possible with C++.
Another design pattern that I’m unable to create: Sound files with variable sample formats. I work with sounds that are sometimes 16-bit, 24-bit, 32-bit floating point, or even 64-bit floating point at times. And it would be really great to be able to pass a sample format along with an outer object and be able to work with the samples, adding, subtracting, multiplying, converting between formats using a generic type. with operator overloading. I also implement my sample types as records and manipulate them with SSE instructions… this is nice because you can actually operate on a 64-bit stereo sound sample’s left and right channels with a single instruction using SSE. This design pattern, once again, is totally possible with C++, but not with Delphi because records do not support inheritance and therefore don’t support Interfaces and constraints, yet classes don’t support operator overloading.
So I guess the limitation in a nutshell can be expressed with the following question: “Operator overloading or Generics? Pick one, but not both. “
Operator Overloading
Many programmers don’t care much about operator overloading, but I’ll tell you one thing, they are really quite nice to have when you’re dealing with scientific applications. If you don’t have operator overloading, any code that you write for dealing with complex numbers, imaginary numbers, vectors, and matrices, becomes rather ugly rather quickly. Continuing with the themes of my earlier examples, they are also useful for pixel formats, sound sample formats, GPIO measurements, and any objects you can think of that are mathematical in nature.
Operator overloading in Delphi is very welcomed, but of course, they never really finished it. It only works for records, which means that you have to give up a lot of features that are present in classes in order to use it. One thing that is particularly annoying to me is that you cannot forward-declare records which makes it impossible to have circular references between types for Implicit() and Explicit() casts… at least I couldn’t figure that one out. These problems don’t exist in C++.
Headers and Partials
Since Delphi puts the “interface” and “implementation” sections in the same file, you don’t have the organizational (or arguably dis-organizational) freedom to put them in different files. 99% of the time, this doesn’t bother me actually… however, if I were someone who thought in more of the C/C++ paradigm, it would likely drive me bonkers. If you, for example, want to implement a DLL and then expose interfaces to objects/records/etc for that DLL to a 3rd party, you’d basically be stuck copying/pasting that code into a new file unless you rigged it up with {$INCLUDE} directives… which are thankfully not used very often in Delphi.
(ED/UPDATE: The most recent editions of Delphi have added “record helpers” and “class helpers” which can be used to add functionality to existing classes without formally extending them, however they cannot add new members to the types, only functions/procedures… so if you’re totally current on your Delphi updates/version, the following section is not entirely accurate)
Futhermore, you can’t declare “partial” classes, which come in handy in C# coding particularly when dealing with forms. . Sometimes, you might have classes with enough aspects and that are complex enough that you would want to segregate different parts of the declaration/implementation into multiple files, e.g. maybe parts are platform specific or scenario specific and you want to implement those parts in separate files for the sake of organization… but you just don’t have as many organizational options in Delphi. On the other hand, this lack of organization freedom is rather convenient, because anyone who’s ever taken on someone else’s sloppy C++ code to find functions declared in headers, but implemented in C-files that are seemingly randomly chosen will appreciate that Delphi is a bit more self organizing and predictable.
Volatiles and Memory Barriers
Delphi doesn’t really need Volatiles or Memory Barriers because it doesn’t optimize to the level that C/C++ code optimizes. But this also means that if you want Delphi to optimize to that level, it is probably pretty important to have Volatile declarations and Memory Barriers (UPDATE: A [volatile] attribute was quietly slipped into the newest versions of delphi, but AFAIK it likely only affects the iOS/Android compilers, and its effects are not really well documented… see this white paper for more).
Memory barriers are compiler and architecturally specific. It is important for the compiler to comply with the memory barrier placement and not reorder instructions on either side of the barrier, and it is also important for the CPU to flush its own cached memory in some cases. On the intel platform, I believe it is more important for the compiler to respect the fence than to explicitly call the CPU-level instructions due to the way Intel ensures multi-core cached memory access… but if you want to get into the weeds, I intend to look at a few case studies at the low level, such as this one, and I’ll likely update this post when I do.
It is getting a bit wishy washy with all the new target platforms, but in general, and historically, in Delphi, all non-local variables are considered to be “volatile” in a sense. Access to outside variables and objects is therefore virtually unoptimized. Delphi will never come close to C++ speed unless something is done about this. The 64-bit Delphi compiler doesn’t even do basic dead-code elimination… again we’ve been sold something that is IMO half-complete.
The volatile keyword in C++ is typically used for processor registers or anything that might be changed via DMA memory access from another CPU or process or thread. It is very important in C++ to get this right for your applications to function properly with optimization turned up to the max. Optimized code would potentially not run as you would expect if you were to… for example… build a while loop that waits on a memory address to be externally changed. The C++ compiler might eliminate the code altogether because it deemed it useless to be waiting on a memory address that should not change. Again, this isn’t a problem in Delphi only because non-local memory is always considered volatile… and therefore it is slow. You’ll find a number of people debating the merits and performance benchmarks of volatiles vs. memory barriers on the internet. If I have an interest in keeping things simple, I default to the use of volatile in C. The performance differences are merely a few cycles at times.
First-class keywords, that never should have been first-class
Message should have never been introduced into Delphi. Its introduction was admittedly rather convenient, however, it is a feature that was intended very specifically for Windows and married the language to windows. Now, if they were to implement the message keyword in a manner that was just as convenient but more abstract, we could use something similar to the message keyword for processing alternative/custom message piping systems, including firemonkey, OSX, and Linux. On a different note, it would also be rather nice to replace TApplication’s message dispatcher with your own… or can you?
Published is a keyword that is welcomed to an extent, but when you really think about it… it doesn’t make any sense that it is there as it relates specifically to interactions with an IDE. C# seems to do just fine without it. But I guess it is a semantic that can be argued.
Initialization and Finalization are unreliable
I suppose this isn’t a language criticism so much as an implementation issue. I love that Delphi has Initialization and Finalization sections, but if you build an app that uses lots of Initialization and Finalization sections, you’ll eventually realize that the order in which Initialization and Finalization are called is completely unreliable. In order to combat this, I had to build a completely custom Initialization Finalization processor that I call TOrderlyInitializer. It is the only way to ensure that services that are started by an Initialization section are not prematurely shutdown before all the units that depend on those services are done with them. I call it a bug, but Embarcadero probably doesn’t even think so. Regardless as I build my own compiler, I want to note this as something that I want to fix/get right.
Multiple Inheritance
20 years ago, most programmers couldn’t think of a good use for multiple inheritance (most programmers were still stuck on COBOL and PICBASIC programs back then), but in today’s work, Multiple-inheritance is almost essential for building aspect oriented designs. There’s a push in the industry to think beyond object-oriented and into aspect-oriented architectures which favor multiple inheritance heavily.
If you’re working in an environment that does not support multiple inheritance, your other options are to expose interfaces of the aspects (which basically means that you’d have additional classification abilities, but no ability to inherit implementations of those aspects without copy-pasting code) or you can build your objects using the composite design pattern where you have a single object type with multiple components representing the various aspects (This is how the Unity3D engine works, also Gamebryo).
But having true multiple inheritance allows for aspect-oriented programming in the most first-class way possible. Delphi lacks it. The closest you can get to multiple inheritance in Delphi is to use the implements keyword (which 90% of Delphi programmers have either forgotten or never heard of). Using it you can declare that an object implements a particular interface, yet delegate the actual code that implements that interface to another object. It is a bit of a kludge and true multiple inheritance would be very useful in my book. Multiple inheritance isn’t something I’ve needed very often, but there have been times when I really wished I had it in Delphi. How else can you extend a whole bunch of heterogeneous classes with some common code very quickly?
C++
C++ suffers not from neglect (as Object Pascal has suffered), but from stubborn standards steering committees that don’t seem to think that there’s anything wrong with the language. To me C++ is archaic. You wouldn’t want a doctor performing brain surgery on you in a hospital without anesthesia, sterile instruments, or antibiotics would you? The C++ steering committee is like a gang of doctors who would rather tell you to bite down on something while they get out their bone saws than give you proper, modern medical treatment. C++ may be remembered as the language of the 20th century, but it is definitely not the language of the 21st century. So what’s wrong with C++?
Strings
The obvious. Everyone knows that C++ lacks any kind of standardization of string types and it is super-duper annoying at times to have libraries from 15 different companies all expecting different string representations. I still see people writing a lot of code with archaic functions like strcat() and sprintf(). Anytime strings are involved, C++ code becomes messy and unreadable very quickly. Much of this is largely the fault of the programmers who would rather join the group doctors from the dark ages than build any kind of standard framework for strings before just diving in with these old methods.
Include
The #include keyword is basically single-handedly responsible for murdering the productivity of C++ programmers around the world. I would guesstimate that billions and billions of dollars have been wasted waiting for C++ programs to build, and it is largely the fault of the #include keyword. Include is different from Delphi’s “uses” and C#’s “using” keywords in that, in the “uses/using” paradigm, the files being used are considered to be precompiled. “Include” does not allow the included file to be precompiled (without essentially tricking the compiler using precompiled headers). Things that happen in the file before the #include can affect things that happen in the included file (essentially the all included files are first merged into one large file before being compiled).
As a result of this silly and rarely useful rule, the same file often gets recompiled hundreds or even thousands of times in a single build operation. If you have hundreds of files, you get to waste lots of time by the company water cooler or check up on your facebook buddies. I have one embedded C app, designed for a device that has a measily 96k of RAM that takes 5+ minutes to compile with all 8 VCores of my i7 machine pegged at 100%. This is for a target that has a measily 96k of RAM and 512K of flash! This is unacceptable. This has always been unacceptable. This will always be unacceptable and it is another reason why C++ can’t be the language of the 21st century.
Try..Finally
Many people were shocked and disappointed when the most recent revisions to the C++ standard came out and didn’t support the “finally” keyword. The old bone-saw toting doctors rationalized that having a “finally” keyword was not consistent with the C++ paradigm and therefore should not be included because C++ encourages life-time management of objects through stack allocation and smart-pointers and the whatnot. But to that, I say, that regardless of your preferred paradigm, you simply cannot deny that having a “try..finally” ability is sometimes really a great thing to have. As a result of this stubbornness to accept change, you instead have programmers looking like assholes for putting “goto” blocks in their code when shit needs to get cleaned up, and you get Jr. programmers fucking up their refactorings when they forget to jump to a cleanup section in lieu of just calling “return”. Having a “finally” block would allow you to, for example, call “return” but still guarantee the execution of “finally” before actually returning. It is convenient, it makes code more readable (C++ needs lots of help in the “readability” category), and it would definitely save companies who write C++ code a buck or two when it comes time to chase down and fix bugs.
Barrier Keywords
What I call “barrier keywords” might be known by other names in other circles. A barrier keyword, is simply a keyword that helps the compiler understand and zero in on syntax errors. For example, Delphi and Java use the word “function” to denote the start of a function and the compiler knows that if it hits this keyword yet the previous function wasn’t closed with a squiggly “}” or “end” declaration, that someone, somewhere typed something wrong. C++ doesn’t have many keywords at all, and we have all bumped into those occasions where a missing squiggly produces compiler error messages that are 15-miles out of the ballpark of where the actual syntax problem is. C++ starts function declarations with a type name or “void” in most cases. If that type is undefined, many compilers get ultra confused and don’t know if you’re trying to define a new variable, a new type, use a macro, or you just expect this undefined type to be a default “int”… and as a result the compiler’s ability to give you meaningful syntax error feedback is limited severely by the language design.
Add the fact that the type names are case-sensitive, and simply forgetting to capitalize a single letter causes the compiler to send you around on wild hunt to find a polar bear in the jungle and you sometimes find yourself having a bad afternoon. There are no polar bears in the jungle… a good compiler for a properly designed language should be able to tell you that.
Case Sensitivity
The trend in computer languages has been to be case-sensitive for a long time now. Java, JavaScript, C#, C++ are all case-sensitive languages. This, in my opinion, is one of the most boneheaded design trends to come out of a group of people who are supposed to be “smart”. You can argue semantics and personal preference to the ends of the earth, but I’ll always win any debate by asking this one question that those of you who think that case-sensitivity is a good thing for language design simply cannot answer:
“How does complaining when I type ‘int32’ instead of ‘Int32’ make your compiler a better, more-productive, and useful ‘tool’ than if you simply just let it slide with a warning or hint?”
To me, computer languages are tools, not rules. You can evaluate the usefulness of an everyday household tool with some fairly objective and quantifiable metrics and chances are, people who have nail-guns are going to be more productive than people who use hammers.
Similar things can be applied to computer languages: Languages that help you get the right answer will yield higher productivity than compilers that simply complain when you get it wrong. On a completely different subject — file systems: they shouldn’t be case sensitive either…. seriously why does my grandma care if she named her file “recipies.txt” or “Recipies.txt” and even as an engineer/power-user, why on earth would I want to have the privilege of having both?
Delegate Methods
I’m a bit fuzzy on the C++ challenges with this one, but I simply know that last time I tried to do this, I simply couldn’t figure it out and none of the other senior engineers I talked to had any clue either.
In Delphi you can easily create method hooks from one object to another. In C#, it is a little weirder, in that you have to create a “delegate method” object, but it is still possible. C++ AFAIK has no standard structure or object or method for doing anything of this sort with any kind of elegance. You can’t even hook into methods from objects that inherit from the object they’re trying to hook into, let alone a foreign object. The only time building a method hook is at-all permitted is within the object that is doing the hooking… but how useful is that? C++ needs some kind of delegate method standard.
I’ve actually never seen anyone do a delegate method in C++. One guy I know said he implemented it, but it was ugly and messy. Most of the C++ guys I’ve talked to about this say they implement message-passing architectures instead… but… still… ? It is like the old docs have never heard of such techniques which are at the heart of what makes WinForms and the VCL possible at all.
(Update: I recently went on the interwebs and stack overflow trying to find a good answer for delegates in C++… I got 6 different answers with noted caveats, and not exactly what I’d call wide acclaim for any of them.)
Properties
C++ even with the latest standards revision, doesn’t formally recognize the “property” paradigm and that is a paradigm that I absolutely love in C# and Delphi. I’m sure the argument from the C++ school of thought is simply that calling a function is a more clear expression of intent than burying the intent inside something that looks like a variable… but to them I simply say, screw, intent… properties are awesome. And array/indexed properties?… More awesome.
initialization/finalization
C++ doesn’t have initialization or finalization sections. I am rather fond of them, because they allow me to simply “use” a file and execute some code without having to mess around with the main() function. I would welcome them in C++.
Namespaces
Whereas C++, unlike C, allows you to define separate namespaces if you’re meticulous about it, you can still bump into situations where you merge code with a 2nd or 3rd party and you have ugly namespace conflicts that are difficult to resolve. I once spent several days hunting down a compilation problem to find that a new Windows header I received from Microsoft had defined a macro that shared the name of one of the parameters in a function declaration. This really confused the compiler in one of those super ugly ways where the error message I received told me virtually nothing about what was actually wrong.
RTTI
C++ wasn’t originally built with Run-Time Type Information in mind, although most compilers support it these days and enable it by default. But there’s just something not terribly pretty about using a dynamic_cast<> call when in Delphi you can simply use the “as” operator. Delphi has the “as” and “is” operator as first-class members of the language and they make things much more readable.
Virtual Constructors
I really dislike the syntax of C++ constructors in general, but I’m not going to spend too much time arguing semantics. However, for whatever reason, in the C++ paradigm of thinking, constructors are simply never virtual. I learned very quickly in my early days programming Delphi that having virtual constructors is actually quite a useful feature to have. For example, I could build a class factory that has pointers to classes with virtual constructors along with an ID or string, then I could implement the factory by simply looking up the object type in the list and calling “create” on it (assuming that “create” is virtual) and it would create the appropriate instance of that particular class. Delphi’s VCL, for the longest time did not define any virtual constructors (even though it has always been possible to declare and use them), possibly simply out of paying homage to the old paradigm, but more recent VCLs have started to see virtual constructors gradually phased in for important things like TControl etc.
If you have any other gripes about the C++ or Delphi/Object Pascal/Free Pascal language. Please comment. I want this post to be a collection point of sorts that will eventually be put into my “todo” list for this new compiler I’m building. Also, let me know if I got anything wrong. I don’t know everything about everything and maybe I made some incorrect assumptions here and there.
I disagree on several points, but I’d first like to hear your full argument about module initialization and finalization. The order is not only knowable in advance, it’s rigorously enforced by the compiler. So please give a demo of this supposed unpredictable behaviour?
I quote:
“if you build an app that uses lots of Initialization and Finalization sections, you’ll eventually realize that the order in which Initialization and Finalization are called is completely unreliable.”.
Let me rewrite that for you:
“If you build an app that uses lots of Initialization and Finalization sections, you’ll eventually realize that the order is completely reliable, and that that order it selected isn’t always the order you wish in your imprecise and unreliable human mind, that it was.”.
Warren
Gosh I can reproduce dozens of examples of this… in fact, I recently found an instance just last week (after having thought I squashed the problem over a year ago).
After going back and forth with a few people, we eventually got the Delphi devs to admit that initialization order, although usually fine, was not guaranteed to be reliable.
Ideally, when you initialize a unit, any units that are dependent on it in its interface uses clause would have to be initialized first. During shutdown, the units would be finalized in reverse order.
You could write a couple dozen apps and think that everything is peachy, but until you write EVERY app possible, you really can’t prove that the initialization/finalization order is 100% reliable.
I worked around the problem by creating a class that I call TOrderlyInitializer. In this class, each of the initialization/finalization sections register function pointers with a singleton along with a dependency list that is independent of the “uses” clause. Units will then only be initialized when all those dependencies are met.
It is quite possible that I use far more initialization/finalization sections than your average developer, so I have exercised this in a whole bunch of ways. I like being able to start and stop certain internal services/threads etc. by simply including them in the uses clause of my project. I have about 130 projects that share common libraries and I like that I don’t have to explicitly start/stop various things in the “main” function like I would have to in C++. Doing things this way ensures elegant startup and elegant shutdown of all my units which may depend on services offered by other units.
I most frequently bump into problems, for example, where I have one unit that uses a background thread from a threadpool service offered in another unit. I have seen a number of cases where the threadpool service was destroyed despite the fact that there were still threads declared in other units waiting to be collected and ultimately stopped/destroyed. I recently had a shutdown bug with an app I was working on where a simple thread that collected and executed low frequency “PeriodicEvents” was still active after the threadpool shutdown (should have been impossible)…. then I also had problems with another simple thread that tracks/empties/logs general purpose statistics on things like # of packets in/out, cycle/idle times for critical processess… etc…
So I’ve definitely seen this happen… definitely. I think it gets confused when a unit uses something which uses another thing which uses another thing…etc…. I would understand if it were a circular reference thing, but I’ll also add that I’m very careful to declare my singletons in the interface section creating the situation where my programs wouldn’t even compile if there were circular dependencies of this nature.
A: Look, we now have 57 computer programming languages that suck! We need to fix this!
B: Alright, boss… let’s do it!
*a few years later*
C: Look, we have 58 computer programming languages that suck! We need to fix this mess!
*yeah… repeat it forever* ^_^
That’s not fair – newer languages target different applications, development styles, and levels of performance in a way that they never did before. The area that the author is talking about is definitely underserved – try to find languages that aren’t garbage collected for example.
Haha, it’s like we’re stuck in a loop of language inception! Kinda makes you wonder if throwing another one into the mix is gonna be the solution or just add to the chaos, huh? ?
As an old school Delphi developer, I’ve been very impressed with C#. I found it fixed many of the warts that were present in Delphi/Object Pascal. While both a positive and negative, the language has advanced with new features and capabilities with new release, unlike Delphi.
hello. did you make compiler like you say?
maybe if new compiler have sse commands like in c++ and have better optimisations better then delphi, I need to use it. but without this functionality, I must switch to c++
Good question about SSE!
Absolutely! Optimizations and SSE are crucial.
“As part of my planning to build a new Object Pascal Language/Compiler that aims to be a better C++ replacement than Object Pascal is currently”
6 years later, any progress?
C++ syntax for advanced, but sometimes common concepts is terrible. You disnt even touch templates… it lacks contraints and its syntax… ugh. Smart pointers? Soo much stuff added, but so few feel like a native resident.
Im guessing its only because of the tooling and libraries that C++ is used instead of another native language like FreePascal.
I did not make any great progress… it is difficult to keep up when you’re just one guy against the world and so many new tools are being created and so many people are demanding you do bigger and newer and better things… I wish I had the time to realize my vision… but there would be like 4 people that would actually use it on the planet if I even DID get around to it.