tag:blogger.com,1999:blog-15321510225056714982024-03-16T02:12:14.944+01:00Delphi sorceryPulling rabbits and other stuff out of the hat with delphi ...Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.comBlogger64125tag:blogger.com,1999:blog-1532151022505671498.post-38343087188782152202021-06-15T00:43:00.000+02:002021-06-15T00:43:02.564+02:00Spring4D 2.0 sneak peek - the evolution of performance<p>To be honest - most of the collections in Spring 1.2 are dead slow - <a href="https://www.idefixpack.de/blog/2016/05/whats-wrong-with-virtual-methods-called-through-an-interface/">especially on Win32</a>. But to be fair, almost nobody complained about that so far. Now that we have <a href="https://delphisorcery.blogspot.com/2021/06/introducing-springbenchmark-port-of.html">some shiny tool</a> to print some numbers here are some simple benchmarks - they show a similar pretty sad picture for almost every method as most of them are virtual in 1.2.</p><p>The benchmark simply measures how long it takes to add n integers to a list with a pre-allocated capacity - so this is just the duration it takes to call and stuff that integer into the internal array (and whatever else is going on) - compiled with 10.4.2 - Win32 release. You can see how hard that return stack buffer trashing hits the performance here.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqKL2gDlKOqdwe6qFFB5jLNYyP92Q3vvNiNN2Rz4EJopyGvz2Gpw5W72ClYmidbXigsGZjEGDZMfc05ARnsRKYBfUIhLfQ5865SQSdSiFz3vgjX_4KEtp7N3lSs60XW9JSEgawdd7hlTDa/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="309" data-original-width="752" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqKL2gDlKOqdwe6qFFB5jLNYyP92Q3vvNiNN2Rz4EJopyGvz2Gpw5W72ClYmidbXigsGZjEGDZMfc05ARnsRKYBfUIhLfQ5865SQSdSiFz3vgjX_4KEtp7N3lSs60XW9JSEgawdd7hlTDa/w640-h262/image.png" width="640" /></a></div><br />The numbers for Win64 are a little less terrible but still far from good:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8uAtQz2tpBkAZP8TXbV5BIRo59ZGjYcHADhs7eEYpXiChjqREaw6NOTkVA1SJeMtLdtShPWHUN8-SpmF8u6SNfN65MgKtviwKZGCiAybI3nYbe-h3Xn4vvbLal5dR6IvkvCD5SzYLGvB8/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="310" data-original-width="756" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8uAtQz2tpBkAZP8TXbV5BIRo59ZGjYcHADhs7eEYpXiChjqREaw6NOTkVA1SJeMtLdtShPWHUN8-SpmF8u6SNfN65MgKtviwKZGCiAybI3nYbe-h3Xn4vvbLal5dR6IvkvCD5SzYLGvB8/w640-h262/image.png" width="640" /></a></div><br /><br /><p></p><p>Now fast forward to the latest development state - Win32:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRMuHZ2czlEjM9EU4L-gckek-jxeIGgwzgSEAVQ9KecA_IasmzyDVrpIxkuLASJU5Ka-A-9oIHTRh2UI3DWPOlI_92RR3q5qesJ0efR_PkvOdGdSoEMgA4ZqdoOyg3D9z7XrG3QD7f6Xmo/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="307" data-original-width="754" height="260" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRMuHZ2czlEjM9EU4L-gckek-jxeIGgwzgSEAVQ9KecA_IasmzyDVrpIxkuLASJU5Ka-A-9oIHTRh2UI3DWPOlI_92RR3q5qesJ0efR_PkvOdGdSoEMgA4ZqdoOyg3D9z7XrG3QD7f6Xmo/w640-h260/image.png" width="640" /></a></div><br />and Win64:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5pyUn3iDBvyJfGzQ7r05LXMMKU6YvY_W0Xxbyb0AHz5RfC58AB0wd-4Qbq6-zUGtGKVaPbdT3MiiSjLfhdIDPVeJCY0Bxf3-WzKaRN9ufFdjnXaOlfIeHGmCObyCZRAr3AFl9H_BV_-xW/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="309" data-original-width="755" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5pyUn3iDBvyJfGzQ7r05LXMMKU6YvY_W0Xxbyb0AHz5RfC58AB0wd-4Qbq6-zUGtGKVaPbdT3MiiSjLfhdIDPVeJCY0Bxf3-WzKaRN9ufFdjnXaOlfIeHGmCObyCZRAr3AFl9H_BV_-xW/w640-h262/image.png" width="640" /></a></div><br /><br /><p></p><p>Before you ask - well - simply adding Spring4D to the binary makes the RTL faster or slower! No, seriously, that was a joke. You can see the effect that Professor Berger mentioned in his talk that I linked in my last blog post. So be careful with these numbers. Simply flipping some bit in your binary can affect completely unrelated code.</p><p>However, the important point here and one that is consistent regardless of the relative numbers is the improvement from 1.2 to 2.0 and this goes for all collections across the board regarding 3 key factors:</p><p>1. speed - there are no virtual methods anymore behind the interfaces, the lists are optimized to perform the fastest way possible especially when there are no OnChanged event handlers attached to them which is the common case and where you want the pure speed.</p><p>2. binary size - as you know generics can cause quite a huge chunk of binary code because for every specialization the code is basically duplicated even for binary identical types including their RTTI. In 2.0 all RTTI for the implementing classes which you will never touch anyway is turned off and with some trickery, many generic specializations are folded and forced into the Spring.Collections.dcu to not <a href="https://quality.embarcadero.com/browse/RSP-18080">pollute each and every dcu</a> you compile a list or dictionary into. And it does not only shrink your binary, but also speeds up your compilation as the compiler simply has less work to do - no generating code (RTTI and executable code) into dcu which the linker later has to go through and eliminate duplicates.</p><p>3. instance size - while it certainly does not matter if a list instance itself is 58byte or 80byte when you store thousands of integers or objects in them it can make a difference if you have many of those lists for example in every data object and each of them only holds a handful of items. Due to field reordering and implementing the interfaces on one class instead of on multiple ones this saves a few bytes for every instance as those interfaces that inherit from each other such as IList<T> inherits from IEnumerable<T> those share the same IMT slot in every object instead of occupying each their own. On 64bit this saving is even bigger due to the doubled size for pointers as you can imagine.</p><p><br /></p><p>2.0 will provide the perfect balance between the first two points - providing the best speed while keeping the binary bloat low and providing a rich and powerful API as you already know and love it including some valuable additions.</p><p><br /></p><p>While the development of 2.0 is still going on it is close to completion - currently, I am working on finalizing the refactoring of our dictionary which as you might know was the last collection type that was simply a wrapper around the RTL in 1.2. But more about that in the next article.</p><p>For now just a benchmark of different dictionary lookups - the match rate is 100% in these benchmarks which means all lookups are successful - yup, that's roughly a 4-7 times improvement over the RTL!</p><p></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhleI07eFdi1JF5RMfK4ciUOOixli3SVGiZTBo8VyduQMaXemArAjQEH6PkPrAjiqN8I3MTARXG6pNJAPv1e3j407TRpeuNnOfNFC_YyMxghNI06fjFzilMaAauTZJaakM1qEtQ0STvsBIO/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="530" data-original-width="793" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhleI07eFdi1JF5RMfK4ciUOOixli3SVGiZTBo8VyduQMaXemArAjQEH6PkPrAjiqN8I3MTARXG6pNJAPv1e3j407TRpeuNnOfNFC_YyMxghNI06fjFzilMaAauTZJaakM1qEtQ0STvsBIO/w640-h428/image.png" width="640" /></a></div><br /><br /></div><br /><br /><p></p>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com10tag:blogger.com,1999:blog-1532151022505671498.post-24209827358867898342021-06-14T07:58:00.002+02:002021-06-14T07:58:46.215+02:00Introducing Spring.Benchmark - a port of Google benchmark<p>Some while ago I <a href="https://en.delphipraxis.net/topic/4802-queryperformancecounter-precision/?do=findComment&comment=41944">expressed the wish</a> for someone to create a wrapper for the famous <a href="https://github.com/google/benchmark">Google benchmark</a> library. It is a library to write so-called <a href="https://stackoverflow.com/a/2842707/587106">microbenchmarks</a>. Microbenchmarks can help to tune small parts of code such as functions or classes. While you can certainly measure code by simply using QueryPerformanceCounter or TStopwatch there is more to a microbenchmark than simply running your code a couple times and stopping the time.</p><p>Usually, such libraries make sure the code you want to measure has a warm-up phase before it actually gets measured. And also apart from simply printing some duration that it took a good library can provide more information such as running the code multiple times with different parameters and calculate meaningful statistics for example an estimated <a href="https://www.youtube.com/watch?v=__vX2sjlpXU">efficiency of the measured code</a>.</p><p>If you are a regular visitor of <a href="https://en.delphipraxis.net/">Delphi-PRAXiS</a> you might have seen several threads where the performance of different functions and algorithms have been discussed. Most of them have the more simple character of measuring the code of a couple of runs with a stopwatch and often don't do any statement about their asymptotic behavior.</p><p>Before we look into what <a href="https://github.com/spring4d/benchmark">Spring.Benchmark</a> is and offers a little disclaimer: micro benchmarking is something you don't do constantly and you need to be aware that it is one of the many tools at our disposal. Also, be aware of the traps that microbenchmarks hold and take its results with a grain of salt. For more information, I really suggest you watch <a href="https://www.youtube.com/watch?v=koTf7u0v41o">this interesting and entertaining talk</a> from CppCon 2020.</p><p>At the bottom of this post, I will put some more video links if you want to know more about this subject.</p><p><br /></p><p><a href="https://github.com/spring4d/benchmark">Spring.Benchmark</a> is a standalone project which does not require the Spring library but is one of multiple projects planned under the Spring4D name. It is a very accurate port of the C++ code from Google Benchmark and thus will be familiar to those that might have used the C++ library before.</p><p>But now let's dive into benchmarking some code. We will measure sorting and compare TArray.Sort from the RTL which uses plain QuickSort internally to the implementation in Spring which uses a hybrid algorithm called <a href="https://en.wikipedia.org/wiki/Introsort">Introsort </a>which uses a combination of different sorting algorithms. While it's certainly interesting to see the difference between those two I want to show you the features of Spring.Benchmark with this example.</p><p>I will run all tests with Delphi 10.4.2 and use the Win32 platform with Release config. The tests will run on an i5-3570K at 3.40GHz.</p><p>Here is the complete listing of our little benchmark program:</p>
<pre><code class="delphi">program QuickSortBenchmark;
{$APPTYPE CONSOLE}
uses
Generics.Collections,
Generics.Defaults,
Spring,
Spring.Benchmark;
type
TSortProc = procedure (var Values: array of Integer);
procedure Sort(const state: TState; const sort: TSortProc);
var
n, i: Integer;
numbers: TArray<Integer>;
begin
n := state[0];
SetLength(numbers, n);
for var _ in state do
begin
state.PauseTiming;
for i := 0 to High(numbers) do
numbers[i] := Random(MaxInt);
state.ResumeTiming;
sort(numbers);
end;
state.SetItemsProcessed(n * state.Iterations);
end;
procedure QuickSort(const state: TState);
begin
Sort(state, Generics.Collections.TArray.Sort<Integer>);
end;
procedure IntroSort(const state: TState);
begin
Sort(state, Spring.TArray.Sort<Integer>);
end;
begin
Benchmark(QuickSort, 'quicksort')
.RangeMultiplier(10)
.Range(10, 100000);
Benchmark(IntroSort, 'introsort')
.RangeMultiplier(10)
.Range(10, 100000);
Benchmark_Main();
end.</code></pre>
<p>The library consists of just one unit: Spring.Benchmark.pas. Put that into your uses and you are ready to go.</p><p>Every benchmark procedure has to have the signature like our procedures QuickSort and IntroSort have, they take a single parameter of type TState. This variable carries the state of the current benchmark run. Since we have two cases with exactly the same code we put that into our Sort procedure which takes a callback that gets either passed the RTL or the Spring TArray.Sort method.</p><p>Now let's take a closer look at our actual benchmark. First, we want to do some setup - in this benchmark, we will do the setup every time it is being run. It depends on the nature of the benchmarked code if that needs to be done every time or if you can do this once and then run the benchmark multiple times. Our state variable carries amongst many other things the parameters for the test in our case we have one for the number of elements we want to test with. We can access those parameters via the square brackets and their index.</p><p>Now for the actual benchmark loop - this is done with a for-in loop over our state variable. I am using the inline variable syntax here, the _ is a common notation for a variable whose value is actually of no interest. In fact the type of this variable - its type is TState.TValue in case you cannot use the inline variable syntax - is an empty record with no fields whatsoever. This interestingly allows for quite some optimized code being emitted for this loop - especially in 10.4, where due to using the new Finalize operator the end of the loop is not in the way of the loop body code.</p><p>Inside the loop, we do two things: we initialize the array with random numbers every time and then we call Sort. Doing the for-in loop over the state variable automatically starts the internal clock to measure the time. However, since we don't want to include the randomization of our array we wrap this loop inside PauseTiming and ResumeTiming calls.</p><p>The library itself decides how many iterations it will do - it will perform some startup code until its duration stabilize and then starts the iterations that it measures.</p><p>Let's now look at the last part of the code which is the setup. It always starts with a call to Benchmark where you pass a procedure that takes a TState parameter and a name for this benchmark to identify it when displaying or when filtering. There are several methods to be called to set up every benchmark. We are using the Range method to provide several values in a convenient way. In combination with RangeMultiplier, it determines which parameter values to call the benchmark with - in our case the benchmark will be called with 10, 100, 1000, 10000, and 100000. You can find more about all the possibilities in the documentation.</p><p>Let's run the project and see what it shows:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirejzjMkdCGpSfXaQnybJfgAO5M87i96gkcTzpFni-9Slx2yEAPmJGbMH6v-4QwY78UXRFbON-vLkPgsI-C1xuCX6rlI9Y6ikYGV1BJzk8-I6R7Czb05cFPIfYDX7nZ4CiALPXJswS92CX/" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="353" data-original-width="885" height="255" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirejzjMkdCGpSfXaQnybJfgAO5M87i96gkcTzpFni-9Slx2yEAPmJGbMH6v-4QwY78UXRFbON-vLkPgsI-C1xuCX6rlI9Y6ikYGV1BJzk8-I6R7Czb05cFPIfYDX7nZ4CiALPXJswS92CX/w640-h255/image.png" width="640" /></a></div><br /><p></p><p>You can see a nicely printed summary of our multiple runs with their timings and the actual number of iterations the library ran the code for. In our code we added the value for a so-called counter which also helps to evaluate the code we just benchmarked - we counted the items that we processed by our sorting by multiplying the numbers in the array by the number of iterations we ran the code. The library code does all the necessary calculations to show that we processed several million items per second with our sorting.</p><p>There are many more features such as writing the statistics to csv or json format, running your benchmark code in multiple threads to also measure its performance when running in parallel.</p><p>The library requires at least Delphi XE7 or FPC 3.2.0 (at least that is what I tested it with) and runs on Windows (Delphi and FPC) and Linux (only tested with Delphi 10.4). Delphi support for macOS, iOS and Android is being worked on. </p><p><br /></p><p>Earlier I promised some more interesting videos to watch and here are a few:</p><p>An interesting and very entertaining <a href="https://www.youtube.com/watch?v=FJJTYQYB1JQ">talk from Andrei Alexandrescu from CppCon 2019</a>.</p><p>Two talks from Chandler Carruth about <a href="https://www.youtube.com/watch?v=fHNmRkzxHWs">efficiency with algorithms</a> and about <a href="https://www.youtube.com/watch?v=nXaxk27zwlk">benchmarks</a>.</p><p><br /></p>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com3tag:blogger.com,1999:blog-1532151022505671498.post-82814525588233682882021-04-28T21:55:00.005+02:002023-11-07T21:11:14.453+01:00TestInsight 1.2 released<p>Finally! Thanks to <a href="https://landgraf.dev/en/">Wagner Landgraf</a> who implemented this feature you can now enjoy the hierarchical view of your unit tests just like you know it from the good ol' DUnit GUI.</p><p>Here is how it looks in TestInsight in the "by fixtures" view - remember you can toggle between the "by type" view and this one at any time just by clicking that notepad icon.</p><p></p><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1w7GsJqPvdBrZkqhpwO3DNZ39tquftdriAJfAqzE2i0e7f6cFhkvPJn74L2HkNEEMq9rD7tGKF2kp3_geEL-H1ddrSdy6XqE14whXDiaWFhQ25zutsi8qRu45c5GdNGoh35IRnFyLeeKB/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="677" data-original-width="414" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1w7GsJqPvdBrZkqhpwO3DNZ39tquftdriAJfAqzE2i0e7f6cFhkvPJn74L2HkNEEMq9rD7tGKF2kp3_geEL-H1ddrSdy6XqE14whXDiaWFhQ25zutsi8qRu45c5GdNGoh35IRnFyLeeKB/s16000/image.png" /></a></div><br /><div style="text-align: left;">Not only does it look nicer than the old DUnit GUI, but it also executes the tests way faster and allows you to directly navigate to the source of any of your tests with a simple double click on them, now even if you inherited that test from a base class - you will always end up in the correct method. And all that directly from within the IDE as you know it.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Wagner also implemented support for running DUnit tests with TestInsight on pas2js - I wonder why that might be... 😏</div><div style="text-align: left;"><br /></div><div style="text-align: left;">We also improved the way tests are selected when a DUnit project runs - it now simply iterates the entire tree of tests and marks those that you selected in the IDE to then just let them run instead of relying on the ShouldRun method from the listener interface which also made it possible to properly use test decorators without them being run even if there were no tests selected for them.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">The new view is not only supported for DUnit though but also for DUnitX and DUnit2.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">By the way - please remember: the "fast forward" icon has two functions. When no tests are selected it runs a "discover" which means doing a quick project run but just collect which tests are there without executing them. If any tests have a checkbox, then it executes just those but still collects all other tests inside the project. And via the context menu, you can quickly run the currently focused test or the entire branch without changing any checkboxes.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">TestInsight supports any Delphi version from XE and newer - always the latest update on each major version.</div><div style="text-align: left;">You can download the setup <a href="https://files.spring4d.com/TestInsight/latest/TestInsightSetup.zip">here</a>.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Thanks again Wagner - was a pleasure to work with you on this release.</div></div></div><p></p>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com9tag:blogger.com,1999:blog-1532151022505671498.post-16931247666431707882021-04-15T00:23:00.000+02:002021-04-15T00:23:31.978+02:00A guide on successful open source collaboration<p>You are sharing your source with others or you want to contribute to an open-source project? This is great!</p><p><br /></p><p>Let's talk about a few points to ensure this will be an enjoyable experience for everyone.</p><p><br /></p><h4 style="text-align: left;">Setting things up</h4><p>First things first: If you maintain a git repository please add an appropriate <i>.gitignore</i> <u>and</u> a <i>.gitattributes</i> file.</p><p>There are several templates for that and articles explaining them - hence I am not going into detail here. Just one thing: rule those line endings and don't let them rule you by messing up your blue dots in the IDE, garbling your class completion or your project file, or making every second commit a complete mess.</p><p>People have different settings in their git on how to deal with line endings and there is no "correct" or "wrong" way, they are just different - that's why smart people invented the .gitattributes file to let the repository decide how line endings should be treated equally for every participant. Add it, possibly clean up any existing wrong line endings and everyone will be happy going forward.</p><p><br /></p><h4 style="text-align: left;">Know the tools</h4><p>Please learn the tools you are using - if your history looks like Cthulhus tentacles because you don't know how to properly pull rebase you are doing something wrong. This is especially important when you are creating pull/merge requests and the reviewer asks you to fix some things and get the branch up to date for a clean and conflict-free merge. Learn how to interactive rebase and respect the maintainers time by not having them look through your trial and error going back and forth, turning things upside down three times, merging upstream changes in the middle garnished with "fixed conflicts after merge" commits until you finally reached the state you want to submit. Learn how to work with different remotes managing your fork and the original repo in order to keep your fork up to date in a clean way. Again there are guides out there explaining these things in great detail. Please read them.</p><p><br /></p><h4 style="text-align: left;">Respect each other</h4><p>Divide separate things into own commits, write meaningful commit messages, and if necessary put things into separate pull requests to not throw one big clump at the maintainer making it easier to look through the several things piece by piece. It will also make it easier to address remarks made during the review, produce a better and/or quicker outcome and leave all participants in a good mood.</p><p>Stick to the coding style of the maintainer - this includes naming and formatting as well as the usual approach on things in the repository. The codebase uses begin after then instead of in a new line? Then do so as well. They write integer lowercase or String uppercase? Then do so as well. Don't try to sneak your personal style in there if it's in contrast to the existing code. Nothing worse than a patchwork of different coding styles emerging over time the more contributors come together.</p><p><br /></p><p>These are just some of my recommendations and you might have a different opinion on some things or some details but after being active in open source development for over 15 years I believe following these suggestions will improve collaboration on open source projects for everyone.</p><p>If you are not contributing to some open source project yet - then please be encouraged to consider doing so. Start small, maybe you found an issue in some library you are using - find out of it's known yet and report if not. If you already found a fix, consider providing this fix attached to the issue or as a pull/merge request.</p>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-54498969138298896422021-04-11T04:33:00.001+02:002021-04-11T16:51:11.707+02:00Out parameters are just bad var parametersAfter <a href="https://delphisorcery.blogspot.com/2021/03/const-parameters-are-implementation.html">talking about const parameters last time</a> today we take a closer look at out parameters and how they differ from var parameters.<div><br /></div><div>I like the concept of out parameters as they clearly state that only the value that comes back matters and not the value that goes in.
However, the Delphi implementation of out parameters has some aspects to it that can cause a decision against them and rather choose a var parameter.
As last time we will look at some assembler code that shows what those differences are. Out parameters like var parameters pass a reference to the value rather than the value itself.</div><div><br /></div><div>The code for this article will be this:
<pre><code class="delphi">{$APPTYPE CONSOLE}
{$O+,W-}
type
TTestType = ...;
procedure C(z: TTestType);
begin
end;
procedure A(var y: TTestType);
begin
y := Default(TTestType);
end;
procedure B(out y: TTestType);
begin
y := Default(TTestType);
end;
procedure Main;
var
x: TTestType;
begin
A(x);
B(x);
end;</code></pre>
Like last time, we will inspect the code for different types for TTestType and see how it differs.
Let's start with Integer - the code we will see for the calls and inside A and B are as follows:
<pre><code class="asm">OutParams.dpr.24: A(x);
00408FA5 8BC4 mov eax,esp
00408FA7 E8E8FFFFFF call A
OutParams.dpr.25: B(x);
00408FAC 8BC4 mov eax,esp
00408FAE E8E9FFFFFF call B</code></pre>
<pre><code class="asm">OutParams.dpr.12: y := Default(TTestType);
00408F94 33D2 xor edx,edx
00408F96 8910 mov [eax],edx
OutParams.dpr.13: end;
00408F98 C3 ret</code></pre>
<pre><code class="asm">OutParams.dpr.17: y := Default(TTestType);
00408F9C 33D2 xor edx,edx
00408F9E 8910 mov [eax],edx
OutParams.dpr.18: end;
00408FA0 C3 ret</code></pre>
Nothing really fancy here - esp is the address of the local variable x. The (<a href="https://quality.embarcadero.com/browse/RSP-30835">unfortunately still undocumented</a>) intrinsic function Default() takes care of setting the parameter to the default value of the given type - so in this case 0. The code for the var parameter will look exactly the same.
For other types, the code will look similar but something interesting happens when we use a managed type such as string. Take a look at how this changes the code being generated:
<pre><code class="asm">OutParams.dpr.24: A(x);
00408FCB 8D45FC lea eax,[ebp-$04]
00408FCE E8C1FFFFFF call A
OutParams.dpr.25: B(x);
00408FD3 8D45FC lea eax,[ebp-$04]
00408FD6 E8F1CEFFFF call @UStrClr
00408FDB E8C0FFFFFF call B</code></pre>
On the caller side, we now see a call to System._UStrClr which the compiler inserted here to ensure that x will be empty before being passed to A. Out parameters are always initialized before they are passed, unlike var parameters.
When we will take a look at the code inside A we will see nothing unusual except a lack of optimization. eax could directly be passed to System._UStrClr - and that is not a result of using Default() but also happens when assigning '' to y:
<pre><code class="asm">OutParams.dpr.11: begin
00408F94 53 push ebx
00408F95 8BD8 mov ebx,eax
OutParams.dpr.12: y := Default(TTestType);
00408F97 8BC3 mov eax,ebx
00408F99 E82ECFFFFF call @UStrClr
OutParams.dpr.13: end;
00408F9E 5B pop ebx
00408F9F C3 ret</code></pre>
The interesting difference however will be obvious when we will look into B:
<pre><code class="asm">OutParams.dpr.16: begin
00408FA0 55 push ebp
00408FA1 8BEC mov ebp,esp
00408FA3 53 push ebx
00408FA4 8BD8 mov ebx,eax
00408FA6 85DB test ebx,ebx
00408FA8 7404 jz $00408fae
00408FAA 33C0 xor eax,eax
00408FAC 8903 mov [ebx],eax
OutParams.dpr.17: y := Default(TTestType);
00408FAE 8BC3 mov eax,ebx
00408FB0 E817CFFFFF call @UStrClr
OutParams.dpr.18: end;
00408FB5 5B pop ebx
00408FB6 5D pop ebp
00408FB7 C3 ret</code></pre>
What is going on here? The first two instructions are setting up a frame pointer which is usual but would not be necessary in this case - especially since we turned off that option. The following lines basically ensure that our y parameter really is empty - again with a lack of optimization.</div><div><br /></div><div>Why is this happening? The caller side ensured that y is empty. This code is for C++Builder compatibility! C++ does not have the concept of out parameter but just by reference parameter which equals the var parameter in Delphi. And because of that when C++ would call such a routine the value would not have been initialized. Unfortunately, at least to my knowledge, there is no option to turn this off because our code will never be called from C++. We have built an executable here in Delphi; our function is not exported nor did we make a DLL.
When using out parameters you pay a price for some most likely unused feature as most if not all Delphi code you ever write will never be called from any C++ code.
The impact of out parameters is so significant in some cases that in 10.4 some of them were changed to var in the RTL - take a look at most parameters of TValue in the unit System.Rtti. Because for records this overhead can be even bigger and even more when they are passed multiple levels of out parameters because every call and routine again produces this completely unnecessary overhead.</div><div><br /></div><div>The entire concept of out parameters to me looks completely unfinished - let's for a second take a look at some C# code which also has out parameters
<pre><code class="csharp">static void B(out int y)
{
WriteLine(y);
}</code></pre>
This code will raise two errors:
<pre>Error CS0269 Use of unassigned out parameter 'y'
Error CS0177 The out parameter 'y' must be assigned to before control leaves the current method</pre>
And the compiler is right - since the parameter is out the value going in actually is undefined behavior - and if the compiler ensures that it cannot be used then it also does not need to do any initialization ensuring any value. That means no unnecessary work to do and no inconsistent behavior - remember unmanaged data types don't get that extra treatment, whatever value is in an int variable being passed is still in there when being passed to an out parameter.</div><div>Second, the compiler ensures setting a value to the out parameter ensuring it is not undefined when returning from this routine. Would an exception occur during the invocation of that routine and before the out parameter was set then upon returning and catching the exception the variable would still contain the value it had before - a totally sane and understandable behavior.</div><div><br /></div><div>As for me - I will be more careful where I use out parameters and in fact, during the refactoring of the collections in Spring4D, I replaced most out parameters with var parameters. Since even without the compiler enforcing this, all code paths inside those methods eventually set the value of the parameter it will be no change in behavior for you but avoid this completely insane overhead.</div>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com8tag:blogger.com,1999:blog-1532151022505671498.post-9542166694524203562021-03-27T02:54:00.003+01:002021-03-27T18:36:25.036+01:00How to hide the CodeInsight status panel<p>The new CodeInsight status panel added in 10.4.2 can be very distracting with its busy progressbar. Unfortunately there is no builtin mechanism to hide it but doing so is very easy.</p><p>Edit: Apparently as Lachlan pointed out on the comments I totally missed the "Code Insight Activity" toggle in the Options dropdown of the Project manager toolbar:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhevHqJV5zEpmL9KpZkuNmuzjWyEhfx-fNmC4eR0JO8mbcY1xt6wwIGHoq3NpE1jeNJ9QnIxRlh175P77-vSVMFXaQp1PxvShtEaJOKAF-d8g6Qlrar4VfGuk6ixdlAoSq8yK7H9WX-yqeB/" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="324" data-original-width="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhevHqJV5zEpmL9KpZkuNmuzjWyEhfx-fNmC4eR0JO8mbcY1xt6wwIGHoq3NpE1jeNJ9QnIxRlh175P77-vSVMFXaQp1PxvShtEaJOKAF-d8g6Qlrar4VfGuk6ixdlAoSq8yK7H9WX-yqeB/s16000/image.png" /></a></div><br /><br /><p></p>
I leave the obsolete way here for documentation - here is the code to add a toggle button to the projectmanager toolbar (adding an icon is left as an excersise to the reader):<div><br /><pre><code class="delphi">unit ToggleLSPStatusPanel;
interface
procedure Register;
implementation
uses
System.Classes,
Vcl.Forms,
Vcl.ComCtrls,
Vcl.ExtCtrls;
var
LSPPanel: TPanel;
ToolButton: TToolButton;
procedure TogglePanel(instance: TObject; Sender: TObject);
begin
LSPPanel.Visible := not LSPPanel.Visible;
end;
procedure Register;
var
e: TNotifyEvent;
begin
var projectManagerForm := Application.MainForm.FindComponent('ProjectManagerForm');
var toolBar := TToolBar(projectManagerForm.FindComponent('Toolbar'));
ToolButton := TToolButton.Create(nil);
LSPPanel := TPanel(projectManagerForm.FindComponent('pLSPStatus'));
ToolButton.Left := toolbar.Width;
ToolButton.Parent := toolbar;
TMethod(e).Data := nil;
TMethod(e).Code := @TogglePanel;
ToolButton.OnClick := e;
end;
initialization
finalization
try
ToolButton.Free;
except
// simply swallow the exception when closing the IDE as
// it will already have destroyed the button
// feel free to make this more robust
end;
end.</code></pre>
<p>Simply add this unit to a designtime package and install it. Enjoy!</p></div>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com6tag:blogger.com,1999:blog-1532151022505671498.post-23979578594241329342021-03-25T03:57:00.006+01:002023-11-07T21:15:12.902+01:00Introducing Delphi Uses Helper<p>I just wanted to share a small IDE plugin I wrote some time ago which helps adding units to your uses clause and/or navigating through code without having to rely on the faulty "Find declaration" feature.</p><p><br /></p><h4 style="text-align: left;">Installation and configuration</h4><p>After installation go to Tools->Options in the IDE and then to Third Party->UsesHelper (or just hit F6, type "UsesHelper" and hit Enter)</p><p>Add any directories whose source you want to have indexed (recursively) and add directories that should be excluded from the indexing - one directory per line, no extra separators. Variables are supported like they are in other places in the IDE such as library path - as you can see with the already present entry for the Delphi sources.</p><p>Under "Unit scope names" you can add those unit scopes you want to omit when adding units to the uses clause - so if you for example like me write code that has to work with versions before XE2 this might be handy to add just Classes rather than System.Classes.</p><p>If any changes were made it will automatically start indexing the source upon hitting save - indexing will only happen at that point or when you hit the button, not on starting the IDE or at any other point (I may add this at some point in the future).</p><p><br /></p><p>Supported versions: XE and higher</p><p><br /></p><h4 style="text-align: left;">What it does internally</h4><p>Indexed are: types, consts, global variables, routines, enums - did I miss anything? Well, basically everything inside the interface section of every unit it finds.</p><p>For the parsing of the files <a href="https://github.com/RomanYankovsky/DelphiAST">DelphiAST</a> is being used. Any errors that might occur are being printed out in the messages panel - I am not giving support for any source code that might not be parsed properly - if you have some issue please verify it with the demo project coming with DelphiAST and report the issue over there. Also be aware that parsing might fail if you have units that are littered with custom compiler defines as the parsing mechanism is not aware of them - it just operates with the defines that DelphiAST uses (which are those of a standard Win32 application).</p><p>Indexing may take a while - it will print out when it's done.</p><p>The index is being stored in a file per Delphi version you have the plugin installed in (the settings are also per Delphi version) that is stored in the installation directory which is %localappdata%\programs\useshelper but loaded into memory by the plugin. Additionally it will index files within the currently opened project on the fly when being invoked.</p><p>Exception: System and SysInit are not being indexed as you cannot add those units to uses clause anyway but this will avoid navigating to any identifier in those units.</p><p><br /></p><h4 style="text-align: left;">Format of the index.dat</h4><div>If you fancy you can write that file yourself - the format is fairly simple:</div><div><br /></div><div><symbolname>=<unitname>|<int32> - the upper 8bit of that number are the column and the lower 24bit are the line number</div><p><br /></p><h4 style="text-align: left;">Now lets try it out</h4><p>When you are in the code and have the caret on some identifier you can hit Ctrl+Shift+A - which usually invokes a similar but way worse feature of the IDE - or whatever hotkey you set this to.</p><p>You will see list of units where this identifier is found in - the match has to be exact, no fuzzy matching, no guessing. It you type TSrtingList it won't find anything I guess. (implementing any kind of more sophisticated search is not planned)</p><p>Now you can navigate with the up/down keys if there are multiple units to chose from or switch between interface/implementation with the left/right keys (it currently does not detect in which section you currently are to make a best guess).</p><p>If you then hit enter the unit will get added to the appropriate uses clause - it will get added at the end and it will in a new line - no configuration to control that behavior and I will not go into the mess of doing so.</p><p>If you hit Shift+Enter the unit you selected will be opened and you will navigate to the declaration. This works even if you have no project open which "find declaration" does not.</p><p>Yes, the dialog is modal - leaving it without doing anything only works with the Esc key at this point.</p><p>Currently moving a unit between interface and implementation is not supported.</p><p><br /></p><p>Anyhow enjoy free stuff as some selected people and me did for quite a while now and don't pester me with feature requests. ;)</p><p><br /></p><h4 style="text-align: left;"><a href="https://files.spring4d.com/UsesHelper/latest/UsesHelperSetup.zip">Download here</a></h4>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-36953070146771257002021-03-20T04:33:00.001+01:002021-03-20T04:33:25.568+01:00Const parameters are an implementation detail<p>A parameter
with a const is different from one without const in terms of a function
signature. But is that actually true? Is <span style="font-family: courier;">A(x: T)</span> really different from <span style="font-family: courier;">B(const x: T)</span>?</p>
<p>Disclaimer:
In this article I am focusing on the default calling convention on Windows in
Delphi which is register.</p>
<p>We will go onto
a journey through some assembler code and look into various method calls with different
typed parameters both const and no const and explore their differences. Don’t
worry - I will explain the assembler in detail.</p>
<p>I have following
code for you that we will use with different types to explore:</p>
<pre><code class="delphi">{$APPTYPE CONSOLE}
{$O+,W-}
type
TTestType = …;
procedure C(z: TTestType);
begin
end;
procedure A(y: TTestType);
begin
C(y);
end;
procedure B(const y: TTestType);
begin
C(y);
end;
procedure Main;
var
x: TTestType;
begin
A(x);
B(x);
end;</code></pre>
<p>We will put
different types for TTestType, put a breakpoint into Main and see how the code
looks like for the calls and inside of A and B. I will be using 10.4.2 for this
experiment. We will ignore the warning because it does not matter for the
experiment.</p>
<p>Let’s start
with something simple: Integer</p>
<p>When we
look into the disassembly we see the following:</p>
<pre><code class="asm">Hamlet.dpr.24: A(x);
00408FA9 8BC3 mov eax,ebx
00408FAB E8E8FFFFFF call A
Hamlet.dpr.25: B(x);
00408FB0 8BC3 mov eax,ebx
00408FB2 E8E9FFFFFF call B</code></pre>
<p>eax is the
register being used for the first and only parameter we have in our routines. ebx
is the register where x is being stored during the execution of Main. No difference
between the two calls though – and here is how it looks inside of A and B. In case
you wonder – the calls to C are just there to prevent the compiler to optimize away
some code which will be important later.</p>
<pre><code class="asm">Hamlet.dpr.12: C(y);
00408F98 E8F7FFFFFF call C
Hamlet.dpr.13: end;
00408F9D C3 ret
Hamlet.dpr.17: C(y);
00408FA0 E8EFFFFFFF call C
Hamlet.dpr.18: end;
00408FA5 C3 ret</code></pre>
<p>Also the
same, the value is being kept in the register it was being passed in – eax –
and C is called. We get the same identical code for various other ordinal types
such as Byte, SmallInt or Cardinal, enums or sets that are up to 4 byte in size,
Pointer or TObject (did someone say “not on ARC”? Well we are on 10.4.2 – I erased
that from my memory already – and soon from my code as well).</p>
<p>Let’s
explore Int64 next:</p>
<pre><code class="asm">Hamlet.dpr.24: A(x);
00408FC7 FF742404 push dword ptr [esp+$04]
00408FCB FF742404 push dword ptr [esp+$04]
00408FCF E8C8FFFFFF call A
Hamlet.dpr.25: B(x);
00408FD4 FF742404 push dword ptr [esp+$04]
00408FD8 FF742404 push dword ptr [esp+$04]
00408FDC E8CFFFFFFF call B</code></pre>
<p>And B and C:</p>
<pre><code class="asm">Hamlet.dpr.11: begin
00408F9C 55 push ebp
00408F9D 8BEC mov ebp,esp
Hamlet.dpr.12: C(y);
00408F9F FF750C push dword ptr [ebp+$0c]
00408FA2 FF7508 push dword ptr [ebp+$08]
00408FA5 E8EAFFFFFF call C
Hamlet.dpr.13: end;
00408FAA 5D pop ebp
00408FAB C20800 ret $0008</code></pre>
<pre><code class="asm">Hamlet.dpr.16: begin
00408FB0 55 push ebp
00408FB1 8BEC mov ebp,esp
Hamlet.dpr.17: C(y);
00408FB3 FF750C push dword ptr [ebp+$0c]
00408FB6 FF7508 push dword ptr [ebp+$08]
00408FB9 E8D6FFFFFF call C
Hamlet.dpr.18: end;
00408FBE 5D pop ebp
00408FBF C20800 ret $0008</code></pre>
<p>On 32bit an
Int64 is passed on the stack as you can see – nothing fancy here but reserving
a stack frame and passing on the value to C. But no difference between the two
routines.</p>
<p>I don’t
want to bore you death with walls of identical assembler code so I can tell you
that for all the floating point types, enums and sets regardless their size the
calls to A and B are always identical.</p>
<p>But now for
the first difference – lets take a look at set of Byte – the biggest possible
set you can have in Delphi. A set is actually a bitmask with one bit for every
possible value – in this case this type is 256 bit in size or 32 byte. Like many
other types that don’t fit into a register it will actually be passed by reference
– this time I also show you the prologue of Main where you can see that the compiler
reserved 32 byte on the stack for our x variable and that this time the address
of the value is being passed to A and B which is in the esp register - the
stack pointer:</p>
<pre><code class="asm">Hamlet.dpr.23: begin
00408FC8 83C4E0 add esp,-$20
Hamlet.dpr.24: A(x);
00408FCB 8BC4 mov eax,esp
00408FCD E8C6FFFFFF call A
Hamlet.dpr.25: B(x);
00408FD2 8BC4 mov eax,esp
00408FD4 E8DFFFFFFF call B</code></pre>
<p>Let’s take
a look into A where we can see the first difference:</p>
<pre><code class="asm">Hamlet.dpr.11: begin
00408F98 56 push esi
00408F99 57 push edi
00408F9A 83C4E0 add esp,-$20
00408F9D 8BF0 mov esi,eax
00408F9F 8D3C24 lea edi,[esp]
00408FA2 B908000000 mov ecx,$00000008
00408FA7 F3A5 rep movsd
Hamlet.dpr.12: C(y);
00408FA9 8BC4 mov eax,esp
00408FAB E8E4FFFFFF call C
Hamlet.dpr.13: end;
00408FB0 83C420 add esp,$20
00408FB3 5F pop edi
00408FB4 5E pop esi
00408FB5 C3 ret</code></pre>
<p>First the
non-volatile registers (that means their values need to be preserved over subroutine
calls, in other words, when A returns the values in those registers must be the
same as they were before A was being called) are being saved. Then we see again
reserving 32byte on the stack as previously.</p>
<p>The next
four instructions: the parameter being passed eax is stored in esi, that is the
address to our value x that we passed in y, then the address of esp is loaded
into edi. The rep instruction does something interesting: it repeats the <a href="https://www.felixcloutier.com/x86/movs:movsb:movsw:movsd:movsq">movsd</a>
instruction (not to be confused with the <a href="https://www.felixcloutier.com/x86/movsd">movsd</a> – hey it’s called complex
instruction set for a reason) –moving 4 byte as often as ecx says using esi as
source address and edi as destination. Long story short, it does a local copy
of our set.</p>
<p>There we
have our first difference – the compiler produces a copy of our parameter value
– if the C call would not be here the compiler would actually optimize that
away as it detects there is nothing to do with y. And it does so even though we
just read the value and not actually modify it as we could with a non const
parameter.</p>
<p>Finally the
type many of you have been waiting for: string</p>
<pre><code class="asm">Hamlet.dpr.24: A(x);
004F088B 8B45FC mov eax,[ebp-$04]
004F088E E88DFFFFFF call A
Hamlet.dpr.25: B(x);
004F0893 8B45FC mov eax,[ebp-$04]
004F0896 E8CDFFFFFF call B</code></pre>
<p>Since string
is a managed type – meaning the compiler inserts code that ensures its initialized
as empty string and gets finalized we got quite some code in Main this time and
the variable is on the stack – string is a reference type so in terms of size
its just the same as a pointer fitting in eax. Just to avoid confusion – string
is a reference type but the parameter is by value – the value of the string
reference is passed in the eax register just like earlier for Integer and alike.</p>
<p>Fasten your
seatbelts as we take a look into the code of A:</p>
<pre><code class="asm">Hamlet.dpr.11: begin
004F0820 55 push ebp
004F0821 8BEC mov ebp,esp
004F0823 51 push ecx
004F0824 8945FC mov [ebp-$04],eax
004F0827 8B45FC mov eax,[ebp-$04]
004F082A E8D994F1FF call @UStrAddRef
004F082F 33C0 xor eax,eax
004F0831 55 push ebp
004F0832 685D084F00 push $004f085d
004F0837 64FF30 push dword ptr fs:[eax]
004F083A 648920 mov fs:[eax],esp
Hamlet.dpr.12: C(y);
004F083D 8B45FC mov eax,[ebp-$04]
004F0840 E8AFFFFFFF call C
Hamlet.dpr.13: end;
004F0845 33C0 xor eax,eax
004F0847 5A pop edx
004F0848 59 pop ecx
004F0849 59 pop ecx
004F084A 648910 mov fs:[eax],edx
004F084D 6864084F00 push $004f0864
004F0852 8D45FC lea eax,[ebp-$04]
004F0855 E8CA93F1FF call @UStrClr
004F085A 58 pop eax
004F085B FFE0 jmp eax
004F085D E9DA89F1FF jmp @HandleFinally
004F0862 EBEE jmp $004f0852
004F0864 59 pop ecx
004F0865 5D pop ebp
004F0866 C3 ret</code></pre>
<p>Ok, that’s
quite a lot of code but the important thing is this - the compiler basically
turned the code into this:</p>
<pre><code class="delphi">var
z: Pointer;
begin
z := Pointer(y);
System._UStrAddRef(z);
try
C(z);
finally
System._UStrClr(z);
end;
end;</code></pre>
<p>The System
routine _UStrAddRef checks if the string is not empty and increases its reference
count if it’s not a reference to a constant string because they have a reference
count of -1 meaning they are not allocated on the heap but point to some
location in your binary where the string constant is located. _UStrClr clears
the passed string variable and reduces its reference count under the same
conditions.</p>
<p>So why is
that being done? The string was passed as non const causing the compiler to ensure
that nothing goes wrong with the string by bumping its reference count for the
time of the execution of this routine. If some code would actually modify the
string the code to do that would see that the string – assuming we have one
that had a reference count of at least 1 when entering the method – has a
reference count of at least 2. This would trigger the copy on write mechanic
and keep the string that was originally passed to the routine intact because
even though a string is a reference type underneath it also has characteristics
of a value type meaning that when we modify it we don’t affect anyone else that
also holds a reference.</p>
<p>Now the code of B:</p>
<pre><code class="asm">Hamlet.dpr.17: C(y);
004F0868 E887FFFFFF call C
Hamlet.dpr.18: end;
004F086D C3 ret</code></pre>
<p>That’s almost
disappointing after so much code in A. But joking aside the const had a significant
effect: all that reference bumping, try finally to ensure proper reference
dropping in case of an exception is not necessary because the parameter being
const ensures that it cannot be modified.</p>
<p>The code being
executed in A is not the end of the world or will utterly destroy the performance
of your application but is completely unnecessary most of the time. And in many cases it can indeed mean the difference for the fast execution of an algorithm. Yes, I know
we can construct some terrible code that causes bugs when the parameter is
const because we have a circular data dependency but let’s not talk about this –
because <a href="https://dalijap.blogspot.com/2021/01/are-const-parameters-dangerous.html">this
has already been discussed</a>.</p>
<p>Similar code
can be found for interfaces, dynamic arrays and managed records (both kinds,
records with managed field types and the new custom managed records).</p>
<p>There are not
many more types left – records and static arrays depending on their size are either
passed by value or reference or in the fancy case of being of size 3 pushed onto
the stack.</p>
<p>For a complete summary please refer to <a href="https://www.guidogybels.eu/asmtable3.html">this table</a>.</p>
<p>As we can see from the caller side a const and a non const parameter are not any
different for the register calling convention. For other calling conventions
however they differ – especially on non Windows platforms. As an example with
the stdcall convention a record that is bigger than a pointer is being pushed
onto the stack as a non const parameter while as a const parameter just its
address is being pushed so basically passed by reference.</p>
<p>That was a rather lengthy adventure – but what did we learn?</p>
<p>For most code on Windows a const parameter is actually an implementation
detail and does not affect the code on the caller side. But knowing that it depends on the platform
and the calling convention makes it clear that as much as some would like non
const and const parameters to be compatible in order to easily add some missing
const throughout the RTL or VCL where it would have a benefit of removing unnecessary
instructions is simply impossible. Nor could we simply assume non const
parameters to behave similar to const parameters.</p>
<p>Using const parameters can avoid unnecessary instructions and speed up your code without any cost or side effects. There is no reason to not make your string, interface, dynamic array or record parameters const. That can be even more important if you are a library developer.</p>
<p>In the next
article we will explore another kind of parameter: out</p>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com3tag:blogger.com,1999:blog-1532151022505671498.post-55060555241303411412020-11-30T09:02:00.008+01:002020-11-30T19:38:41.658+01:00Extended Q&A to my DelphiCon session about Spring4D<p>Many of you have probably seen <a href="https://delphicon.embarcadero.com/talks/introduction-to-spring4d-taking-delphi-development-to-the-next-level/">my session at DelphiCon</a> <a href="https://www.youtube.com/watch?v=izI0Vah2CRs">(watch directly on Youtube)</a> and the overall feedback has been quite positive - many thanks to you and I hope I could stir up some interest if it was completely new for you.</p><p>In this article I will address the questions that came up during the presentation but have not yet been answered. Please leave a comment if I missed any.</p><p><br /></p><p><b>Q</b>: When using Shared<T> can I determine how many shares are in play and therefore, if I'm processing the last running share?</p><p><b>A</b>: Not without hacking into the data structure. Since you can pass a finalizer that automatically triggers when the last one goes out of scope that should not be necessary.</p><p><br /></p><p><b>Q</b>: Shared<T>, IShared<T> are really nice! What about performance?</p><p><b>A</b>: While they involve a small heap allocation and atomic reference counting everything is as fast as it can be without any unnecessary overhead. In fact there is no object underneath implementing the interface but a handcrafted memory block which avoids overhead of object construction and destruction and adjustor thunks for the method calls. I will share some more details on this approach in a future blog post as I have been using this in several places now to achieve that extra bit of performance and reduce binary size especially for generics.</p><p><br /></p><p><b>Q</b>: Can the Shared<T> be used as a class field that you pass to other classes? Also can it be a function result that you keep until the end? I'm thinking about using this with an api wrapper like you said.</p><p><b>A</b>: Absolutely!</p><p><br /></p><p><b>Q</b>: Can we / could we do unique pointer in Delphi?</p><p><b>A</b>: If my understanding of a unique pointer is not wrong it prevents any assignment and makes sure that there is just one reference. If I am again not mistaken this is achieved in C++ by hiding/not giving an assignment/copy operator. This is not possible in Delphi. I was hoping that custom managed records get this functionality but unfortunately they didn't. So there is no way to prevent having an explicit or implicit assignment happening.</p><p><br /></p><p><b>Q</b>: What will it take to extend Spring4D's reach to FPC (and, in turn, Pas2js -- now that generics are supported) so we can enjoy Spring4D for type-safe Web application development?</p><p><b>A</b>: I have looked into FPC several times over the years and while many people praise it for being the open source rescue from Delphi it still lacks things that many of us take for granted in Delphi such as anonymous methods - I don't know their current state but they are integral part of the majority of the library. Pas2js is a bit different and I would not consider it the regular FPC because TMS is pushing this to make TMSWebCore work which aims for Delphi compatibility. I am certainly interested in looking into it at some point but that will not be any time soon. I also don't want to get into some ifdef hell - I just have been happy about removing a lot that were for Delphi2010 and probably a few more for ARC soon.</p><p><br /></p><p><b>Q</b>: What font are you using in the editor? It is very nice to write code!</p><p><b>A</b>: I was hoping for anyone to notice. ;) It is JetBrains Mono - you can get it <a href="https://www.jetbrains.com/lp/mono/">here</a>. Make sure to use those directly in the ttf folder of that zip file because the Delphi code editor does not support ligatures.</p><p><br /></p><p><b>Q</b>: Are any of the IEnumerable functions using threads behind the scenes to speed up processing. For example, complex where clauses on massive lists of objects.</p><p><b>A</b>: No, there is no multithreading going on - the implementation is pretty much similar to the one you find in System.Linq in .NET. Due to the extensible nature of the library by using interfaces you can just make your own Version of this where you can go fancy. Unfortunately since we don't have interface helpers you have to invoke it like this: TMyEnumerable.MyWhere<TCustomer>(customers, otherstuff)</p><p><br /></p><p><b>Q</b>: Nullable is the same as C#, value based?</p><p><b>A</b>: Yes, pretty much although in Delphi some types are supported that are not strictly value types but mostly treated like them such as strings. While it does not prevent you from using it with some T that is a reference type it does not make much sense and might behave wrong because it does not explicitly differ between null and explicitly having a typed nil being assigned.</p><p><br /></p><p><b>Q</b>: Could the Nullable be stringified and then parsed back?</p><p><b>A</b>: I originally answered the question possibly wrong during the Q&A so here is another take at it: Yes, you can turn a nullable into a string and reverse. Here is some example code on how to achieve that. You need to have a conditional when parsing back the string though to either set the value or to set it as null. This is achieved with the helper for TValue from Spring.pas.</p>
<pre><code class="delphi">
var
n: Nullable<Integer>;
s: string;
v: TValue;
begin
n := 42;
s := TValue.From(n).ToString;
Writeln(s);
n := nil;
s := TValue.From(n).ToString;
Writeln(s);
s := '55';
v := TValue.From(s);
v.TryToType(n);
Writeln(n.HasValue);
Writeln(n.Value);
v := TValue.Empty;
v.TryToType(n);
Writeln(n.HasValue);
end.</code></pre>
<p><br /></p><p><b>Q</b>: Is there any active development on the library or future plans? What news can we expect in Version 2?</p><p><b>A</b>: There is constant development and I am always open for suggestions or improvements. Some things that I showed in the presentation are new in 2.0 but I will go into more details on features and some technical stories in the coming weeks. Right now the goal is to get an RC ready before the end of the year and before my Christmas holiday in Night City ;) The plans for the version after that is to finally work on some major refactoring of the DI container in order to fix some of its slowly revealing shortcomings in its architecture/implementation which will enable the implementation of some pretty exciting features. But more about that when the time comes.</p><p><br /></p><p><b>Q</b>: What about the future of the ORM part?</p><p><b>A</b>: Due to lack of time and expertise in that area I put development of that part on hold for now. I explained this a bit more in detail some while ago <a href="https://groups.google.com/g/spring4d/c/R7O3q52hO5s/m/yPdyTvpdCwAJ">on the mailing list</a> (thanks google for deprecating the ability to pin topics...). It was a decision that was not easy but I am confident that it is better for the overall quality of the library as I can stay focused on its core features.</p><div><br /></div><div>I know that many of you wish for better documentation and tutorials and that is top priority right after getting the 2.0 RC out.</div><div><br /></div><div>Downloads: <a href="https://s3.amazonaws.com/heysummit-production/media/uploads/events/delphicon/SourceCode.zip">sample code</a> - <a href="https://s3.amazonaws.com/heysummit-production/media/uploads/events/delphicon/Spring4D_slides.pdf">slides</a></div>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com4tag:blogger.com,1999:blog-1532151022505671498.post-30026347110335575592020-09-07T22:18:00.002+02:002020-09-08T18:11:42.000+02:00Open array parameters and subranges<p>Open array parameters in Delphi are one of the unique language constructs in Delphi. While their syntax resembles that of a dynamic array they are a different thing. In fact under the hood they consist of two parameters, the first being the pointer to the first item and the second being the highest index.</p><p>You can pass static and dynamic arrays to an open array parameter as well as directly specify values to be passed using the square brackets. This sums up what they are but the <a href="http://docwiki.embarcadero.com/RADStudio/Sydney/en/Parameters_(Delphi)#Open_Array_Parameters">documentation</a> has some more details about them. There is also a <a href="https://web.archive.org/web/20200124193818/http://rvelthuis.de/articles/articles-openarr.html">detailed article</a> about them.</p><p>Open array parameters are a great way to work with sub ranges without additionally passing start and end index or start index and length which avoids any possible defects by wrong offset calculation.</p><p>If you want to take a subrange of such an array you can use the <a href="http://docwiki.embarcadero.com/Libraries/Sydney/en/System.Slice">Slice</a> function - which however has two little deficiencies. It <a href="https://quality.embarcadero.com/browse/RSP-21329">does not work on dynamic arrays</a> and what this article is about: it can only return subranges that start at the beginning.</p><p>Imagine the following code - for the sake of this article we take a standard recursive mergesort implementation and try to partially implement it by using an open array.</p>
<pre><code class="delphi">procedure MergeSort(var values: array of Integer);
begin
if Length(values) <= 1 then
Exit;
MergeSort(<left half of values>);
MergeSort(<right half of values>);
// actual merge left as an exercise to the reader
end;</code></pre>
<p>The left half is easy as you can just use Slice:</p>
<pre><code class="delphi"> MergeSort(Slice(values, Length(values) div 2);</code></pre>
<p>But the right side must not start at the beginning and gives us a problem. The question is not new and <a href="https://stackoverflow.com/q/15993428/587106">has already been asked on stackoverflow</a> and shows a way how to trick the compiler into generating the correct code - keep in mind we don't want to copy parts of the array but implement an inplace merge sort here - the sort itself does not matter but just serves to demonstrate getting a subrange.</p><p>We aim for getting the best code being generated while keeping as much readability as possible - so any fancy solutions using anonymous methods are off limits. The only solution to the problem is to trick Slice in thinking it is dealing with a static array (which the questioner tried to avoid if possible). Because we need it later let's declare a type for this:</p>
<pre><code class="delphi">type
TOpenArray<T> = array[0..0] of T;</code></pre>
<p>As this is a generic type it can be used for any open array later - for now we are just sorting Integer. With its help we can complete the trick and outwit Slice:</p>
<pre><code class="delphi"> len := Length(values);
mid := len div 2;
MergeSort(Slice(TOpenArray<Integer>(values[mid]), len - mid));</code></pre>
<p>Apart from introducing variables for the length and the middle of the array we are now taking the start of the right half (if the length is uneven the right side will be one element longer) and hardcasting this to our one-sized static array of Integer and passing the length of the right half. If you compile and run this code with some data it will eventually raise an ERangeError because the compiler assumes that this array has only one element and we want to pass more than that.</p><p>So we have to surround that code with directives to temporarily disable range checking if that is enabled - I usually write the code as follows:</p>
<pre><code class="delphi"> len := Length(values);
mid := len div 2;
{$IFOPT R+}{$R-}{$DEFINE RANGECHECKS_ON}{$ENDIF}
MergeSort(Slice(TOpenArray<Integer>(values[mid]), len - mid));
{$IFDEF RANGECHECKS_ON}{$R+}{$UNDEF RANGECHECKS_ON}{$ENDIF}</code></pre>
<p>If you are using something such as <a href="https://github.com/project-jedi/jedi/blob/master/jedi.inc">jedi.inc</a> you already get the RANGECHECKS_ON define and can check for that and don't have to undef - anyhow the goal is to not get this exception - of course you have to make sure that the length you are passing here is correct!</p><p>So far so good we have our Mergesort implementation and could implement the interesting part, the merging simply with an array that starts at 0 and has Length(values) elements - no tedious offset calculation. How you do that though is out of the scope for this article.</p><p>Now let's make it generic!</p>
<pre><code class="delphi">class procedure TArray.MergeSort<T>(var values: array of T);
var
len, mid: Integer;
begin
len := Length(values);
if len <= 1 then
Exit;
mid := len div 2;
MergeSort<T>(Slice(values, mid));
{$IFOPT R+}{$R-}{$DEFINE RANGECHECKS_ON}{$ENDIF}
MergeSort<T>(Slice(TOpenArray<T>(values[mid]), len - mid));
{$IFDEF RANGECHECKS_ON}{$R+}{$ENDIF}
// actual merge left as an exercise to the reader
end;
</code></pre>
<p>When we try to compile this code we will get an error in line 14: E2089 Invalid typecast - ugh... generics. Obviously for whatever reason the compiler here does not allow us to hardcast that one element of type T (values[mid]) into a one sized static array of type T (is that a bug given the types have an exact match? Should I report this? Let me know). Anyhow we can solve this - and that might be an indicator that the compile error is bogus:</p>
<pre><code class="delphi"> MergeSort<T>(Slice(TOpenArray<T>((@values[mid])^), len - mid));</code></pre>
<p>Simply using the address and dereferencing satisfies the compiler to compile - and generate the correct code. Speaking of which - let's take a look at it - I am using optimization and turned off and range and overflow checking to just get the essence:</p>
<pre><code class="asm">MergeSortDemo.dpr.44: begin
0041CA34 53 push ebx
0041CA35 56 push esi
0041CA36 51 push ecx
0041CA37 890424 mov [esp],eax
MergeSortDemo.dpr.45: len := Length(values);
0041CA3A 8D5A01 lea ebx,[edx+$01]
MergeSortDemo.dpr.46: if len <= 1 then
0041CA3D 83FB01 cmp ebx,$01
0041CA40 7E24 jle $0041ca66
MergeSortDemo.dpr.49: mid := len div 2;
0041CA42 8BF3 mov esi,ebx
0041CA44 D1FE sar esi,1
0041CA46 7903 jns $0041ca4b
0041CA48 83D600 adc esi,$00
MergeSortDemo.dpr.54: MergeSort<T>(Slice(values, mid));
0041CA4B 8BD6 mov edx,esi
0041CA4D 4A dec edx
0041CA4E 8B0424 mov eax,[esp]
0041CA51 E8DEFFFFFF call TArray.MergeSort<System.Integer>
MergeSortDemo.dpr.59: MergeSort<T>(Slice(TOpenArray<T>((@values[mid])^), len - mid));
0041CA56 8BD3 mov edx,ebx
0041CA58 2BD6 sub edx,esi
0041CA5A 4A dec edx
0041CA5B 8B0424 mov eax,[esp]
0041CA5E 8D04B0 lea eax,[eax+esi*4]
0041CA61 E8CEFFFFFF call TArray.MergeSort<System.Integer>
MergeSortDemo.dpr.63: end;
0041CA66 5A pop edx
0041CA67 5E pop esi
0041CA68 5B pop ebx
0041CA69 C3 ret </code></pre>
<p>Not much to complain about here - the compiler generated just the right code - sure it could have used a register to store eax in instead of using the stack and thus avoid one mov but that is what it usually does in generics. But for the important use of Slice it properly calculates the offset in the array and passes that to MergeSort.</p><p>I stated in the introduction that open array parameters are a unique language feature in Delphi - however many other languages have better support for subranges of arrays or strings - and in fact I only came to the solution being presented here because I was looking how to improve the <a href="https://bitbucket.org/sglienke/spring4d/src/ffee3360a2e8cb9ae5311621a745bb9ed809870f/Source/Base/Spring.pas#lines-2218">implementation of introsort in Spring4D</a> when I saw <a href="https://github.com/dotnet/runtime/blob/3a4307e4ea7e3a212a73fa72c66c0efe578945d8/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs#L130">how .Net Core utilizes its Span<T></a>.</p><p>Would it be nice to have Slice work without that workaround and have a more powerful way to specify ranges on arrays? Yes and I might file another issue on QP about it - but in the meantime using open array parameters is a nice way to work with subranges of arrays without unnecessary offset calculation.</p>
<p>Update: I previously omitted the generic type parameter on the MergeSort call in the generic implementation which seems to work properly in Delphi 10.4.1 but causes some endless loop in the compiler in earlier versions. Updated the code to not use type inference but explicitly specify the generic type parameter.</p>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com2tag:blogger.com,1999:blog-1532151022505671498.post-60529967918114296262020-09-03T16:29:00.006+02:002021-05-17T18:59:06.073+02:00TestInsight 1.1.9.0 released<div class="separator"><div style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" height="83" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFIAAABSCAYAAADHLIObAAAHOElEQVR4Ae2bu2okRxRA98uc27n9Af6AzWVHSmykUM4ksEHKPKGEH6BNFtkO7GE3GfADTbKgTRaBMVjgpM0ZuMOd0q131Uyrdwqa6q6uR9epW/dRIz0b9qkJgWdNetl3MmSB/Ofhv2H203J4uXgbRPfX3d8D1/uUkkAC8MvZ6+Gjwx+HDw6uVter23cmJ8qlzifHL4bv52/MelMrTAL52fmvazgakgsD4MCTOpJ/e7N0q07uOQnkF7PXj+AA6evrPzaA8CzwdO7W22g0kYckkN/N35iAgMWWf372y/DN9Z/eOjGdOgWWSSAxHFrCcu/Z8lNPSSABkQtP6n989GLqDFfzi4IE4qcnL4tBYul9Fn5KhNcgBRhW9/OL31b+4qvluyqIIpXk4leS4xJ9dbWI+qNPCfQapM/iahg190im9kOlr7f3/z4lXt5vXYO0/D+ZbM98Kq7RCqSORlKh4VvKdmWZUA043h8e/pClT1nAKaQVSCBgXVMh4lf6Els1xzgBfgppvbWBeXK5iMJM2Yqpfufz05+HyelIkQq2eUg6AZ6SfGElUo8UhqQ6pf+x1VlLpP4wJMXa5pSnplBYOUW/shvIkAF7b0D6jEWORGLBLammTFv7VAkfe70NiWSCnOT4AFCeqiOtM0zpl8ipxMgsl8thPp+vrrGBXYMMHYMJAPJWVpsoh58tYunh4WE4Pj4eDg4ONq6bm5tY062+X4EMbUMNUe5DFhdJy4mScLlC6fb2dgOgAAVuTrq/vx+4eqUVyJCFFXhuzoGu1nUARKqteNptq59jx2xInsBzcyCnJL0Ys9lsuLu7S2mWVWe9tXNDOw2j5j6mKq6vr70ggRJLqIbDw8NHfVxcXMSaZr1fg2RCGghgfdZb16u9jxmvEEgAASqUTk5OHkEUyQ61y323BklDdCXbXLYsk2wBk0WhTy7GwKLTL/exFAIJEKy4LyGxAs3Nj46OfM2KyjdAWj0A0xfppEgjELUDjn4CTqqeioE8OzuzPnsF2IWnny8vL812pYVRkHRcYowEstaBwNP6CkixFAMJnMVisbHF3XE0QLmnTcuUBDLlVEjAublEQ77JocNC0nl+fu7dngJFchYJCbX8TqkjeUuI9JUE0qcn0XVIHJcvksEd8llOmRS5JZ3oPy3Bun7NPYvTOiWBdKWMZ3Sfa3F9x2/z398kSRXSidMMwBSpKoXZIypKAmlJpPXXE74TH6w1yr104q3b9YhwkkCyDTA4ApRDW1/S+pTtri12yKdrDSvUX0gn63lxSJKakkFKhymnNsCz6iEJPXReCJr1jm+wYIpaIeqR7/S5V8JD8myQ0rA0R/9Zk9t2mcAEKIYutFtSYvqtg2QBQhHHtoGmjJcilTsBiTtEiJYyibHUiUnlTkAilazyWCDFvgM1EDsc2QlIfT4Ym4T7HrVghYT4hqenp80XR3zbmE3YCcgSaSQaSfH/WKRWaoMxY5IogLcGEgjy45UrZbHn0FGZTETnTL7WoNE+JzUFCSx8MCSuVYhXE86VbvXchQN4U5ApR14x6dPvAVGTWFhxrHW/vnvqxqyz73uagiyVAN/EUnSib2JSnrO4ub9MyhjkTUGGogMfLF95rTTKJNGXvjGscmmXmzcFaX1YaVmNbnQh5CywFYO7/VnPTUGyjXJ0Ughyqa6yJpmzva+urpJdHj1WU5B0zFbC6tXqy12BlMXF+8j5Xac5SL1KGAukocRB3jVIAcoOw6eMbfmuIDVUwOQAzZEGPY51n/MDmgC0cqy6D+jWQDLBnGij5e/OrYID4Poinq2CxBJbK22V1fh0WiqRIKv/0jJUlZW2CpLtnTOBklDNnWTJAUnoG30qZ2sgseZYwtBHuu9Q9LQrTUza7bP22RdtbQUk2yvHKdaTpV0JTMas8Wlx3/SFbvRJIwvdHSSD10wIqMD0SYIlrSVj1oakXUHmRBRaCq17FoP+QtIJ7BzPQI9T67d2AVmiD/WkYvcYEKDKRVhXqjoYq1Yau23tUqmIAez1vlYau4FsFUn0Auf2i2TXpi5bO9dfdCe2i+daqewCktVF71hA0GWEfxKzjkUNEEmFDFlMYruBxIJiabnY6kQpPhdmLDD5jtLUDWTuB41Fr4ac7tCc9iCd/3FkB/l2zpMAyQQsndqiDJ2M/kuNeEq2+CgkspeVB4grXQCN6eQSd2gUIPnwmORh7XOjl9BWZPGsE/sSaWScUYCMAcIQ6YTrlGKcdBvrHunU/yRQCpG+dw6SyYSkEcjUcRMwQ+14l5qQztpD5J2DxAD4gGCAxHG3oFhbU/dltelVtnOQIf0Yk5LYb0C9oFn97hwk29YKJ1N+RYypBWvCvcp2DlImhp4SoOjF1OQzOjl9pI4VqjcakPKRIZ0odXTu07EtDmv1OLH70YGMfbD13nWfgBjTr1Y/NWWTAImuRDVYblINnJy2kwCZM+FedfcgG5Hdg9yDbESgUTf/A/H5gfTAuz1yAAAAAElFTkSuQmCC" width="83" /></div></div><div>A new version of TestInsight is available for Delphi 10.1 and higher including the just released Delphi 10.4.1 - older Delphi versions can still use version 1.1.5.0.</div><div><br /></div><div><strike>Download for <a href="http://files.dsharp.org/TestInsight/1.1.9.0/TestInsightSetup.zip">1.1.9.0</a> (10.1 or higher) or <a href="http://files.dsharp.org/TestInsight/1.1.5.0/TestInsightSetup.zip">1.1.5.0</a> (XE to 10.0)</strike></div><div>Download for <a href="https://files.spring4d.com/TestInsight/1.2.0.0/TestInsightSetup.zip">1.2.0.0</a>.</div><div><br /></div><div><br /></div><div>It contains mostly bugfixes (some had already been fixed in 1.1.8.0):</div><div><ul style="text-align: left;"><li>div by zero exception when trying to discover tests on DUnitX</li><li>truncated duration column for larger numbers: the format of the duration has been improved and you can resize the column now</li><li>an AV could occur in certain situations when closing the IDE with a docked TestInsight window</li><li>clipboard shortcuts did not work in the filter edit</li><li>vertical scrollbar did not have the correct size</li></ul><div>But also some nice improvements:</div></div><div><ul style="text-align: left;"><li>run test at the cursor from the interface section of the unit now works (not for individual testcase attributes yet though, it executes all for that particular method) - you have to be in the line of the method declaration</li><li>navigating from a test result to its implementation now works if the method is implemented by a parent class</li></ul></div>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com9tag:blogger.com,1999:blog-1532151022505671498.post-81551504998409848232018-11-19T09:55:00.000+01:002018-11-19T10:10:49.778+01:00Spring4D news and announcement<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Today I would like to inform you about the recent development and some future plans of the library.<br />
<br />
As you might have seen we have been busy in various branches. The 1.2.2 branch contains many bugfixes and small improvements and will also contain Delphi 10.3 support. It will be released once Delphi 10.3 does.<br />
<br />
The next version after that will be 1.3 which will contain a large refactoring of the collections to make them faster and smaller in size. There are also many new collection types - more about them in a future post. Originally this release should also contain a refactoring of the DI container but as that is another huge task I felt that holding back any changes even longer was not a good idea. So the next version after 1.3 will contain that. The release of 1.3 is scheduled for early 2019.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://conf.spring4d.com/"><img border="0" data-original-height="95" data-original-width="300" src="https://conf.spring4d.com/assets/img/spring4d-logo-300.png" /></a></div>
<br />
I am very happy to announce <b><a href="https://conf.spring4d.com/">Spring4D conf</a></b> which will be on april 17th and 18th in Bergamo, Italy. You will get two days packed with information about Spring4D and meet the authors and experts to learn more about the countless ways to improve your software - from the basic building blocks to advanced scenarios using powerful features like interception and dependency injection.<br />
<br />
So please head over to the <a href="https://conf.spring4d.com/">conference page</a> and order your tickets today!Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-83680768220930558972018-07-17T20:22:00.010+02:002020-07-24T23:53:28.690+02:00Loop wars - measuring performanceToday <a href="https://plus.google.com/u/0/+DavidNottageDelphiExpert/posts/2NRqLQzG6S8">on Google+</a> there was some off topic discussion going on and the claim was being made that for-in loops are slower than for-to loops and some benchmark was being posted to back that argument.<br />
<br />
Because I like to measure and investigate things I looked into that.
I was running the following benchmark. Yes, it is a micro benchmark - let's not argue about the usefulness of this in isolation.<br />
<br />
Keep in mind that testing code in the dpr main influences results because all variables are then global instead of local variables - that is why I put such code into some routine.<br />
<br />
<pre><code class="delphi">procedure Main;
var
xStrings: TStrings;
i: Integer;
s: string;
sw: TStopwatch;
begin
xStrings := TStringList.Create;
for i := 1 to 10000000 do
xStrings.Add(i.ToString);
sw := TStopwatch.StartNew;
for i := 0 to xStrings.Count - 1 do
s := xStrings[i];
Writeln(sw.ElapsedMilliseconds);
sw := TStopwatch.StartNew;
for s in xStrings do;
Writeln(sw.ElapsedMilliseconds);
Readln;
end;</code></pre>
<br />
Running this on my machine here gave me following average results across multiple runs (release config):<br />
<br />
210<br />
470<br />
<br />
During the for-in loop I am not assigning s to some other variable which was done in the originally posted code because I already have the value for each iteration, doing that again would result in one more string assignment in the for-in loop - be fair when comparing both.<br />
<br />
Looks like one loop is more than twice as fast as the other - let's investigate why that is.<br />
<br />
The TStringsEnumerator being used here is a class which comes with some overhead for its object allocation but only once for the GetEnumerator call. It might be of significance when measuring many loop iterations but in this case we are not. Anyway, we are going change it, copy the code from Classes.pas and change it to a record and use a helper for TStrings to easily get our enumerator being used. This is the added code:<br />
<br />
<pre><code class="delphi">type
TStringsEnumerator = record
private
FIndex: Integer;
FStrings: TStrings;
public
function GetCurrent: string; inline;
function MoveNext: Boolean;
property Current: string read GetCurrent;
end;
TStringsHelper = class helper for TStrings
function GetEnumerator: TStringsEnumerator;
end;
{ TStringsHelper }
function TStringsHelper.GetEnumerator: TStringsEnumerator;
begin
Result.FIndex := -1;
Result.FStrings := Self;
end;
{ TStringsEnumerator }
function TStringsEnumerator.GetCurrent: string;
begin
Result := FStrings[FIndex];
end;
function TStringsEnumerator.MoveNext: Boolean;
begin
Result := FIndex < FStrings.Count - 1;
if Result then
Inc(FIndex);
end;</code></pre>
<br />
Looking at the results again - nothing significant changed but that was expected.<br />
<br />
The generated assembly of the for-to loop:<br />
<br />
<pre><code class="x86asm">Loops.dpr.60: for i := 0 to xStrings.Count - 1 do
004D5EB8 8B45F0 mov eax,[ebp-$10]
004D5EBB 8B10 mov edx,[eax]
004D5EBD FF5214 call dword ptr [edx+$14]
004D5EC0 8BD8 mov ebx,eax
004D5EC2 4B dec ebx
004D5EC3 85DB test ebx,ebx
004D5EC5 7C1C jl $004d5ee3
004D5EC7 43 inc ebx
004D5EC8 C745EC00000000 mov [ebp-$14],$00000000
Loops.dpr.61: s := xStrings[i];
004D5ECF 8D4DFC lea ecx,[ebp-$04]
004D5ED2 8B55EC mov edx,[ebp-$14]
004D5ED5 8B45F0 mov eax,[ebp-$10]
004D5ED8 8B30 mov esi,[eax]
004D5EDA FF560C call dword ptr [esi+$0c]
004D5EDD FF45EC inc dword ptr [ebp-$14]
Loops.dpr.60: for i := 0 to xStrings.Count - 1 do
004D5EE0 4B dec ebx
004D5EE1 75EC jnz $004d5ecf</code></pre>
<br />
You can see that there are in fact 2 loop variables being used, one counting down to zero from xStrings.Count (stored in ebx), one counting up being used for accessing the items (stored on the stack at [ebp-$14]). The Count property is only read once.
Anyhow changing the code in the enumerator to only read Count once and store that internally will not change things drastically.<br />
<br />
The assembly of the for-in loop - oh wow:
<br />
<br />
<pre><code class="x86asm">Loops.dpr.67: for s in xStrings do;
004D5F11 8D55C4 lea edx,[ebp-$3c]
004D5F14 8B45F0 mov eax,[ebp-$10]
004D5F17 E8E8FEFFFF call TStringsHelper.GetEnumerator
004D5F1C EB44 jmp $004d5f62
004D5F1E 33C0 xor eax,eax
004D5F20 55 push ebp
004D5F21 685B5F4D00 push $004d5f5b
004D5F26 64FF30 push dword ptr fs:[eax]
004D5F29 648920 mov fs:[eax],esp
004D5F2C 8D4DF4 lea ecx,[ebp-$0c]
004D5F2F 8B55C4 mov edx,[ebp-$3c]
004D5F32 8B45CC mov eax,[ebp-$34]
004D5F35 8B18 mov ebx,[eax]
004D5F37 FF530C call dword ptr [ebx+$0c]
004D5F3A 8D45FC lea eax,[ebp-$04]
004D5F3D 8B55F4 mov edx,[ebp-$0c]
004D5F40 E89F4EF3FF call @UStrLAsg
004D5F45 33C0 xor eax,eax
004D5F47 5A pop edx
004D5F48 59 pop ecx
004D5F49 59 pop ecx
004D5F4A 648910 mov fs:[eax],edx
004D5F4D 68625F4D00 push $004d5f62
004D5F52 8D45F4 lea eax,[ebp-$0c]
004D5F55 E8624AF3FF call @UStrClr
004D5F5A C3 ret
004D5F5B E97840F3FF jmp @HandleFinally
004D5F60 EBF0 jmp $004d5f52
004D5F62 8D45C4 lea eax,[ebp-$3c]
004D5F65 E8B6FEFFFF call TStringsEnumerator.MoveNext
004D5F6A 84C0 test al,al
004D5F6C 75B0 jnz $004d5f1e</code></pre>
<br />
What the hell... well, I smell what is going on here... I know that the compiler generates terrible code when you are using inline with managed function results and I saw GetCurrent being marked as inline - we will change that.
The generated assembly - nice and short!
<br />
<br />
<pre><code class="x86asm">Loops.dpr.66: for s in xStrings do;
004D5F1E 8D55E8 lea edx,[ebp-$18]
004D5F21 8B45F4 mov eax,[ebp-$0c]
004D5F24 E8DBFEFFFF call TStringsHelper.GetEnumerator
004D5F29 EB0B jmp $004d5f36
004D5F2B 8D55FC lea edx,[ebp-$04]
004D5F2E 8D45E8 lea eax,[ebp-$18]
004D5F31 E8DAFEFFFF call TStringsEnumerator.GetCurrent
004D5F36 8D45E8 lea eax,[ebp-$18]
004D5F39 E8EAFEFFFF call TStringsEnumerator.MoveNext
004D5F3E 84C0 test al,al
004D5F40 75E9 jnz $004d5f2b</code></pre>
<br />
It would be a bit more if the enumerator was an object because the the compiler has to insert the cleanup code for the enumerator instance but it would not affect performance on this benchmark because we are only profiling the iteration and not the GetEnumerator and finalize code - that would be a different story.<br />
<br />
Now look at the results:<br />
<br />
210<br />
240<br />
<br />
BOOM! There you have it. The difference is close to 10% and it even changes depending on what loop executes first giving a bit of room for error of measurement. Applying the optimization to only read Count once also gains another 10ms. We can also remove the check on Result and always increase FIndex because when MoveNext returns false accessing Current is not permitted which gives us another few ms.
Now imagine the compiler would properly inline GetCurrent we would certainly see equal or even better performance of the for-in loop in this benchmark.<br />
<br />
Anyhow - the benefit of for-in loops over classic index based for-to loops is not only to avoid dreaded off by one errors (ever forget to -1?) but them being composable - a very important feature when developing code. You can easily add filters and transformations into the chain and then can iterate through all the items without using intermediate storage or writing nested loops - if you are using Spring4D collections you know what I am talking about, right?<br />
<br />
So, what did we learn from this?<br />
<br />
1. Measure and investigate code<br />
2. Some code in the RTL is far from being ideal<br />
3. The 32-bit compiler has much room for optimization<br />
<br />
P.S. <a href="http://hallvards.blogspot.com/2007/10/more-fun-with-enumerators.html">Hallvard did similar investigations</a> more than a decade ago already - makes me a bit sad that the compiler is still not generating more performant code on for-in loops.<br />
<br />
<b>Update (16.06.2019)</b>: It looks like that in recent versions the inline got removed from the GetCurrent function bringing the TStringsEnumerator in range of 10% to the for-to loop.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-19077173066077995972017-05-12T00:15:00.000+02:002020-07-24T23:57:38.655+02:00How to create an operator overload that only accepts nilSince the introduction of Nullable types in Spring4D they had a problem: assigning null to them was cumbersome. Some time ago you could implicitly assign a Variant to them which made it possible to assign Null. However that caused some issue because of implicit Variant type conversion.<br />
<br />
Imagine you had a Nullable<Integer> which should only allow Null or some Integer. But in fact you could assign a string to it - not good. Why was that the case? Well because it had an implicit operator for Variant to Nullable<Integer> and the compiler was too smart converting the string into a Variant to pass that to the operator overload. Result was an exception at runtime - very bad considering that Nullable<T> should be type safe at compile time.<br />
<br />
Next idea was making an overload for Pointer to Nullable<T> but that made it possible to pass all kinds of reference types to a nullable - another source for bugs.<br />
<br />
Before anyone asks, a Clear method was also never an option because the type should be immutable. Think of a property. If you would call Clear on that you would just clear the value returned from the getter.<br />
<br />
For 1.2 I used an explicit type with no state and a static property Null so you can assign or compare with Nullable.Null (<a href="http://www.delphipraxis.net/1321469-post12.html">suggestion by Sir Rufo</a>). An option but it always bugged me because it was not as crisp as just assigning nil like in Swift or null in C#.<br />
<br />
Today it struck me. Use some reference type that you cannot assign anything else but nil to. How is that possible? Make it private so you cannot declare any variable of that type.<br />
<br />
My first attempt was this:<br />
<br />
<pre><code class="delphi">type
Nullable<T> = record
strict private type
Null = class end;
public
class operator Implicit(const value: Null): Nullable<T>;
end;</code></pre>
<br />
That kinda worked. It is impossible to declare a variable of Null and thus you cannot assign anything but nil to it. But wait. You can assign any untyped pointer to it (and declaring $TYPEDADDRESS in the unit the Nullable<T> type is declared in did not work). Well, are there any types that don't allow assigning some untyped pointer to? Yes, events or managed types like dynamic arrays or interfaces. I tried them all and if I did not miss anything it works with a private interface type - I was not able to assign any typed or untyped pointer, interface or object to it.<br />
<br />
So this is what worked (it looks a bit different in Spring.pas because I did not put the type into the generic Nullable<T> type but somewhere else where it is not accessible except from the same unit)<br />
<br />
<pre><code class="delphi">type
Nullable<T> = record
strict private type
Null = interface end;
public
class operator Implicit(const value: Null): Nullable<T>;
end;</code></pre>
<br />
Now (starting with Spring4D 1.2.1) you can finally simply assign nil to a Nullable<T> without losing type safety.<br />
<br />
P.S. By default the Nullable<T> is still in compatibility mode to 1.1 which makes the implicit Variant conversion possible. Take a look into Spring.inc to disable it and make the Nullable<T> completely type safe.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-14835559665244404382017-05-11T21:37:00.000+02:002020-07-25T00:19:12.777+02:00I wish I had dcc32 -dontmakemycodeslowQuick, what is wrong with this code performance wise?<br />
<br />
<pre><code class="delphi">function TWhereIterator<T>.MoveNext: Boolean;
var
current: T;
begin
Result := False;
if fState = STATE_ENUMERATOR then
begin
fEnumerator := fSource.GetEnumerator;
fState := STATE_RUNNING;
end;
if fState = STATE_RUNNING then
begin
while fEnumerator.MoveNext do
begin
current := fEnumerator.Current;
if fPredicate(current) then
begin
fCurrent := current;
Exit(True);
end;
end;
fState := STATE_FINISHED;
fEnumerator := nil;
end;
end;</code></pre>
<br />
Maybe you have read <a href="https://www.delphitools.info/2009/05/06/code-optimization-go-for-the-jugular/">this article</a> in the past (if not, feel free to read it first, I'll be waiting) and know what to look for.<br />
<br />
There is no exception or string here but a function returning an interface. Unfortunately the compiler does not just pass the field to GetEnumerator (Remember: the result of a function where the return type is managed is actually being passed as hidden var parameter) but preserves space on the stack for a temporary variable and makes sure it is properly cleared before returning, every time the method is called. In a tight loop this causes more overhead for every call to MoveNext than the actual work being done in the STATE_RUNNING block.<br />
<br />
Always be aware of code executed under some condition and possible variables created by the compiler. I had another case in the past where I was working with TValue in a big case statement (over TTypeKind) and I ended up with a dozen of temporary variables (for each case label) generated by the compiler, although only one of them was used each time.<br />
<br />
I solved this by putting the STATE_ENUMERATOR block into a method. That plus moving around the state checking a bit caused this code to run almost twice as fast as before. This was the test code:<br />
<br />
<pre><code class="delphi">TEnumerable.Range(1, 100000000)
.Where(
function(const x: Integer): Boolean
begin
Result := x mod 3 = 0
end)
.Count;</code></pre>
<br />
Which now takes around 1 second on my machine - a for-to loop with the same condition takes around 250 ms. Not bad after all given that you hardly do that for some simple integers and a bit more complex filters. By the way it is just as fast - or slow ;) as the same code in C#.<br />
<br />
As you might have noticed development of Spring4D 1.2.1 is ongoing and will contain some bugfixes, a few small new features and significant performance optimization for some collection use cases.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-39662435933705547252017-02-23T18:48:00.000+01:002020-07-25T00:27:24.264+02:00Generics, modules and typeinfo<h3>
The good</h3>
When working in a loosely coupled software architecture you might be using different modules: packages, dynamic link libraries and typically a host executable that loads them.<br />
<br />
If you are using a DI Container it is very easy to extend functionality by just putting that into one module and once its loaded it feeds the container all the nice things it contains and another module might consume these. That all works fine and well.<br />
<br />
<h3>
The bad</h3>
The fun begins as so often when generic types join the game. Let's say we have a generic interface that we are using and this is contained in a runtime package that two other modules require. Everything is fine and both modules can use that interface. However IList<Integer> in one of the modules is not exactly the same as IList<Integer> in the other modules because when you use a generic type the Delphi compiler compiles it for every unit you are using it in and during the linking phase all duplicates are removed. So far so good. But if you have two different modules both now contain IList<Integer> and with that bring their own typeinfo which was compiled into them. When a module is being loaded and uses runtime packages which typically includes the rtl.bpl also they get registered there. Now you have two (or more) of identical types in your application (given you compiled them from the same source state of course).<br />
<br />
Now the DI container comes into play which identifies types by their typeinfo pointer - which as I explained is different across multiple modules.<br />
<br />
You can test this yourself by creating two small projects, an executable and a DLL with the following code:<br />
<br />
<pre><code class="delphi">library MyLibrary;
uses
Spring.Container,
Spring.Collections;
procedure RegisterStuff;
begin
GlobalContainer.RegisterType<IList<Integer>>.DelegateTo(
function: IList<Integer>
begin
Result := TCollections.CreateList<Integer>([1, 2, 3, 4, 5, 6, 7, 8, 9]);
end).AsSingleton;
GlobalContainer.Build;
end;
begin
RegisterStuff;
end.</code></pre>
<br />
<br />
<pre><code class="delphi">program MyProgram;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows,
Spring.Container,
Spring.Collections;
var
i: Integer;
begin
LoadLibrary('MyLibrary.dll');
try
for i in GlobalContainer.Resolve<IList<Integer>> do
Writeln(i);
except
on E: Exception do
Writeln(E.Message);
end;
Readln;
end.</code></pre>
<br />
If you execute MyProgram you will get a nice EResolveException with message 'Cannot resolve type: IList<System.Integer>'. Now that we know what is going on this is no surprise. But how can we solve that?<br />
<br />
<h3>
The ugly</h3>
In our software I used an admittedly scary hack. If you know a bit about the DI container architecture you know that it is very extensible and that you can add so called sub dependency resolvers. These resolvers are being asked if they can resolve a requested type. There are a few builtin ones that are used to resolve dynamic arrays, lists or a TFunc of a registered type. But you can also add your own ones. I wrote a resolver that checks if the requested type could not be resolved by the container already and then looks through its known types to find one with the same full qualified name. Since we are using very explicit named units there is no chance that we accidentally have 2 different types that have the same full qualified name.<br />
<br />
I will not post the code here to not scare you away but since others might also run into this problem (I know at least one person that was building a plugin system by using the DI container and also ran into that problem) this issue will be addressed during the development and refactoring for the DI container in version 1.3 of Spring4D.<br />
<br />
Speaking of version 1.3 - the release of 1.2 is almost there! We are working hard to deliver it shortly after the next Delphi release which will happen ... soonish, I think. ;)<br />
<br />
The release/1.2 branch has been around for a while - please take a look if you haven't already. It contains a lot of new features and bugfixes. I will tell you more about some of the amazing features next time which won't take as long as it took since the last blog post - promise. :)Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-7600220031326812882015-06-26T22:56:00.000+02:002020-07-25T00:28:44.377+02:00Anonymous method overloadingA few weeks ago I learned about <a href="http://knockoutjs.com/">Knockout.js</a> which is a very lightweight JavaScript MVVM library. It contains of some basic features which you can read about on their home page if you are interested.<br />
<br />
<div>
I quickly was amazed how great their concept of observables works (I love design patterns on steroids). However JS is very different from Delphi code so my first version had them declared like this:</div>
<pre><code class="delphi">type
IObservable<T> = interface
function GetValue: T;
procedure SetValue(const value: T);
property Value: T read GetValue write SetValue;
end;</code></pre>
<div>
So working with them looked like this:</div>
<pre><code class="delphi">var
o: IObservable<Integer>;
begin
...
Writeln(o.Value);
o.Value := 42;</code></pre>
<div>
That worked quite well but especially with more and more of these observables in my viewmodel that .Value all the time became annoying.</div>
<br />
<div>
Because in JS functions are first class citizens you can treat them differently so in knockout if you call o() it returns the value of the observable and if you call o(42) is sets the value of the observable.</div>
<br />
<div>
Could Delphi do the same? We know anonymous methods so we could make our IObservable<T> an anonymous method. But we can make it either a function or a procedure - not both. Yes, we can!</div>
<br />
<div>
Anonymous methods are implemented as interface. So in fact TFunc<T> is the same as an interface with a method returning T. And that method is called Invoke. We can even inherit interfaces from an anonymous method type making them an anonymous method themselves. However the compiler prevents you from calling a method on them because in Delphi the parentheses are not required for a call. So what if we inherit from TFunc<T> and add an overload to Invoke?</div>
<br />
<div>
We now have a type that looks like this:</div>
<pre><code class="delphi">type
IObservable<T> = interface(TFunc<T>)
procedure Invoke(const value: T); overload;
end;</code></pre>
<div>
Now we can write code like this:</div>
<pre><code class="delphi">var
o: IObservable<Integer>;
begin
...
Writeln(o);
o(42);</code></pre>
<div>
Now that might look a bit strange at first but once you understand the concept this is really amazing.</div>
<div>
For more information on my KnockoutJS inspired prototype check out the <a href="https://bitbucket.org/sglienke/knockoff">Knockoff</a> project on Bitbucket. It contains some basic examples and tests to show how the observable dependency tracking works and how they can be used to do binding on UI controls. Though keep in mind that it is only a research project so far - but showing much potential.<br />
<br />
Please let me know what you think.</div>
Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-45050090435841918492015-05-07T21:17:00.000+02:002020-07-25T00:30:49.646+02:00Extending the Spring4D DI container - lifetime managersToday <a href="https://bitbucket.org/sglienke/spring4d/issue/107">we got the request to support TInterfacedPersistent</a> in the DI container.<br />
<br />
As you may know TInterfacedPersistence does not do reference counting like TInterfacedObject does - or to be more precise it delegates it to its possible owner. Usually the owner will be nil so it does not do reference counting through its _AddRef and _Release method and often is used for classes that should not do reference counting if you don't want to write your own class (or use Spring.TInterfaceBase for that purpose).<br />
<br />
When working with DI your life will be much easier when using interfaces. But if the implementing class is of TInterfacedPersistence you might have a problem - in form of a memory leak. The container will create the instance and unless you have registered it as singleton (which means the container will only ever create one instance and return this whenever asking for it) not hold a reference to this instance. Because of the missing reference counting it will never destroyed if the interface reference goes out of scope.<br />
<br />
"Then he needs to implement reference counting into that class" my coworker said, when I told him about that request. Good idea but there are a few problems with that. The _AddRef and _Release methods in TInterfacedPersistence are not virtual so you can only "override" them by implementing IInterface again. "Easy enough!" you might say. Yes, but that will only cause reference counting when you query IInterface from the instance but not any other interface that you implemented (and which you more likely will use than IInterface). So you had to re-implement all the other interfaces as well. This will not only add to the instance size (every implemented interface causes the size of each instance to grow by SizeOf(Pointer) - did you know that?) but might not be possible because some implemented methods could be private which would cause the compiler to complain about a missing implementation of the interface.<br />
<br />
So, long story short. If you want to make a class that inherits from TInterfacedPersistent reference counted you have to take another approach - we are talking about a class that you cannot modify for whatever reason: provide an owner that does the reference counting and takes care of destroying it. Usually TInterfacedPersistent looks for the owner in its AfterConstruction method. So what we do is look if FOwnerInterface is not already set and then assign an owner to it - but how, it is private? Fortunately there is a trick using a class helper (also could have used RTTI because that has access to private fields by default) - here is the code:<br />
<br />
<pre><code class="delphi">type
TInterfacedPersistentHelper = class helper for TInterfacedPersistent
private type
TOwner = class(TInterfacedObject)
private
FOwnedInstance: TObject;
protected
procedure AfterConstruction; override;
public
constructor Create(const instance: TInterfacedPersistent);
destructor Destroy; override;
end;
public
procedure EnableRefCount;
end;
procedure TInterfacedPersistentHelper.EnableRefCount;
begin
Assert(not Assigned(Self.FOwnerInterface));
Self.FOwnerInterface := TOwner.Create(Self);
end;
constructor TInterfacedPersistentHelper.TOwner.Create(
const instance: TInterfacedPersistent);
begin
inherited Create;
FOwnedInstance := instance;
end;
destructor TInterfacedPersistentHelper.TOwner.Destroy;
begin
FOwnedInstance.Free;
inherited;
end;
procedure TInterfacedPersistentHelper.TOwner.AfterConstruction;
begin
// assigning to FOwnerInterface will increment this to 0
FRefCount := -1;
end;</code></pre>
<br />
Now how do we get that into our container? The easiest way would be to use the DelegateTo method in the registration:<br />
<br />
<pre><code class="delphi">container.RegisterType<IFoo, TFoo>.DelegateTo(
function: TFoo
begin
Result := TFoo.Create;
Result.EnsureRefCount;
end);</code></pre>
<br />
But what if TFoo would require arguments in its constructor? We would need to Resolve them from the container and inject them. But don't we use the container to get rid of all the construction work?<br />
<br />
So with a minor refactoring - isn't it great when things are easy to change without breaking the entire thing - I added support for custom lifetime managers - I assume I don't have to explain what they do. Let's write our own for handling our TInterfacedPersistent classes - we inherit from the TTransientLifetimeManager from Spring.Container.LifetimeManager:<br />
<br />
<pre><code class="delphi">type
TInterfacedPersistentLifetimeManager = class(TTransientLifetimeManager)
protected
procedure DoAfterConstruction(const instance: TValue); override;
end;
procedure TInterfacedPersistentLifetimeManager.DoAfterConstruction(
const instance: TValue);
begin
inherited;
instance.AsType<TInterfacedPersistent>.EnableRefCount;
end;</code></pre>
<br />
And now we register our type with it (the API is not yet final the method name might change to something more explicit):<br />
<br />
<pre><code class="delphi">container.RegisterType<IFoo, TFoo>.AsCustom<TInterfacedPersistentLifetimeManager></code></pre>
<br />
Now that is nice already. But what if the container could figure out when a class inherits from TInterfacedPersistent and register this lifetime manager? No problem - we need to add an IBuilderInspector to the container for that purpose. What these do is inspect the registered type for certain aspects (there are built-in ones looking for things like what interfaces does a class implement and register them as services if not already explicitly specified or look for members with special attributes). Ours looks like this:<br />
<br />
<pre><code class="delphi">type
TInterfacedPersistentBuilderInspector = class(TInterfacedObject, IBuilderInspector)
protected
procedure ProcessModel(const kernel: IKernel; const model: TComponentModel);
end;
procedure TInterfacedPersistentBuilderInspector.ProcessModel(const kernel: IKernel;
const model: TComponentModel);
begin
if model.ComponentType.IsInstance
and model.ComponentType.AsInstance.MetaclassType.InheritsFrom(TInterfacedPersistent)
and (model.LifetimeType = TLifetimeType.Transient) then
model.LifetimeManager := TInterfacedPersistentLifetimeManager.Create(model);
end;</code></pre>
<br />
And we need to attach that to the container before calling Build (I suggest doing that as the first thing when setting up the container):<br />
<br />
<pre><code class="delphi">container.Kernel.Builder.AddInspector(TInterfacedPersistentBuilderInspector.Create);</code></pre>
<br />
But wait, there is more - the container supports so called extensions. These extensions can be attached to the container to add certain behaviors or features (like adding support for decorator detection or changing the way the container selects constructors to create instances). In our case it is really simple as it just adds one component to the container.<br />
<br />
<pre><code class="delphi">type
TInterfacedPersistentLifetimeExtension = class(TContainerExtension)
protected
procedure Initialize; override;
end;
procedure TInterfacedPersistentLifetimeExtension.Initialize;
begin
Kernel.Builder.AddInspector(TInterfacedPersistentBuilderInspector.Create);
end;</code></pre>
<br />
And attaching the extension is also a one liner.<br />
<br />
<pre><code class="delphi">container.AddExtension<TInterfacedPersistentLifetimeExtension>;</code></pre>
<br />
So in the end with one line we can add support for making TInterfacedPersistent classes reference counted for using them in our DI container powered application.<br />
<br />
"I love it when a plan comes together" ;)<br />
<br />
P.S. with the same technique you can add reference counting to TComponent, by attaching an IVCLComObject interface to it.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-48197886000281553282015-02-21T18:46:00.000+01:002015-02-22T14:31:04.479+01:00Type less - how to use GExperts macro templatesWith all these articles and presentations about the PPL you might have seen this often. Calling TThread.Queue (or TThread.Synchronize if you are doing it wrong ;)) to execute your VCL or FMX related code in the main thread.<br />
<br />
Are you still typing this code <a href="http://xkcd.com/1205/">wasting precious time</a>? Well then this article is for you!<br />
<br />
I see this so often in presentations by people that should know better given their many years of Delphi experience. And to be honest - I am guilty of that too. I could spend way less time writing stupid code (with stupid code I usually refer to boilerplate code like this).<br />
<br />
Disclaimer: I tried using live templates also but man that sucked and invoking the macro always killed the selection in the code editor.<br />
<br />
Open the GExperts macro templates (default shortcut is Shift+Alt+T) and click on configuration.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiu3vk-rioXmL7kifogIzmUQLBf5VlqK5OR3WRgXdTYxHWgBKWvSYGBtscPJajm55tkdLeZ8uDPFPLj7_PO0FOnuP-PmTiNFWhwh7WZ-wv9nNdqOxIIcr5mrMrQWg5fvwCv36HrZuZVlO9/s1600/macro1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiu3vk-rioXmL7kifogIzmUQLBf5VlqK5OR3WRgXdTYxHWgBKWvSYGBtscPJajm55tkdLeZ8uDPFPLj7_PO0FOnuP-PmTiNFWhwh7WZ-wv9nNdqOxIIcr5mrMrQWg5fvwCv36HrZuZVlO9/s1600/macro1.png" /></a></div>
<br />
After you click OK you enter the following code into the editor (with a trailing line break):<br />
<br />
<pre class="brush: delphi">TThread.Queue(nil,
procedure
begin
%SELECTION%|
end);</pre>
<br />
So it looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCFQG8izd0s5cDAZc4IB9M221GXCG5lBG6yzopGxMgKjGY_wm_OE86oV1bKlLg0RkStbXA2rXjsvldLuuQKjO9e9-55Jl9CTpFijYKQ-vKpeN9Dcd5YiOzRU6JNBrQp02LA7PorTyuM6gP/s1600/macro2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCFQG8izd0s5cDAZc4IB9M221GXCG5lBG6yzopGxMgKjGY_wm_OE86oV1bKlLg0RkStbXA2rXjsvldLuuQKjO9e9-55Jl9CTpFijYKQ-vKpeN9Dcd5YiOzRU6JNBrQp02LA7PorTyuM6gP/s1600/macro2.png" /></a></div>
<br />
The %SELECTION% tells the macro to insert the selected text here and the pipe tells it to set the cursor to this position after.<br />
<br />
Click on OK now and select some source you want to invoke in the main thread.<br />
Press the shortcut for macro templates, type queue and press enter, boom, done.<br />
<br />
More time for awesome code or creating more of these templates!<br />
(or to watch funny clips on the internet ...) ;)Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-73649195290766566692015-02-21T16:53:00.001+01:002020-07-25T00:32:38.900+02:00Extending the parallel programming libraryRecently Robert Love blogged about <a href="http://robstechcorner.blogspot.de/2015/02/tpl-ttask-exception-management.html">Exception Management</a> in the PPL (let's please stick to this abbreviation since that is what Embarcadero calls it).<br />
<br />
What I was missing though was handling exceptions in fire and forget tasks since you usually don't have some place that calls wait on them just to get the exception being raised.<br />
<br />
So I quickly hacked together some stuff to show how to use a feature from the <a href="https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx">TLP</a> (that's the .NET one): <a href="https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.continuewith(v=vs.110).aspx">ContinueWith</a>. From its documentation:<br />
<br />
Creates a continuation that executes asynchronously when the target Task completes.<br />
<br />
Easy enough. We got all the pieces to create this - how this can be done <a href="http://youtu.be/rZfux4by0po?t=15m20s">has been shown previously</a>. Too bad he did not make a method of it but hacked it all into a button click event. :(<br />
<br />
Did I just hear anyone say: "Hey, that would have been a use-case for an interface helper, right!?" back there? Well, you are right...<br />
<br />
Enough talk - let's look at the code. Keep in mind this is just some quick and dirty example to show how to extend TTask in a clean way to add new features. Let's hope they will come out of the box with the next version because continuations on tasks are a must have imho.<br />
<br />
<pre><code class="delphi">unit ThreadingEx;
interface
uses
SysUtils,
Threading;
type
TAction<T> = reference to procedure(const arg: T);
TTaskContinuationOptions = (
NotOnCompleted,
NotOnFaulted,
NotOnCanceled,
OnlyOnCompleted,
OnlyOnFaulted,
OnlyOnCanceled
);
ITaskEx = interface(ITask)
['{3AE1A614-27AA-4B5A-BC50-42483650E20D}']
function GetExceptObj: Exception;
function GetStatus: TTaskStatus;
function ContinueWith(const continuationAction: TAction<ITaskEx>;
continuationOptions: TTaskContinuationOptions): ITaskEx;
property ExceptObj: Exception read GetExceptObj;
property Status: TTaskStatus read GetStatus;
end;
TTaskEx = class(TTask, ITaskEx)
private
fExceptObj: Exception;
function GetExceptObj: Exception;
protected
function ContinueWith(const continuationAction: TAction<ITaskEx>;
continuationOptions: TTaskContinuationOptions): ITaskEx;
public
destructor Destroy; override;
class function Run(const action: TProc): ITaskEx; static;
end;
implementation
uses
Classes;
{ TTaskEx }
function TTaskEx.ContinueWith(const continuationAction: TAction<ITaskEx>;
continuationOptions: TTaskContinuationOptions): ITaskEx;
begin
Result := TTaskEx.Run(
procedure
var
task: ITaskEx;
doContinue: Boolean;
begin
task := Self;
if not IsComplete then
DoneEvent.WaitFor;
fExceptObj := GetExceptionObject;
case continuationOptions of
NotOnCompleted: doContinue := GetStatus <> TTaskStatus.Completed;
NotOnFaulted: doContinue := GetStatus <> TTaskStatus.Exception;
NotOnCanceled: doContinue := GetStatus <> TTaskStatus.Canceled;
OnlyOnCompleted: doContinue := GetStatus = TTaskStatus.Completed;
OnlyOnFaulted: doContinue := GetStatus = TTaskStatus.Exception;
OnlyOnCanceled: doContinue := GetStatus = TTaskStatus.Canceled;
else
doContinue := False;
end;
if doContinue then
continuationAction(task);
end);
end;
destructor TTaskEx.Destroy;
begin
fExceptObj.Free;
inherited;
end;
function TTaskEx.GetExceptObj: Exception;
begin
Result := fExceptObj;
end;
class function TTaskEx.Run(const action: TProc): ITaskEx;
var
task: TTaskEx;
begin
task := TTaskEx.Create(nil, TNotifyEvent(nil), action, TThreadPool.Default, nil);
Result := task.Start as ITaskEx;
end;
end.</code></pre>
<br />
So what I did is add the ContinueWith method here that takes the delegate that gets executed when the previous task finished with a certain state. I also added properties for the Status and the Exception that might have been raised.<br />
<br />
How can this be used?<br />
<br />
<pre><code class="delphi"> TTaskEx.Run(
procedure
begin
Sleep(2000);
raise EProgrammerNotFound.Create('whoops')
end)
.ContinueWith(
procedure(const t: ITaskEx)
begin
TThread.Queue(nil,
procedure
begin
ShowMessage(t.ExceptObj.Message);
end);
end, OnlyOnFaulted);</code></pre>
<br />
This executes the first task which Sleeps 2 seconds and then raises an exception. This would not only leak memory (!) but also there is no possibility to handle this exception unless you keep a reference to this task and then call Wait somewhere. But this would limit its use very much. Instead we call ContinueWith now passing the error reporting delegate and OnlyOnFaulted in order to execute this only if the previous task had an error.<br />
<br />
Easy enough, isn't it?Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com1tag:blogger.com,1999:blog-1532151022505671498.post-80060716421345621412015-02-07T15:46:00.002+01:002023-03-04T18:43:02.746+01:00TestInsight - unit testing like a proThe time of teasing is finally over - today I want to show you what I have been working on over the past few weeks:<br />
<br />
<b>TestInsight</b> is an IDE plugin for Delphi (supporting XE and up) that will improve your unit test experience. It integrates nicely into your IDE removing the necessity to deal with some external test runner. It runs and presents the results on demand, when saving or continuously whenever you change the code.<br />
<br />
Navigating to any test is just a matter of double clicking the test in the results overview.<br />
<br />
As I would like you to experience it yourself here is a quick introduction:<br />
<br />
After you downloaded and installed TestInsight you can access it from the Delphi main menu ("View -> TestInsight Explorer").<br />
<br />
To get your test project running with TestInsight you have to add the TESTINSIGHT define to your project - you can do that by context menu in the project manager:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyf2Oqlp-1mffy-n5ozGsHvwaxcqP_d6AB3CN9PEK4ZsgSI5eXiHWwiupz1XCl27ttXjKjUCQjr1TbhCz0KI7TNbOVDdwJeLdd5H0huCgOO2fMp7T_FdsTNTnT6fQqmUk-9hRxqBLxbp6u/s1600/TestInsight_howto_1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyf2Oqlp-1mffy-n5ozGsHvwaxcqP_d6AB3CN9PEK4ZsgSI5eXiHWwiupz1XCl27ttXjKjUCQjr1TbhCz0KI7TNbOVDdwJeLdd5H0huCgOO2fMp7T_FdsTNTnT6fQqmUk-9hRxqBLxbp6u/s1600/TestInsight_howto_1.png" /></a></div>
<br />
The second step will be to add the TestInsight client unit to your project. Currently supported frameworks are DUnit, DUnit2 and DUnitX. So let's say you have a project that is using DUnit you have to add the unit TestInsight.DUnit to your project. Calling RunRegisteredTests (as you know from DUnit) of that unit will execute your tests using TestInsight.<br />
<br />
So a very basic project file will then look like this:<br />
<br />
<pre class="brush: delphi">program MyTests;
uses
TestInsight.DUnit,
MyTestCase in 'MyTestCase.pas';
begin
RunRegisteredTests;
end.</pre>
<br />
After building your project and hitting run it will report the results to the plugin. That will enable you to run your tests on remote machines or even mobile devices.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg69pbexIBLM2nM3hab6jWq8crw_oJiT0aPik-Oa_YNiRndslSEe8reFLyYnri7zzdIdGDRpmlJDAAYAaCg5TKrAyWbQesX3ZXnrQW1DWDgZxiUhgObmt1iISBV4TBLxXhVyXpDsjFAtw76/s1600/TestInsight_howto_2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg69pbexIBLM2nM3hab6jWq8crw_oJiT0aPik-Oa_YNiRndslSEe8reFLyYnri7zzdIdGDRpmlJDAAYAaCg5TKrAyWbQesX3ZXnrQW1DWDgZxiUhgObmt1iISBV4TBLxXhVyXpDsjFAtw76/s1600/TestInsight_howto_2.png" /></a></div>
<br />
Hopefully you don't have errors or warnings in your results. ;) But if you do navigating to the source is just a double click (if you are using madExcept, JclDebug or similar libraries you can even navigate right to the line that caused the error - look into the TestInsight.Client unit on how to enable that)<br />
<br />
For an even better <b>TDD </b>experience you can select the clock or disk icon to execute the tests whenever you change the code (after a small configurable idle time) or save it. You can completely focus on the TDD cycle without being interrupted by running the tests manually.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDiY487Lrbi276MTKzOmLntFEid_-tlI6BXTVZZiZCd3EQpDMgzKivboDYVp6dILpc_o3gZb2V0-fyFcjAPe12tJnrethxaKCW93t5xM6EGtNA4-7HRZsVf9DXSw5qLf8tQ7duRhbaOCRd/s1600/tdd-cycle.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDiY487Lrbi276MTKzOmLntFEid_-tlI6BXTVZZiZCd3EQpDMgzKivboDYVp6dILpc_o3gZb2V0-fyFcjAPe12tJnrethxaKCW93t5xM6EGtNA4-7HRZsVf9DXSw5qLf8tQ7duRhbaOCRd/s1600/tdd-cycle.png" /></a></div>
<br />
<a href="https://files.spring4d.com/TestInsight/latest/TestInsightSetup.zip">Download the setup here</a>. For more information or reporting issues please visit the <a href="http://testinsight.dsharp.org/">official project page</a>.<br />
<br />
And also many thanks to my closed beta testers and coworkers that helped me finding many bugs during development.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-11413441105822938882015-01-29T23:18:00.001+01:002020-07-25T00:33:31.974+02:00New dynamic array type in Spring4D 1.2Today I want to talk about dynamic arrays. Often you might prefer them over a full blown generic list because it avoids creating an instance just to store a couple of elements (which are backed by a dynamic array inside the list anyway - at least for a regular TList<T> regardless if you use those from the RTL or the Spring4D ones).<br />
<br />
But they are tedious to use even with the <a href="http://blog.marcocantu.com/blog/2014_september_dynamic_arrays_delphixe7.html">additional syntax support in Delphi XE7</a>. There are <a href="http://blog.synopse.info/post/2011/03/12/TDynArray-and-Record-compare/load/save-using-fast-RTTI">other implementations</a> that improve dynamic array usage that work even for older versions.<br />
<br />
Spring4D will introduce the new DynamicArray<T> type which is a record type with several methods and operator overloads (take a look at it in our <a href="https://bitbucket.org/sglienke/spring4d/commits/branch/develop">develop branch on Bitbucket</a> where we are busy implementing a lot of awesome things for Spring4D 1.2 release this spring).<br />
<br />
Let's take a quick look at some of the code you can write using the new DynamicArray<T>.<br />
<br />
<pre><code class="delphi">var
arr, arr2: DynamicArray<Integer>;
i: Integer;
begin
{$IFDEF DELPHIXE7_UP}
arr := [1, 2, 3];
arr := arr + arr;
arr := arr + [4, 5];
{$ELSE}
arr.Assign([1, 2, 3]);
arr.Add(arr);
arr.Add([4, 5]);
{$ENDIF}
for i in arr do
Write(i, ' '); // 1 2 3 1 2 3 4 5
Writeln;
arr2.Assign([2, 4]);
if arr2 in arr then
begin
arr := arr - arr2;
for i in arr do
Write(i, ' '); // 1 3 1 2 3 5
Writeln;
end;
arr.Assign([1, 2, 3, 4, 5, 6]);
arr2 := arr.Splice(2, 2, [7, 8, 9]);
for i in arr2 do
Write(i, ' '); // 3 4
Writeln;
for i in arr do
Write(i, ' '); // 1 2 7 8 9 5 6
Writeln;</code></pre>
<br />
It will be available in all versions supported (Delphi 2010 and higher) - only the new syntax to initialize a dynamic array (with the square brackets) is limited to XE7 and higher. But DynamicArray<T> has the add operator and even some more that you don't have with native XE7 dynamic arrays. All methods are continuously optimized for performance as far as possible using pure pascal. DynamicArray<T> does not perform any worse than using the native operations - in some situations even better. Of course it is assignment compatible to TArray<T> by implicit operator overload and will run on all platforms.<br />
<br />
Until next time with more about Spring4D 1.2 or something different but not any less interesting!<br />
<br />
Edit (01.02.2015): renamed TDynArray to DynamicArray<T>Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-8506216071894554872015-01-25T13:52:00.000+01:002020-07-25T00:34:43.470+02:00The Either type for DelphiIn a <a href="http://delphisorcery.blogspot.de/2015/01/never-return-nil-maybe.html">previous post</a> I talked about the Maybe type that similar to the Nullable type allows you to handle values or the state of having no value in a functional approach.<br />
<br />
Today I show you another type that is common in functional programming - the Either type. It allows you to return 2 different types from one function. This could be for example the content of a web request or an error message. In our example we are using similar code as in the previous post and return the result of a division or an error message.<br />
<br />
Here is how the function would look like:<br />
<br />
<pre><code class="delphi">function Divide(x, y: Integer): Either<string,Integer>;
begin
if y = 0 then
Result := 'Division by zero!'
else
Result := x div y;
end;</code></pre>
<br />
As you already can imagine Either is a record type with implicit operator overloading making this code look nice and clean. It allows assigning either a string or an integer value. It then sets a flag that says if it's a left or a right. In cases where you use it for returning errors of the action it is common practice to use the right for the correct result and left for some exception/error message.<br />
<br />
There are different ways to call this - and I am afraid I will shock quite some people - one of them involves our beloved with. I know that people went on some crusade against that thing but in this code I find it very convenient.<br />
<br />
<pre><code class="delphi">with Divide(42, 0) do
case Match of
IsRight: Writeln(Right);
IsLeft: ShowError(Left);
end;
// or
with Divide(42, 0) do
if Match then
Writeln(Right)
else
ShowError(Left);
// or
Divide(42, 0).Fold(
procedure(i: integer)
begin
Writeln(i);
end,
ShowError);</code></pre>
<br />
Either has the members Left, Right and Match which is a Boolean. IsRight and IsLeft are just aliases for True and False to make the code clearer. The Fold method takes two anonymous methods of which it calls one depending on if it's a left or right. ShowError is just a small routine I wrote taking a string and writing it to the console to make the code shorter for this example.
<br />
<br />
Wait a second - don't we have something like that in Delphi already? Yes, its called variant records and it allows something like that. Having a flag field and a dynamic part to store values depending on the flag field. Unfortunately that only works for non nullable value types which renders it pretty useless for this approach.
<br />
So finally here is the code for the Either type:
<br />
<br />
<pre><code class="delphi">const
IsLeft = False;
IsRight = True;
type
Either<TLeft,TRight> = record
strict private
fMatch: Boolean;
fRight: TRight;
fLeft: TLeft;
function GetRight: TRight; inline;
function GetLeft: TLeft; inline;
public
constructor FromLeft(const value: TLeft);
constructor FromRight(const value: TRight);
procedure Fold(const right: TProc<TRight>; const left: TProc<TLeft>); overload;
function Fold<TResult>(const right: TFunc<TRight,TResult>;
const left: TFunc<TLeft,TResult>): TResult; overload;
property Match: Boolean read fMatch;
property Right: TRight read GetRight;
property Left: TLeft read GetLeft;
class operator Implicit(const value: TRight): Either<TLeft,TRight>;
class operator Implicit(const value: TLeft): Either<TLeft,TRight>;
end;
constructor Either<TLeft, TRight>.FromRight(const value: TRight);
begin
fRight := value;
fLeft := Default(TLeft);
fMatch := IsRight;
end;
constructor Either<TLeft, TRight>.FromLeft(const value: TLeft);
begin
fLeft := value;
fRight := Default(TRight);
fMatch := IsLeft;
end;
procedure Either<TLeft, TRight>.Fold(const right: TProc<TRight>;
const left: TProc<TLeft>);
begin
case Match of
IsRight: right(fRight);
IsLeft: left(fLeft);
end;
end;
function Either<TLeft, TRight>.Fold<TResult>(
const right: TFunc<TRight, TResult>;
const left: TFunc<TLeft, TResult>): TResult;
begin
case Match of
IsRight: Result := right(fRight);
IsLeft: Result := left(fLeft);
end;
end;
function Either<TLeft, TRight>.GetRight: TRight;
begin
case fMatch of
IsRight: Result := fRight;
IsLeft: raise EInvalidOpException.Create('Either type has no right value.');
end;
end;
function Either<TLeft, TRight>.GetLeft: TLeft;
begin
case fMatch of
IsRight: raise EInvalidOpException.Create('Either type has no left value.');
IsLeft: Result := fLeft;
end;
end;
class operator Either<TLeft, TRight>.Implicit(
const value: TRight): Either<TLeft, TRight>;
begin
Result.fRight := value;
Result.fLeft := Default(TLeft);
Result.fMatch := IsRight;
end;
class operator Either<TLeft, TRight>.Implicit(
const value: TLeft): Either<TLeft, TRight>;
begin
Result.fLeft := value;
Result.fRight := Default(TRight);
Result.fMatch := IsLeft;
end;</code></pre>
<br />
And finally here is a little teaser of something else I am working on:<br />
<br />
<pre><code class="delphi">Writeln(Divide(42, 3).Fold<string>(
'Result: ' + i.ToString,
'Error: ' + s));</code></pre>
<br />Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-859915238625552902015-01-21T01:02:00.000+01:002020-07-25T00:36:10.823+02:00Never return nil? Maybe!I admit - such headlines are getting old - at least for those that know a bit about functional programming. But for those of you not familiar with the term <a href="http://en.wikipedia.org/wiki/Monad_(functional_programming)">monad</a> it might be new. But don't be scared though by all that functional programming gibberish in that Wikipedia article.<br />
<br />
Today after <a href="http://www.codingindelphi.com/blog/on-the-use-and-acceptance-of-nil/">Nick's heretical post</a> about avoiding nil we had quite some discussion in the Spring4D team. Because given you must not use nil - how do you deal with the state of <i>none</i> in your code? The business logic might define that a valid result is <i>zero</i> or <i>one</i> item. This often is represented as <i>nil</i> or an <i>assigned</i> instance. But then all your code will have to do nil checks whenever you want to perform operations on that item.<br />
<br />
So after some research and reading several articles I found <a href="http://blog.ploeh.dk/2011/02/04/TheBCLalreadyhasaMaybemonad/">this article</a> and I smacked my head because I did not see that obvious solution. Of course having 0 or 1 element is a special case of a collection. So what would be better suited for that than an enumerable?<br />
<br />
I looked around a bit more about and found some more articles with <a href="http://stevegilham.blogspot.de/2011/11/c-null-object-pattern-and-poor-mans.html">example</a> code making use of that idea.<br />
<br />
In fact implementing it in a very similar way in Delphi is not that hard.<br />
<br />
<pre><code class="delphi">program MaybeMonad;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
Maybe<T> = record
strict private
fValue: T;
fHasValue: string;
type
TEnumerator = record
private
fValue: T;
fHasValue: string;
public
function MoveNext: Boolean;
property Current: T read fValue;
end;
public
constructor Create(const value: T);
function GetEnumerator: TEnumerator;
function Any: Boolean; inline;
function GetValueOrDefault(const default: T): T;
class operator Implicit(const value: T): Maybe<T>;
end;
constructor Maybe<T>.Create(const value: T);
begin
case GetTypeKind(T) of
tkClass, tkInterface, tkClassRef, tkPointer, tkProcedure:
if (PPointer(@value)^ = nil) then
Exit;
end;
fValue := value;
fHasValue := '@';
end;
function Maybe<T>.Any: Boolean;
begin
Result := fHasValue <> '';
end;
function Maybe<T>.GetValueOrDefault(const default: T): T;
begin
if Any then
Exit(fValue);
Result := default;
end;
function Maybe<T>.GetEnumerator: TEnumerator;
begin
Result.fHasValue := fHasValue;
Result.fValue := fValue;
end;
class operator Maybe<T>.Implicit(const value: T): Maybe<T>;
begin
Result := Maybe<T>.Create(value);
end;
function Maybe<T>.TEnumerator.MoveNext: Boolean;
begin
Result := fHasValue <> '';
if Result then
fHasValue := '';
end;
function Divide(const x, y: Integer): Maybe<Integer>;
begin
if y <> 0 then
Result := x div y;
end;
function DoSomeDivision(denominator: Integer): Maybe<Integer>;
var
a, b: Integer;
begin
for a in Divide(42, denominator) do
for b in Divide(a, 2) do
Result := b;
end;
var
a: string;
b: Integer;
c: TDateTime;
result: Maybe<string>;
begin
try
for a in TArray<string>.Create('Hello World!') do
for b in DoSomeDivision(0) do
for c in TArray<TDateTime>.Create(EncodeDate(2010, 1, 14)) do
result := a + ' ' + IntToStr(b) + ' ' + DateTimeToStr(c);
Writeln(result.GetValueOrDefault('Nothing'));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.</code></pre>
<br />
Now there are a few things in that code I should explain. The record is pretty straight forward. It holds a value and a flag if its empty or not depending on what gets passed to the constructor. For XE7 we can use the <a href="http://delphisorcery.blogspot.de/2014/10/new-language-feature-in-xe7.html">new intrinsic function</a> GetTypeKind that makes it possible for the compiler to remove the case code path in this particular code because we have a type kind of tkInteger in our example. But if you had an object or interface this code would run and check for nil.<br />
<br />
The class operator makes assigning to a Maybe<T> possible. That's why we could write Result := x div y in the Divide function.<br />
<br />
To enumerate our value we just need to implement the GetEnumerator method that returns an instance with the MoveNext method and a Current property.<br />
<br />
Now for the fun part. "You are missing an assignment in the else part in Divide!" you might say. Well, that is OK because the field in Maybe<T> marking if we have a value is a string which is a managed type and thus gets initialized by the compiler generated code - you might know that trick already from the Spring.Nullable<T> (which is in fact very similar to the Maybe<T>). In case of y being 0 our result will contain an empty string in the fHasValue field - exactly what we want (please don't argue that a division by zero should raise an exception and not return nothing - I did not invent that example - I was just too lazy to come up with my own). ;)<br />
<br />
DoSomeDivision and the 3 nested loops in the main now might look weird at first but if we keep in mind that a Maybe<T> is an enumerable that contains zero or one item it should be clear that these loops won't continue if we have an empty Maybe<T>. And that's the entire trick here. Not checking if there is an item or not. Just perform the operation on a data structure that fits our needs. In this case one that can deal with the state of having or not having an item.<br />
<br />
Of course we could avoid all that Mumbo jumbo and use a dynamic array directly that contains no or one item. But even then our code would still contain any kind of checks (and we could not make sure there are not more than one element in that array). With using our Maybe<T> type we can easily use GetValueOrDefault or Any to perform the check if we have an item or not at the very end of our processing when we evaluate the result but not in the middle of the processing.<br />
<br />
Of course if you are into functional programming you might argue that this is not what makes a monad and that is true but for this particular use case of dealing with zero or one item it does the job very well. Probably more about functional programming approaches in Delphi or other interesting things in the next post.<br />
<br />
Edit: Here is another example which deals with objects:<br />
<br />
<pre><code class="delphi">type
Maybe = record
class function Just<T>(const value: T): Maybe<T>; static;
end;
class function Maybe.Just<T>(const value: T): Maybe<T>;
begin
Result := Maybe<T>.Create(value);
end;
var
window: TForm;
control: TControl;
activeControlName: Maybe<string>;
begin
for window in Maybe.Just(screen.ActiveForm) do
for control in Maybe.Just(window.ActiveControl) do
activeControlName := control.Name;
activeControlName.ForAny(ShowMessage);
end;</code></pre>
<br />
Same effect here: the loop will not execute if the Maybe returned by the Just call contains nil.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0tag:blogger.com,1999:blog-1532151022505671498.post-79197156265693577422015-01-08T02:29:00.001+01:002020-07-25T00:43:34.519+02:00Smart pointers in DelphiYes, I know - <a href="http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html">I am</a> <a href="http://blog.barrkel.com/2008/11/reference-counted-pointers-revisited.html">way too</a> <a href="http://blog.barrkel.com/2008/11/somewhat-more-efficient-smart-pointers.html">late to</a> <a href="https://adugmembers.wordpress.com/2011/12/05/smart-pointers/">that party</a>.<br />
<br />
But this is yet another thing that the language itself is missing but can be done with some tricks as the posts showed. However I felt in all these implementations something was missing. Barry's first implementations were record based which had the disadvantage of always having to write .Value when accessing the actual instance. I prefer his latest approach using an anonymous method type. The approach shown in the ADUG blog uses the constructor constraint to be able to create the instance inside the TSmartPointer<T> which is also appealing.<br />
<br />
However all these were missing something that could be done with a smart pointer as well. Allocate space for a typed pointer and manage that.<br />
<br />
So Spring4D 1.2 (release date tba) will - among many other cool new things - contain a smart pointer implementation.<br />
<br />
Code that looked like this until now:<br />
<br />
<pre><code class="delphi">var
s: TStrings;
begin
s := TStringList.Create;
try
DoSomething(s);
finally
s.Free;
end;
end;</code></pre>
<br />
Will look like this:<br />
<br />
<pre><code class="delphi">var
s: IShared<TStrings>;
begin
s := Shared<TStringList>.New;
DoSomething(s);
end;</code></pre>
<br />
or:
<br />
<br />
<pre><code class="delphi">var
s: Shared<TStrings>;
begin
s := TStringList.Create;
DoSomething(s);
end;</code></pre>
<br />
The first is using the interface based (anonymous method to be more precise) smart pointer where you don't need to write .Value when accessing the instance.<br />
<br />
The second code is using the record based type where you have to write .Value to access the actual instance but it supports implicit casting from and to the actual type. That is why you can directly assign the TStringList to it and also pass it to the routine.<br />
<br />
Here is another example of how to manage some typed pointer:<br />
<br />
<pre><code class="delphi">var
s: IShared<PMyRecord>;
begin
s := Shared<PMyRecord>.New;
s.num := 42;
s.text := 'Hello world';
end;</code></pre>
<br />
In this code the smart pointer will allocate memory for the record and properly finalize and free the memory when it goes out of scope.<br />
<br />
Now it would be cool if on a record type you could specify a property as default so it does member lifting in order to get rid of having to type .Value when accessing the underlying value. Especially when the record does not have any other members anyway. That would indeed make records even more powerful as they are already.<br />
<br />
Edit: Updated for Spring4D 1.2.1 syntax.Stefan Glienkehttp://www.blogger.com/profile/05509404049325709406noreply@blogger.com0