Synopse Open Source - Tag - NextGenmORMot MVC / SOA / ORM and friends2024-02-02T17:08:25+00:00urn:md5:cc547126eb580a9adbec2349d7c65274DotclearLinux support for Delphi to be available end of 2016urn:md5:785e93a45ce015e9ed922adf0f00140d2016-02-08T16:55:00+01:002016-02-08T17:24:14+01:00AB4327-GANDIPascal Programming64bitARCCrossPlatformDelphiLinuxNextGen<p>Marco Cantu, product manager of Delphi/RAD Studio, did publish the <a href="http://community.embarcadero.com/article/news/16211-embarcadero-rad-studio-2016-product-approach-and-roadmap-2">
official RAD Studio 2016 Product Approach and Roadmap</a>.<br />
The upcoming release has a codename known as "BigBen", and should be called
Delphi 10.1 Berlin, as far as I understand.<br />
<a href="http://community.embarcadero.com/uploads/376/2016roadmap.png"><img src="http://community.embarcadero.com/uploads/376/2016roadmap.png" width="500" height="275" /></a></p>
<p>After this summer, another release, which codename is "Godzilla", will
support Linux as a compiler target, in its Delphi 10.2 Tokyo release.<br />
This is a very good news, and some details are given.<br />
I've included those official names to <em>mORMot</em>'s <a href="http://synopse.info/fossil/info/8ebd41a0fe">internal compiler version
detection</a>.<br />
Thanks Marco for the information, and pushing in this direction!</p>
<p>My only concern is that it would be "ARC-enabled"...</p> <p>Details are the following:</p>
<ul>
<li>Apache modules in WebBroker and support for DataSnap and EMS</li>
<li>FireDAC Linux database access</li>
<li>Linux platform support for console apps with IoT support</li>
<li>We will formally support Ubuntu Server & RedHat Enterprise. We will
extend the formally supported Linux distributions list over time as demand
dictates</li>
<li>Windows based IDE with cross-compiler, deploy and debug via PAServer</li>
<li>Linux compilers will be for Intel 64-bit server, LLVM-based and
<strong>ARC-enabled</strong></li>
</ul>
<p>Sounds fine to me to support only Intel 64 bit at first, and Ubuntu/RedHat
support - I guess most other distributions would work, even if not officially
supported.<br />
I'm not sure LLVM would make the running code actually faster, since generated
code from mobile targets were not as efficient as it could have been.<br />
But LLVM would make the compilation phase much slower (10 times slower I
guess), in respect to Win32/OSX compilation timing... we loose one of the great
points of Delphi, which is its almost instant compilation time.</p>
<p>The latest information, 'ARC-enabled' is a concern to me.<br />
What I write here is not an absolute, your experiment and expectations may
differ, and you may rejoice for ARC being enabled.<br />
I'm writing from my own needs.</p>
<p>I still do not understand the benefit of using ARC, in regard to the
compatibility break it would induce with existing code.<br />
All our code base on servers, relies on explicit memory allocation. We mitigate
the code verbosity with simple ownership of objects. Not a big deal, with
modern IDEs.</p>
<p>On a server, disposing resources as soon as possible is a very sensitive
task. Processes do run 24/7, so any leak may be avoided at all cost.<br />
ARC is a fine memory model - we spoke about it <a href="https://blog.synopse.info?post/post/2011/12/08/Avoiding-Garbage-Collector%3A-Delphi-and-Apple-on-the-same-side">
some years ago</a> - but, <a href="http://docwiki.embarcadero.com/RADStudio/Seattle/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers">
as implemented in Delphi NextGen compiler</a>, it is hard to have a single code
base for both ARC and non ARC platforms. A lot of IFDEF is needed, or you need
to use <code>FreeAndNil</code>. IMHO calling <code>DisposeOf</code> may break
most of the benefits of using ARC, since it leave standing pointers in an
unsafe state...<br />
And remote debugging over PAServer for Linux won't be perfect, we can be
sure... If only we may compile with ARC on Windows, it may ease debugging big
applications...</p>
<p>I like very much the FPC approach: they support a lot of targets and
platforms (including Linux since the beginning), and really do their best not
to break existing code.<br />
Having two memory models at once is a real PITA, from the developer point of
view, if they are excluding...<br />
The more I use FPC, the more I like it. Using Lazarus under Linux is very nice.
You are able to debug your application in place...</p>
<p>I hope Delphi for Linux may have both models: ARC-enabled and
ARC-disabled.<br />
Or at least that Delphi for Windows may be ARC-enabled, so that we may share
the same code base, and debug/test under Windows, and cross-compile with
confidence.<br />
From the compiler point of view, it is not a big deal, as far as A. Bauer wrote
(IIRC): they internally have an ARC-enabled compiler for Windows.<br />
From LLVM point of view, it is a feasible, since <a href="http://www.codeography.com/2011/10/10/making-arc-and-non-arc-play-nice.html">you
can set a per-file</a> ARC enabling.</p>
<p>Please do not break compatibility with existing code base!<br />
Please!</p>Performance issue in NextGen ARC model - much better nowurn:md5:1f8e7453fa2aa0f21031e151f438100f2015-09-14T08:42:00+02:002015-09-14T09:43:19+02:00AB4327-GANDIARCblogDelphimultithreadNextGenperformance<p>Back in 2013, I found out an implementation weakness in the implementation
of ARC weak references in the RTL.<br />
A <a href="https://blog.synopse.info?post/post/2013/05/21/Performance-issue-in-NextGen-ARC-model">giant lock
was freezing all threads and cores</a>, so would decrease a lot the performance
abilities of any ARC application, especially in multi thread.</p>
<p><img src="http://m.c.lnkd.licdn.com/mpr/mpr/AAEAAQAAAAAAAAOiAAAAJGU4NThiOTdkLTQ1MzItNDZhZi1iZTBiLTMzZjBlMThjYWJlNQ.png" alt="" /></p>
<p>I just investigated that things are now better.</p> <p>Do you remember that the previous code was the following:</p>
<pre>
<code>procedure TInstHashMap.RegisterWeakRef(Address: Pointer; Instance: Pointer);
var
H: Integer;
Item: PInstItem;
begin
Lock;
try
H := Hash(Instance);
Item := FindInstItem(Instance, H);
if Item = nil then
Item := AddInstItem(Instance, H);
Item.RegisterWeakRef(Address);
finally
Unlock;
end;
end;</code>
</pre>
<p>Now (in Delphi 10 Seatle), it sounds like:</p>
<pre>
<code>procedure TInstHashMap.RegisterWeakRef(Address: Pointer; Instance: Pointer);
var
H, Index: Integer;
Item: PInstItem;
begin
if not FInitialized then Initialize;
H := Hash(Instance);
FBuckets[H].Lock;
try
Item := FindInstItem(Instance, H, Index);
if Item = nil then
Item := AddInstItem(Instance, H, Index);
finally
FBuckets[H].Unlock;
end;
Item.RegisterWeakRef(Address);
end;</code>
</pre>
<p>As you can see, the giant lock has been replaced by a per-bucket lock.<br />
So the giant lock has been replaced by 197 locks.<br />
Much better.</p>
<p>They are still using <code>TMonitor</code>, which <a href="https://www.delphitools.info/2013/05/30/performance-issue-in-nextgen-arc-model/">
may be an issue in some cases</a>, in respect to native OS mutex API.<br />
And also the fact that the lock is released, then re-acquired in
<code>Item.RegisterWeakRef()</code> - not optimal. A single new locked method
at <code>FBuckets[H]</code> level would be more elegant and efficient than
this mess of nested calls, for sure.<br />
Of course, we still prefer a per-class hash list, as implemented in <a href="https://blog.synopse.info?post/post/2012/06/18/Circular-reference-and-zeroing-weak-pointers"><em>mORMot</em> zeroing
weak references</a>, instead of this global hash list.</p>
<p>But it is a good move, in such a critical path of the RTL!<br />
Good to see that some part of the RTL is been polished in newer versions, about
performance and potential bottlenecks.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?pid=17707#p17707">welcome in our
forum, as usual</a>.</p>I do not like people shoot in my foot, do you?urn:md5:75d4d2226543f64f935bf141ddc119202015-05-08T10:10:00+02:002015-05-08T11:11:48+02:00AB4327-GANDIPascal ProgrammingblogDelphigenericsGoodPracticenativeNextGenperformancestringTStringHelper<p>There was some discussion about the new
<code>TStringHelper</code> feature introduced in latest versions of
Delphi.<br />
I <a href="https://plus.google.com/115779584326078289546/posts/RhkWsDP7Q6K">was
told to be some kind of archaic guy</a>, not able to see the benefit of
this.<br />
Reducing opinions to a conservative/progressive approach - another famous 10
kinds of coders - is very reductive.</p>
<p>Of course, this was IMHO unfair and my point was that I have the feeling
that some decisions about the Delphi language and RTL are inadequate.<br />
Some changes are welcome. I enjoy the introduction of generics - even if it is
was painful, and even buggy (<a href="http://stackoverflow.com/q/29906723/458259">do not use TList<T> with
managed record types in XE8</a>!).<br />
But some upcoming changes about the <code>string</code> policy - breaking
everything just because we want to align with mainstream C# or Java habits -
are just non sense to me.<br />
I really think that Embarcadero deciders like to shoot their own foot.<br />
Or - certainly worse - our own feet!</p>
<p><img src="http://www.fitnessmash.com/wp-content/uploads/2014/02/shoot-yourself-foot-internet-marketing-sabotage-online-marketing-success-training.jpg" alt="" /></p>
<p>I will post here some part of the discussion...<br />
So that we may be able to share our ideas.</p> <h3>Round 1</h3>
<p><em>First <a href="https://plus.google.com/115779584326078289546/posts/RhkWsDP7Q6K">original
question</a>.</em></p>
<blockquote>
<p>I'm starting to code using TStringHelper. What do i have to do to be able to
evaluate the expressions in the debugger?</p>
</blockquote>
<p>Short answer: this is a bug in the IDE debugger, which is not in synch with
the compiler.</p>
<p><em>Then I reacted:</em></p>
<blockquote>
<p>Sounds like if eventually I've something in common with the Delphi debugger.
I'm afraid I feel "syntax diabetic" - since I start becoming intolerant to
"syntax sugar"...</p>
<p>Either you make consistent choices, like with Oxygene, and you define a true
object oriented pascal-flavored language, or you should just leave the compiler
alone. String or record helpers, as currently implemented, are half-backed
workarounds. Even the Delphi RTL is not using them. So I'm not surprised the
Delphi debugger is not supporting those.</p>
<p>Thanks to all those helpers inflation, next major "feature" of the compiler
would probably be to deprecate plain functions calls. Then replace the
begin..end with braces, and we would start having a sub-version of C# or Java
(close to what they did offer 10 years ago), and some may be happy with it. I
for sure won't, and would always prefer C# or Java in this respect.</p>
</blockquote>
<p><em>David made a welcome precision:</em></p>
<blockquote>
<p>New code in the RTL is using string helper. Extension methods are a useful
and valuable feature. That the IDE is too lame to understand them fully is an
issue with the IDE. Same for anon methods. The way forward is to fix the IDE.
</p>
</blockquote>
<p><em>Then Stefan shared his opinion :</em></p>
<blockquote>
<p>If you buy an electric screwdriver and it does not work you can either say
"bah, electric screwdrivers are crap, I assemble my new IKEA shelf unit without
one" or you go back to the store, complain and exchange it for a working one.
Unfortunately many Delphi developers including you are doing the first.</p>
</blockquote>
<p><em>Then I had to answer:</em></p>
<blockquote>
<p>You missed my point. What I said is that if I need an electric screwdriver,
I will use Java or C#.<br />
If I follow your comparison, some of the Delphi modifications would be to
suppress half of the handle (RawByteString suppression), add a double handle
(string helpers), change the shaft into a Torx (ARC), make your screwdriver
break if you want to use it on some kind of screws (e.g. the XE8
TList<..> regression), force me to change the hand I'm using (0-based
strings). IMHO datastructures, patterns (e.g. SOLID) and algorithms have much
more importance than code expressiveness, or familiarity with mainstream
"standards".<br />
Using aString.IndexOf(aChar) or pos(aChar,aString) don't change my mind.<br />
BTW, I find your comment offensive. I use C# as a Senior Dev and Architect,
including all its latest features. I'm perhaps less sensitive to hype than
other people. When I saw <a href="https://www.youtube.com/watch?v=GDVAHA0oyJU">Barbara's Liskov presentation
video</a> - I just felt at home.Her POV about Java or C# is refreshing. Her
students code in C or C++, even when they do experimentation about new
languages - just because they know what they do. Or when I learned to code in
Clojure, it opened new perspectives to me, and made my Delphi code somewhat
better. Far away from syntax sugar.</p>
</blockquote>
<p><img src="http://funny-pics.co/wp-content/uploads/funny-cat-with-screwdriver.jpg" alt="" /></p>
<h3>Round 2</h3>
<p><em>Second related <a href="https://plus.google.com/115779584326078289546/posts/RhkWsDP7Q6K">question</a>.</em></p>
<blockquote>
<p>What am i missing here? This is how TStringHelper.Remove looks:</p>
<pre>
<code>function TStringHelper.Remove(StartIndex, Count: Integer): string;
begin
Result := Self;
System.Delete(Result, StartIndex + 1, Count);
end;</code>
</pre>
<p>The Result := Self will copy the string. Do you think this was
intentional?</p>
</blockquote>
<p><em>Olivier explained:</em></p>
<blockquote>
<p>The function returns a string. Also, the input string (self) could be a
const. And, all Stringhelper functions are emulating 0-based strings in
preparation to the future.</p>
</blockquote>
<p><em>Then I reacted (of course, I have nothing against Oliver: I just wanted
to react to those language changes):</em></p>
<blockquote>
<p>We are so lucky to have a so bright future. This is a feature I desperately
expected and pray for every night before going to bed. Next step would be to
make all strings immutable (just because Java or C# do), and break all the COW
benefits. All this is non-sense...</p>
</blockquote>
<p><em>But what I would like to discuss is the following:</em></p>
<blockquote>
<p>BTW, the fact that TStringHelper treat self as immutable is another
testimony of the potential "string immutability" we were talking to.<br />
As a result, this TStringHelper.Remove pseudo-method would be much less
efficient than the system.Delete() function, since both strings would remain in
memory, so there would be a new memory allocation, whereas delete() is able to
change the original string content in place (if its refcount=1, which is very
likely).<br />
If the string is several MB long, the system.Delete() would be very fast (just
a move in-place of the right part of the string, with FastMM4 doing in-place
reallocation), whereas TStringHelper.Remove() would use twice more memory, and
would always move the whole string content.<br />
When I read in the official EMB documentation that "older System unit
string-handling functions are deprecated and support might be removed in the
future", I really think: are those people crazy enough to shot themself in the
foot?</p>
</blockquote>
<h3>Round 3</h3>
<p>OK. Let's go to the ultimate point.<br />
<code>TStringHelper</code> is just broken, even at compiler level.<br />
Its methods do NOT work for sub types.</p>
<p>The following would not compile:</p>
<pre>
type<br /> mystring = type string;<br />var s: string;<br /> s2: mystring;<br />begin<br /> s := '1234';<br /> writeln(s.IndexOf('3'),'=2');<br /> s2 := '1234';<br /> writeln(s2.IndexOf('3'),'=2');
</pre>
<p>It complains on the last line compilation (I tested with XE6), with a:</p>
<pre>
[dcc32 Error] stringhelper.dpr(20): E2018 Record, object or class type required
</pre>
<p>IMHO this is just a show stopper.<br />
If you define your own sub-type, you would not be able to use the string helper
syntax.<br />
Then you would have to use the system.pas classic functions - which will work,
of course.</p>
<p>I use a lot of those custom types, which are IMHO mandatory to write safe
code.<br />
Strong typing, even for simple types, is a very good practice.<br />
If you define</p>
<pre>
type<br /> TTimeInSeconds = type double;<br /> TTimeInMilliseconds = type double;
</pre>
<p>Then the compiler will make a difference between a second and millisecond
variable, your function definitions would be much more efficient.<br />
This is one of the reasons why the NASA would never use JavaScript to write its
software. It may <a href="http://www.computerworld.com/article/2515483/enterprise-applications/epic-failures--11-infamous-software-bugs.html">
lead into disasters</a>...</p>
<p>So, we are told by Delphi documentation to use TStringHelper
methods...<br />
And we can not use it on custom sub types?<br />
I hope you start understanding my point.</p>
<h3>Let's discuss</h3>
<p>For a refreshment about TStringHelper, <a href="https://theroadtodelphi.wordpress.com/2012/09/05/exploring-delphi-xe3-record-helpers-for-simple-types-system-sysutils-tstringhelper/">
check this blog article</a>.</p>
<p>If we look at the whole note in the <a href="http://docwiki.embarcadero.com/Libraries/XE8/en/System.SysUtils.TStringHelper%EF%BB%BF">
official TStringHelper documentation</a>, you would find this:</p>
<blockquote>
<p>Note: You are encouraged to move to the TSringHelper methods when
manipulating strings on all platforms. To preserve backward compatibility with
existing code as much as possible, the longstanding functions in the System
unit that deal with string indices will continue to function with 1-based
strings, and also perform the necessary automatic conversions to do the correct
operations on 0-based strings as well. One example is System.Pos. However,
these older System unit string-handling functions are deprecated and support
might be removed in the future.</p>
</blockquote>
<p>Take some time to listen to Barbara's conference (<a href="https://www.youtube.com/watch?v=GDVAHA0oyJU">link above</a>).<br />
You will find out why I had to react.<br />
We are too much polluted with the "mainstream way of doing things".<br />
Strings being zero-indexed or one-indexed, or having functions or methods...
sincerely no life changer to me. I switch from C# to Delphi strings without a
problem, every day. If I would not be able to, I would not be able to do any
coding, I suspect. On the contrary, I guess it will help fighting against
Alzheimer kind of disease...</p>
<p>If those moves at language and RTL level were only optional, I would not
mind. But we are forced to follow those decisions which I personally find
aberrant.<br />
The main aberrations being that it breaks existing code, and induce performance
penalties in regard to amazing patterns like <a href="http://en.wikipedia.org/wiki/Copy-on-write">COW</a>, which made Delphi
distinctive and appealing to me.<br />
If I have to rewrite the code, just to let it be more Javaish or C#ish, I would
rewrite it in Java or C#.<br />
And do not argue that "immutable strings are multi-thread friendly", which is a
bad joke when you look at the <a href="https://blog.synopse.info?post/post/2013/05/21/Performance-issue-in-NextGen-ARC-model">current RTL
implementation of latest features</a>... far away from multi-thread friendly.
Delphi COW strings were never a bottleneck - you would even be able to change
the memory manager, if needed. The TStringBuilder approach is a need for
immutable strings, and is a <a href="https://www.delphitools.info/2013/10/30/efficient-string-building-in-delphi/3/">
performance nightmare</a> - I can tell you that... Even in .Net they did
tremendous attempts to tune the performance of this C#
StringBuilder... </p>
<p>The more I think about it, the more I like FPC pragmatic approach, about
language evolution, and diverse compile-time flavors, known as <a href="http://www.freepascal.org/docs-html/prog/progsu105.html">modes</a> -
which could be quite powerful, since e.g. it allows to <a href="http://wiki.freepascal.org/FPC_PasCocoa">compile native iOS/MacOS objects</a>
without the need to use runtime level interfaces, as the Delphi compiler
does.<br />
When FPC introduced <a href="http://lists.freepascal.org/fpc-announce/2013-February/000587.html">helpers
for primitive types</a>, they offered a tunable approach via modes, and did not
deprecate the <em>system.pp</em> string functions!</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=2556">welcome in our forum</a>, or
in <a href="https://plus.google.com/u/0/communities/103113685381486591754">the
Google+ forums</a>.</p>Delphi is not a cross-compiler, but a set of cross-compilersurn:md5:ab14ca5a07051338afc18901b94d28812015-04-20T09:46:00+02:002015-04-20T09:11:20+02:00AB4327-GANDIPascal ProgrammingblogCrossPlatformDelphiFireMonkeynativeNextGenRADstringUserInterface<p>It is worth saying again.<br />
I'm not speaking this time about <a href="https://blog.synopse.info?post/post/2013/05/21/Performance-issue-in-NextGen-ARC-model">performance
issues</a>, but about a common misunderstanding of what the latest version of
Delphi offers.</p>
<p><img src="http://docwiki.embarcadero.com/images/RADStudio/XE8/e/thumb/7/7e/MDPreviewWindow1.png/500px-MDPreviewWindow1.png" alt="" /></p>
<p>Since Delphi "<a href="http://wiert.me/2013/09/05/delphi-mobile-nextgen-compiler-unsupported-data-types-means-unsupported-rtti-as-well/">NextGen</a>"
compilers did break the memory model (introducing <a href="http://docwiki.embarcadero.com/RADStudio/XE4/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers">
ARC</a>), and also reducing low-level types (e.g. <a href="https://blog.synopse.info?post/post/2013/05/11/Delphi-XE4-NextGen-compiler-is-disapointing">RawByteString/AnsiString</a>),
we can not say that Delphi is a single cross-compiler.<br />
In practice, it has a set of cross-compiler<strong>s</strong>.</p> <p>In fact, the Delphi compilers have been refactored to be split in:</p>
<ul>
<li>Front-end compiler, which compiles the Delphi source code (but with
"classic" or "NextGen" exclusive flavors);</li>
<li>Back-end compiler, which generates the binary and links into an executable
(may be internal, or LLVM-based).</li>
</ul>
<p>Currently, Delphi has several <a href="http://en.wikipedia.org/wiki/Cross_compiler">cross-compiling</a> tool-chains:
it is able to compile, from a 32 bit Windows IDE, to Win32, Win64, OSX, Android
and iPhone.<br />
Sounds like if all targets would be reachable with a single source.</p>
<p>Sadly, in practice, you can not share directly your code between "classic"
and "NextGen" targets.<br />
If you stick to RAD components, you may not have any particular issue, since
all your memory allocation would be defined by <code>TComponent</code>
ownership, on all targets.<br />
But if you start writing real OOP code, involving classes, you would reach the
breaking changes. The main change being that <code>TObject.Free</code> does
nothing on "NextGen"... In real business code, the class destructor does more
than just releasing the memory: it would also release resources and react to
some business logic. So your code starts to be polluted with <code>$ifdef
NEXTGEN</code> blocks, just to call <code>DisposeOf</code> on one target,
and <code>Free</code> on another.. For no benefit...</p>
<p>On the other hand, <a href="http://www.freepascal.org/">FreePascal</a> (FPC)
is a true single cross-compiler.<br />
You have a single compiler, able to compile several flavors of object pascal
(including most Delphi syntax, and even a <em>Objective Pascal</em> mode
targeting OSX or iOS), then generate assembler and/or binary, using either
internal or external tools. You can cross-compile for Linux under Windows,
or vice-versa, just from the Lazarus IDE. You can compile for Android or
iOS under Windows, Linux or OSX...<br />
Then the RTL would take care of abstracting most of the low-level OS
particularities under Windows, Linux, BSD or several other exotic
systems. And the LCL will generate UI for several targets.<br />
With a single memory model, sharing most of your source code on all targets...
In fact, the object pascal flavors are handled via non exclusive modes, at unit
level, so you have the maximum flexibility at hand.</p>
<p>So please be aware that using Delphi for mobile applications is a nice
possibility.<br />
The IDE is pleasant, and FMX does pretty good work to leverage the diverse UI
targets.<br />
But do not believe that it is all magic, and that a single re-compile will be
sufficient: since the compiler is not the same, you would probably need some
deep knowledge of the platforms, just to manage the diverse memory model, and
reach the right API.</p>
<p>In all cases, do not put your logic (e.g. your SQL) within your mobile
app.<br />
A <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_7">
two-tier architecture</a> is clearly not a good design. You would need to
re-publish your application - which can be painful - just to fix some little
point of logic.<br />
Consider switching to true <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_17">
n-Tier SOA</a> (n>=3), writing your logic on the server side - perhaps using
<em>mORMot -</em> then consume those services on your mobile app, which
would be a rich be logic-less client.</p>Cross-Platform mORMot Clients - Smart Mobile Studiourn:md5:c91c39eba98fded8fbc28d7f62d2954b2014-08-11T20:44:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h3>Smart Mobile Studio client samples</h3>
<p>In addition to <em>Delphi</em> and <em>FreePascal</em> clients, our
framework is able to access any <em>mORMot</em> server from HTML5 / AJAX rich
client, thanks to <em>Smart Mobile Studio</em>.</p>
<h3>Adding two numbers in AJAX</h3>
<p>You can find in <code>SQLite3- CrossPlatform ClientsSmartMobileStudio</code>
a simple client for the <code>TServiceCalculator.Add()</code> interface-based
service.<br />
If your <code>Project14ServerHttpWrapper</code> server is running, you can just
point to the supplied <code>wwwhtml</code> file in the sub-folder.<br />
You would then see a web page with a "<code>Server Connect</code>" button, and
if you click on it, you would be able to add two numbers. This a full HTML5 web
application, connecting securely to your <em>mORMot</em> server, which will
work from any desktop browser (on <em>Windows</em>, <em>Mac OS X</em>, or
<em>Linux</em>), or from any mobile device (either <em>iPhone</em> /
<em>iPad</em> / <em>Android</em> / <em>Windows 8 Mobile</em>).</p>
<p>In order to create the application, we just clicked on "<ins>download as
file</ins>" in the <strong>SmartMobileStudio</strong> link in the web page, and
copied the generated file in the source folder of a new <em>Smart Mobile</em>
project.<br />
Of course, we did copy the needed <code>SynCrossPlatform*.pas</code> units from
the <em>mORMot</em> source code tree into the Smart library folder, as stated
above. Just ensure you run <code>CopySynCrossPlatformUnits.bat</code> from the
<code>CrossPlatform</code> folder at least once from the latest revision of the
framework source code.</p>
<p>Then, on the form visual editor, we added a <code>BtnConnect</code> button,
then a <code>PanelCompute</code> panel with two edit fields named
<code>EditA</code> and <code>EditB</code>, and two other buttons, named
<code>BtnComputeAsynch</code> and <code>BtnComputeSynch</code>. A
<code>LabelResult</code> label will be used to display the computation result.
The <code>BtnConnect</code> is a toggle which will show or display the
<code>PanelCompute</code> panel, which is hidden by default, depending on the
connection status.</p>
<p><img src="https://blog.synopse.info?post/public/mORMot/SmartCalculator.png" alt="" title="Smart Mobile Studio Calculator Service Client Sample, Aug 2014" /></p>
<p>In the <code>Form1.pas</code> unit source code side, we added a reference to
our both <code>SynCrossPlatformREST</code> and <code>mORMotClient</code> units,
and some events to the buttons:</p>
<pre>
<strong>unit</strong> Form1;
<strong>interface
uses</strong>
SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms,
SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Panel,
SmartCL.Controls.<strong>Label</strong>, SmartCL.Controls.EditBox, SmartCL.Controls.Button,
<span style="background-color:yellow;">SynCrossPlatformREST, mORMotClient;</span>
<br /><strong>type</strong>
TForm1 = <strong>class</strong>(TW3Form)
<strong>procedure</strong> BtnComputeSynchClick(Sender: TObject);
<strong>procedure</strong> BtnComputeAsynchClick(Sender: TObject);
<strong>procedure</strong> BtnConnectClick(Sender: TObject);
<strong>private</strong>
<em>{$I 'Form1:intf'}</em>
<strong>protected</strong>
<span style="background-color:yellow;">Client: TSQLRestClientURI;</span>
<strong>procedure</strong> InitializeForm; <strong>override</strong>;
<strong>procedure</strong> InitializeObject; <strong>override</strong>;
<strong>procedure</strong> Resize; <strong>override</strong>;
<strong>end</strong>;
</pre>
<p>The <code>BtnConnect</code> event will connect asynchronously to the server,
using <code>'User'</code> as log-on name, and <code>'synopse'</code> as
password (those as the framework defaults).<br />
We just use the <code>GetClient()</code> function, as published in our
generated <code>mORMotClient.pas</code> unit:</p>
<pre>
<em>/// create a TSQLRestClientHTTP instance and connect to the server
// - it will use by default port 888
// - secure connection will be established via TSQLRestServerAuthenticationDefault
// with the supplied credentials
// - request will be asynchronous, and trigger onSuccess or onError event</em>
<strong>procedure</strong> GetClient(<strong>const</strong> aServerAddress, aUserName,aPassword: <strong>string</strong>;
onSuccess, onError: TSQLRestEvent; aServerPort: integer=SERVER_PORT);
</pre>
<p>It uses two callbacks, the first in case of success, and the second
triggered on failure. On success, we will set the global <code>Client</code>
variable with the <code>TSQLRestClientURI</code> instance just created, then
display the two fields and compute buttons:</p>
<pre>
<strong>procedure</strong> TForm1.BtnConnectClick(Sender: TObject);
<strong>begin</strong>
<strong>if</strong> Client=<strong>nil then</strong>
<span style="background-color:yellow;">GetClient('127.0.0.1','User','synopse',</span>
<span style="background-color:yellow;">lambda (aClient: TSQLRestClientURI)</span>
PanelCompute.Visible := true;
W3Label1.Visible := true;
W3Label2.Visible := true;
LabelConnect.Caption := '';
BtnConnect.Caption := 'Disconnect';
LabelResult.Caption := '';
<span style="background-color:yellow;">Client := aClient;</span>
<strong>end</strong>,
lambda
ShowMessage('Impossible to connect to the server!');
<strong>end</strong>)
<strong>else begin</strong>
PanelCompute.Visible := false;
BtnConnect.Caption := 'Server Connect';
Client.Free;
Client := <strong>nil</strong>;
<strong>end</strong>;
<strong>end</strong>;
</pre>
<p>The <code>GetClient()</code> function expects two callbacks, respectively
<code>onSuccess</code> and <code>onError</code>, which are implemented here
with two <em>SmartPascal</em> <code>lambda</code> blocks.</p>
<p>Now that we are connected to the server, let's do some useful
computation!<br />
As you can see in the <code>mORMotClient.pas</code> generated unit, our
interface-based service can be accessed via a <em>SmartPascal</em>
<code>TServiceCalculator</code> class (and not an <code>interface</code>), with
two variations of each methods: one <em>asynchronous</em> method - e.g.
<code>TServiceCalculator.Add()</code> - expecting success/error callbacks, and
one <em>synchronous</em> (blocking) method - e.g.
<code>TServiceCalculator._Add()</code>:</p>
<pre>
<strong>type</strong>
<em>/// service accessible via http://localhost:888/root/Calculator</em>
<em>// - this service will run in sicShared mode</em>
<em>// - synchronous and asynchronous methods are available, depending on use case</em>
<em>// - synchronous _*() methods will block the browser execution, so won't be</em>
<em>// appropriate for long process - on error, they may raise EServiceException</em>
TServiceCalculator = <strong>class</strong>(TServiceClientAbstract)
<strong>public</strong>
<em>/// will initialize an access to the remote service</em>
<strong>constructor</strong> Create(aClient: TSQLRestClientURI); <strong>override</strong>;
<strong>procedure</strong> Add(n1: integer; n2: integer;
onSuccess: <strong>procedure</strong>(Result: integer); onError: TSQLRestEvent);
<strong>function</strong> _Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>end</strong>;
</pre>
<p>We can therefore execute asynchronously the <code>Add()</code> service as
such:</p>
<pre>
<strong>procedure</strong> TForm1.BtnComputeAsynchClick(Sender: TObject);
<strong>begin</strong>
TServiceCalculator.Create(Client).Add(
StrToInt(EditA.Text),StrToInt(EditB.Text),
lambda (res: integer)
LabelResult.Caption := format('Result = %d',[res]);
<strong>end</strong>,
lambda
ShowMessage('Error calling the method!');
<strong>end</strong>);
<strong>end</strong>;
</pre>
<p>Or execute synchronously the <code>_Add()</code> service:</p>
<pre>
<strong>procedure</strong> TForm1.BtnComputeSynchClick(Sender: TObject);
<strong>begin</strong>
LabelResult.Caption := format('Result = %d',
[TServiceCalculator.Create(Client)._Add(
StrToInt(EditA.Text),StrToInt(EditB.Text))]);
<strong>end</strong>;
</pre>
<p>Of course, the synchronous code is much easier to follow and maintain. To be
fair, the <em>SmartPascal</em> <code>lambda</code> syntax is not difficult to
read nor write. In the browser debugger, you can easily set a break point
within any <code>lambda</code> block, and debug your code.</p>
<p>Note that if the server is slow to answer, your whole web application will
be unresponsive, and the browser may even complain about the page, proposing
the kill its process!<br />
As a consequence, simple services may be written in a synchronous manner, but
your serious business code should rather use asynchronous callbacks, just as
with any modern AJAX application.</p>
<p>Thanks to the <em>Smart Linking</em> feature of its compiler, only the used
version of the unit will be converted to <em>JavaScript</em> and included in
the final <code>index.html</code> HTML5 file. So having both synchronous and
asynchronous versions of each method at hand is not an issue.</p>
<h3>CRUD/ORM remote access</h3>
<p>If the server did have some ORM model, its <code>TSQLRecord</code> classes
will also be part of the <code>mORMotClient.pas</code> generated unit. All
types, even complex record structures, will be marshaled as expected.</p>
<p>For instance, if you run the <code>RegressionTestsServer.dpr</code> server
(available in the same folder), a much more complete unit could be generated
from <code>http://localhost:888/root/wrapper</code>:</p>
<pre>
<strong>type</strong> <em>// define some enumeration types, used below</em>
TPeopleSexe = (sFemale, sMale);
TRecordEnum = (reOne, reTwo, reLast);
<br /><strong>type</strong> <em>// define some record types, used as properties below</em>
TTestCustomJSONArraySimpleArray = <strong>record</strong>
F: <strong>string</strong>;
G: <strong>array of string</strong>;
H: <strong>record</strong>
H1: integer;
H2: <strong>string</strong>;
H3: <strong>record</strong>
H3a: boolean;
H3b: TSQLRawBlob;
<strong>end</strong>;
<strong>end</strong>;
I: TDateTime;
J: <strong>array of record</strong>
J1: byte;
J2: TGUID;
J3: TRecordEnum;
<strong>end</strong>;
<strong>end</strong>;
<strong>type</strong>
<em>/// service accessible via http://localhost:888/root/Calculator</em>
<em>// - this service will run in sicShared mode</em>
<em>// - synchronous and asynchronous methods are available, depending on use case</em>
<em>// - synchronous _*() methods will block the browser execution, so won't be</em>
<em>// appropriate for long process - on error, they may raise EServiceException</em>
TServiceCalculator = <strong>class</strong>(TServiceClientAbstract)
<strong>public</strong>
<em>/// will initialize an access to the remote service</em>
<strong>constructor</strong> Create(aClient: TSQLRestClientURI); <strong>override</strong>;
<strong>procedure</strong> Add(n1: integer; n2: integer;
onSuccess: <strong>procedure</strong>(Result: integer); onError: TSQLRestEvent);
<strong>function</strong> _Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>procedure</strong> ToText(Value: currency; Curr: <strong>string</strong>; Sexe: TPeopleSexe; Name: <strong>string</strong>;
onSuccess: <strong>procedure</strong>(Sexe: TPeopleSexe; Name: <strong>string</strong>); onError: TSQLRestEvent);
<strong>procedure</strong> _ToText(<strong>const</strong> Value: currency; <strong>const</strong> Curr: RawUTF8; <strong>var</strong> Sexe: TPeopleSexe; <strong>var</strong> Name: RawUTF8);
<strong>procedure</strong> RecordToText(Rec: TTestCustomJSONArraySimpleArray;
onSuccess: <strong>procedure</strong>(Rec: TTestCustomJSONArraySimpleArray; Result: <strong>string</strong>); onError: TSQLRestEvent);
<strong>function</strong> _RecordToText(<strong>var</strong> Rec: TTestCustomJSONArraySimpleArray): <strong>string</strong>;
<strong>end</strong>;
<br /> <em>/// map "People" table</em>
TSQLRecordPeople = <strong>class</strong>(TSQLRecord)
<strong>protected</strong>
fFirstName: <strong>string</strong>;
fLastName: <strong>string</strong>;
fData: TSQLRawBlob;
fYearOfBirth: integer;
fYearOfDeath: word;
fSexe: TPeopleSexe;
fSimple: TTestCustomJSONArraySimpleArray;
<em>// those overriden methods will emulate the needed RTTI</em>
<strong>class function</strong> ComputeRTTI: TRTTIPropInfos; <strong>override</strong>;
<strong>procedure</strong> SetProperty(FieldIndex: integer; <strong>const</strong> Value: <strong>variant</strong>); <strong>override</strong>;
<strong>function</strong> GetProperty(FieldIndex: integer): <strong>variant</strong>; <strong>override</strong>;
<strong>public</strong>
<strong>property</strong> FirstName: <strong>string read</strong> fFirstName <strong>write</strong> fFirstName;
<strong>property</strong> LastName: <strong>string read</strong> fLastName <strong>write</strong> fLastName;
<strong>property</strong> Data: TSQLRawBlob <strong>read</strong> fData <strong>write</strong> fData;
<strong>property</strong> YearOfBirth: integer <strong>read</strong> fYearOfBirth <strong>write</strong> fYearOfBirth;
<strong>property</strong> YearOfDeath: word <strong>read</strong> fYearOfDeath <strong>write</strong> fYearOfDeath;
<strong>property</strong> Sexe: TPeopleSexe <strong>read</strong> fSexe <strong>write</strong> fSexe;
<strong>property</strong> Simple: TTestCustomJSONArraySimpleArray <strong>read</strong> fSimple <strong>write</strong> fSimple;
<strong>end</strong>;
</pre>
<p>In the above code, you can see several methods to the
<code>ICalculator</code> service, some involving the complex
<code>TTestCustomJSONArraySimpleArray</code> record type. The
<code>implementation</code> section of the unit will in fact allow
serialization of such records to/from JSON, even with obfuscated
<em>JavaScript</em> field names, via <code>ComputeRTTI() GetProperty()</code>
and <code>SetProperty()</code>.</p>
<p>Some <em>enumerations</em> types are also defined, so will help your
business code be very expressive, thanks to the <em>SmartPascal</em> strong
typing. This is a huge improvement when compared to <em>JavaScript</em> native
weak and dynamic typing.</p>
<p>There is a <code>TSQLRecordPeople</code> class generated, which will map the
following <em>Delphi</em> class type, as defined in the
<code>PeopleServer.pas</code> unit:</p>
<pre>
TSQLRecordPeople = <strong>class</strong>(TSQLRecord)
<strong>protected</strong>
fData: TSQLRawBlob;
fFirstName: RawUTF8;
fLastName: RawUTF8;
fYearOfBirth: integer;
fYearOfDeath: word;
fSexe: TPeopleSexe;
fSimple: TTestCustomJSONArraySimpleArray;
<strong>public</strong>
<strong>class procedure</strong> InternalRegisterCustomProperties(Props: TSQLRecordProperties); <strong>override</strong>;
<strong>published</strong>
<strong>property</strong> FirstName: RawUTF8 <strong>read</strong> fFirstName <strong>write</strong> fFirstName;
<strong>property</strong> LastName: RawUTF8 <strong>read</strong> fLastName <strong>write</strong> fLastName;
<strong>property</strong> Data: TSQLRawBlob <strong>read</strong> fData <strong>write</strong> fData;
<strong>property</strong> YearOfBirth: integer <strong>read</strong> fYearOfBirth <strong>write</strong> fYearOfBirth;
<strong>property</strong> YearOfDeath: word <strong>read</strong> fYearOfDeath <strong>write</strong> fYearOfDeath;
<strong>property</strong> Sexe: TPeopleSexe <strong>read</strong> fSexe <strong>write</strong> fSexe;
<strong>public</strong>
<strong>property</strong> Simple: TTestCustomJSONArraySimpleArray <strong>read</strong> fSimple;
<strong>end</strong>;
</pre>
<p>Here, a complex <code>TTestCustomJSONArraySimpleArray</code> record field
has been published, thanks to a manual
<code>InternalRegisterCustomProperties()</code> registration, as we already
stated above.<br />
You can see that types like <code>RawUTF8</code> were mapped to the standard
<em>SmartPascal</em> <code>string</code> type, as expected, when converted to
the <code>mORMotClient.pas</code> generated unit.</p>
<p>Your AJAX client can then access to this <code>TSQLRecordPeople</code>
content easily, via standard CRUD operations.<br />
See the <code>SQLite3- SmartMobileStudio Client</code> sample, for instance the
following line:</p>
<pre>
<span style="background-color:yellow;">people := new TSQLRecordPeople;</span>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
<span style="background-color:yellow;">assert(client.Retrieve(i,people));</span>
assert(people.ID=i);
assert(people.FirstName='First'+IntToStr(i));
assert(people.LastName='Last'+IntToStr(i));
assert(people.YearOfBirth=id+1800);
assert(people.YearOfDeath=id+1825);
<strong>end</strong>;
</pre>
<p>Here, the <code>client</code> variable is a <code>TSQLRestClientURI</code>
instance, as returned by the <code>GetClient() onSuccess</code> callback
generated in <code>mORMotClient.pas</code>.<br />
You have <code>Add() Delete() Update() FillPrepare()
CreateAndFillPrepare()</code> and <code>Batch*()</code> methods available,
ready to safely access your data from your AJAX client.</p>
<p>If you update your data model on the server, just re-generate your
<code>mORMotClient.pas</code> unit from
<code>http://localhost:888/root/wrapper</code>, then rebuild your <em>Smart
Mobile Studio</em> project to reflect all changes made to your ORM data model,
or your SOA available services.</p>
<p>Thanks to the <em>SmartPascal</em> strong typing, any breaking change of the
server expectations would immediately be reported at compilation, and not at
runtime, as it would with regular <em>JavaScript</em> clients.</p>Cross-Platform mORMot Clients - Delphi / FreePascalurn:md5:1a7657aad2fbf5869145db279b552ec32014-08-11T20:42:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h3>Delphi / FreePascal client samples</h3>
<p>The "<code>27 - CrossPlatform ClientsRegressionTests.dpr</code>" sample
creates a <em>mORMot</em> server with its own ORM data model, containing a
<code>TSQLRecordPeople</code> class, and a set of interface-based SOA services,
some including complex types like a record.</p>
<p>Then this sample uses a generated <code>mORMotClient.pas</code>, retrieved
from the "<ins>download as file</ins>" link of the
<strong>CrossPlatform</strong> template above.<br />
Its set of regression tests (written using a small cross-platform
<code>TSynTest</code> unit test class) will then perform remote ORM and SOA
calls to the <code>PeopleServer</code> embedded instance, over all supported
authentication schemes - see <em><a href="https://blog.synopse.info?post/post/2011/05/24/How-to-implement-RESTful-authentication">Authentication</a></em>:</p>
<pre>
Cross Platform Units for mORMot
---------------------------------
<br />1. Running "Iso8601DateTime"
30003 tests passed in 00:00:018
2. Running "Base64Encoding"
304 tests passed in 00:00:000
3. Running "JSON"
18628 tests passed in 00:00:056
4. Running "Model"
1013 tests passed in 00:00:003
5. Running "Cryptography"
4 tests passed in 00:00:000
<br />Tests failed: 0 / 49952
Time elapsed: 00:00:080
<br />Cross Platform Client for mORMot without authentication
---------------------------------------------------------
<br />1. Running "Connection"
2 tests passed in 00:00:010
2. Running "ORM"
4549 tests passed in 00:00:160
3. Running "ORMBatch"
4564 tests passed in 00:00:097
4. Running "Services"
26253 tests passed in 00:00:302
5. Running "CleanUp"
1 tests passed in 00:00:000
<br />Tests failed: 0 / 35369
Time elapsed: 00:00:574
<br />Cross Platform Client for mORMot using TSQLRestServerAuthenticationNone
-------------------------------------------------------------------------
...
<br />Cross Platform Client for mORMot using TSQLRestServerAuthenticationDefault
----------------------------------------------------------------------------
...
</pre>
<p>The generated <code>mORMotClient.pas</code> unit is used for all
"<code>Cross Platform Client</code>" tests above, covering both ORM and SOA
features of the framework.</p>
<h3>Connection to the server</h3>
<p>You could manually connect to a <em>mORMot</em> server as such:</p>
<pre>
<strong>var</strong> Model: TSQLModel;
Client: TSQLRestClientHTTP;
...
Model := TSQLModel.Create([TSQLAuthUser,TSQLAuthGroup,TSQLRecordPeople]);
Client := TSQLRestClientHTTP.Create('localhost',SERVER_PORT,Model);
<strong>if not</strong> Client.Connect <strong>then</strong>
<strong>raise</strong> Exception.Create('Impossible to connect to the server');
<strong>if</strong> Client.ServerTimeStamp=0 <strong>then</strong>
<strong>raise</strong> Exception.Create('Incorrect server');
<strong>if not</strong> Client.SetUser(TSQLRestAuthenticationDefault,'User','synopse') <strong>then</strong>
<strong>raise</strong> Exception.Create('Impossible to authenticate to the server');
...
</pre>
<p>Or you may use the <code>GetClient()</code> function generated in
<code>mORMotClient.pas</code>:</p>
<pre>
<em>/// create a TSQLRestClientHTTP instance and connect to the server
// - it will use by default port 888
// - secure connection will be established via TSQLRestServerAuthenticationDefault
// with the supplied credentials - on connection or authentication error,
// this function will raise a corresponding exception</em>
<strong>function</strong> GetClient(<strong>const</strong> aServerAddress, aUserName,aPassword: <strong>string</strong>;
aServerPort: integer=SERVER_PORT): TSQLRestClientHTTP;
</pre>
<p>Which could be used as such:</p>
<pre>
<strong>var</strong> Client: TSQLRestClientHTTP;
...
Client := GetClient('localhost','User','synopse')
</pre>
<p>The data model and the expected authentication scheme were included in the
<code>GetClient()</code> function, which will raise the expected
<code>ERestException</code> in case of any connection or authentication
issue.</p>
<h3>CRUD/ORM remote access</h3>
<p>Thanks to <code>SynCrossPlatform*</code> units, you could easily perform any
remote ORM operation on your <em>mORMot</em> server, with the usual
<code>TSQLRest</code> CRUD methods.<br />
For instance, the <code>RegressionTests.dpr</code> sample performs the
following operations</p>
<pre>
<span style="background-color:yellow;">fClient.CallBackGet('DropTable',[],Call,TSQLRecordPeople); <em>// call of method-based service</em></span>
check(Call.OutStatus=HTML_SUCCESS);
people := TSQLRecordPeople.Create; <em>// create a record ORM</em>
<strong>try</strong>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
people.FirstName := 'First'+IntToStr(i);
people.LastName := 'Last'+IntToStr(i);
people.YearOfBirth := i+1800;
people.YearOfDeath := i+1825;
people.Sexe := TPeopleSexe(i <strong>and</strong> 1);
<span style="background-color:yellow;">check(Client.Add(people,true)=i); <em>// add one record</em></span>
<strong>end</strong>;
<strong>finally</strong>
people.Free;
<strong>end</strong>;
...
<span style="background-color:yellow;">people := TSQLRecordPeople.CreateAndFillPrepare(fClient,'',</span>
<span style="background-color:yellow;">'yearofbirth=?',[1900]); <em>// parameterized query returning one or several rows</em></span>
<strong>try</strong>
n := 0;
<span style="background-color:yellow;"><strong>while</strong> people.FillOne <strong>do begin</strong></span>
inc(n);
check(people.ID=100);
check(people.FirstName='First100');
check(people.LastName='Last100');
check(people.YearOfBirth=1900);
check(people.YearOfDeath=1925);
<strong>end</strong>;
check(n=1); <em>// we expected only one record here</em>
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do</strong>
<strong>if</strong> i <strong>and</strong> 15=0 <strong>then</strong>
<span style="background-color:yellow;">fClient.Delete(TSQLRecordPeople,i) <strong>else</strong> <em>// record deletion</em></span>
<strong>if</strong> i <strong>mod</strong> 82=0 <strong>then begin</strong>
people := TSQLRecordPeople.Create;
<strong>try</strong>
id := i+1;
people.ID := i;
people.YearOfBirth := id+1800;
people.YearOfDeath := id+1825;
<span style="background-color:yellow;">check(fClient.Update(people,'YEarOFBIRTH,YEarOfDeath')); <em>// record modification</em></span>
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<strong>end</strong>;
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
<span style="background-color:yellow;">people := TSQLRecordPeople.Create(fClient,i); <em>// retrieve one instance from ID</em></span>
<strong>try</strong>
<strong>if</strong> i <strong>and</strong> 15=0 <strong>then</strong> <em>// was deleted</em>
Check(people.ID=0) <strong>else begin</strong>
<strong>if</strong> i <strong>mod</strong> 82=0 <strong>then</strong>
id := i+1 <strong>else</strong> <em>// was modified</em>
id := i;
Check(people.ID=i);
Check(people.FirstName='First'+IntToStr(i));
Check(people.LastName='Last'+IntToStr(i));
Check(people.YearOfBirth=id+1800);
Check(people.YearOfDeath=id+1825);
Check(ord(people.Sexe)=i <strong>and</strong> 1);
<strong>end</strong>;
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<strong>end</strong>;
</pre>
<p>As we already stated, BATCH mode is also supported, with the classic
<em>mORMot</em> syntax:</p>
<pre>
...
res: TIntegerDynArray;
...
<span style="background-color:yellow;">fClient.BatchStart(TSQLRecordPeople);</span>
people := TSQLRecordPeople.Create;
<strong>try</strong>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
people.FirstName := 'First'+IntToStr(i);
people.LastName := 'Last'+IntToStr(i);
people.YearOfBirth := i+1800;
people.YearOfDeath := i+1825;
<span style="background-color:yellow;">fClient.BatchAdd(people,true);</span>
<strong>end</strong>;
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<span style="background-color:yellow;">fClient.fBatchSend(res)=HTML_SUCCESS);</span>
check(length(res)=200);
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do</strong>
check(res[i-1]=i); <em>// server returned the IDs of the newly created records</em>
</pre>
<p>Those <code>BatchAdd</code> / <code>BatchDelete</code> /
<code>BatchUpdate</code> methods of <code>TSQLRest</code> have the benefit to
introduce at client level:</p>
<ul>
<li>Much higher performance, especially on multi-insertion or multi-update of
data;</li>
<li>Transactional support: <code>TSQLRest.BatchStart()</code> has an optional
<code>AutomaticTransactionPerRow</code> parameter, set to <code>10000</code> by
default, which will create a server-side transaction during the write process,
and an ACID rollback in case of any failure.</li>
</ul>
<p>You can note that all above code has exactly the same structure and methods
than standard <em>mORMot</em> clients.</p>
<p>The generated <code>mORMotClient.pas</code> unit contains all needed
<code>TSQLRecord</code> types, and its used properties, including enumerations
or complex records. The only dependency of this unit are
<code>SynCrossPlatform*</code> units, so would be perfectly cross-platform
(whereas our main <code>SynCommons.pas</code> and <code>mORMot.pas</code> units
do target only <em>Win32</em> and <em>Win64</em>).</p>
<p>As a result, you are able to <em>share</em> server and client code between a
Windows project and any supported platform, even AJAX (see "<em>Smart Mobile
Studio client samples</em>" below). A shared unique code base would eventually
reduce both implementation and debugging time, which is essential to unleash
your business code potential and maximize your ROI.</p>
<h3>Service consumption</h3>
<p>The ultimate goal of the <em>mORMot</em> framework is to publish your
business via a <em>Service-Oriented Architecture (SOA)</em>.<br />
As a consequence, those services should be made available from any kind of
device or platform, even outside the <em>Windows</em> world. The server is able
to generate client wrappers code, which could be used to consume any
<em>Client-Server services via interfaces</em> using any supported
authentication scheme - see <em><a href="https://blog.synopse.info?post/post/2013/06/07/Authentication-and-Authorization">Authentication</a></em>.</p>
<p>Here is an extract of the <code>mORMotClient.pas</code> unit as generated
for the <code>RegressionTests.dpr</code> sample:</p>
<pre>
<strong>type</strong>
<em>/// service implemented by TServiceCalculator</em>
<em>// - you can access this service as such:</em>
<em>// !var aCalculator: ICalculator;</em>
<em>// !begin</em>
<em>// ! aCalculator := TCalculator.Create(aClient);</em>
<em>// ! // now you can use aCalculator methods</em>
<em>// !...</em>
ICalculator = <strong>interface</strong>(IServiceAbstract)
['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
<strong>function</strong> Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>procedure</strong> ToText(<strong>const</strong> Value: currency; <strong>const</strong> Curr: <strong>string</strong>; <strong>var</strong> Sexe: TPeopleSexe; <strong>var</strong> Name: <strong>string</strong>);
<strong>function</strong> RecordToText(<strong>var</strong> Rec: TTestCustomJSONArraySimpleArray): <strong>string</strong>;
<strong>end</strong>;
<br /> <em>/// implements ICalculator from http://localhost:888/root/Calculator</em>
<em>// - this service will run in sicShared mode</em>
TServiceCalculator = <strong>class</strong>(TServiceClientAbstract,ICalculator)
<strong>public</strong>
<strong>constructor</strong> Create(aClient: TSQLRestClientURI); <strong>override</strong>;
<strong>function</strong> Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>procedure</strong> ToText(<strong>const</strong> Value: currency; <strong>const</strong> Curr: <strong>string</strong>; <strong>var</strong> Sexe: TPeopleSexe; <strong>var</strong> Name: <strong>string</strong>);
<strong>function</strong> RecordToText(<strong>var</strong> Rec: TTestCustomJSONArraySimpleArray): <strong>string</strong>;
<strong>end</strong>;
</pre>
<p>As you can see, a dedicated class has been generated to consume the
server-side <code>ICalculator</code> interface-based service, in its own
<code>ICalculator</code> client-side type.<br />
It is able to handle complex types, like enumerations (e.g.
<code>TPeopleSexe</code>) and records (e.g.
<code>TTestCustomJSONArraySimpleArray</code>), which are also defined in the
very same <code>mORMotClient.pas</code> unit.<br />
You can note that the <code>RawUTF8</code> type has been changed into the
standard <em>Delphi</em> / <em>FreePascal</em> <code>string</code> type, since
it is the native type used by our <code>SynCrossPlatformJSON.pas</code> unit
for all its JSON marshaling. Of course, under latest version of <em>Delphi</em>
and <em>FreePascal</em>, this kind of content may be Unicode encoded (either as
UTF-16 for the <code>string</code> = <code>UnicodeString</code> <em>Delphi</em>
type, or as UTF-8 for the <em>FreePascal</em> / <em>Lazarus</em>
<code>string</code> type).</p>
<p>The supplied regression tests show how to use remotely those services:</p>
<pre>
<span style="background-color:yellow;"><strong>var</strong> calc: ICalculator;</span>
i,j: integer;
sex: TPeopleSexe;
name: <strong>string</strong>;
...
<span style="background-color:yellow;">calc := TServiceCalculator.Create(fClient);</span>
check(calc.InstanceImplementation=sicShared);
check(calc.ServiceName='Calculator');
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do</strong>
<span style="background-color:yellow;">check(calc.Add(i,i+1)=i*2+1);</span>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
sex := TPeopleSexe(i <strong>and</strong> 1);
name := 'Smith';
<span style="background-color:yellow;">calc.ToText(i,'$',sex,name);</span>
check(sex=sFemale);
check(name=format('$ %d for %s Smith',[i,SEX_TEXT[i <strong>and</strong> 1]]));
<strong>end</strong>;
...
</pre>
<p>As with regular <em>mORMot</em> client code, a
<code>TServiceCalculator</code> instance is created and is assigned to a
<code>ICalculator</code> local variable. As such, no <code>try ... finally
Calc.Free end</code> block is mandatory here, to avoid any memory leak: the
compiler will create such an hidden block for the <code>Calc:
ICalculator</code> variable scope.</p>
<p>The service-side contract of the <code>ICalculator</code> signature is
retrieved and checked within <code>TServiceCalculator.Create</code>, and would
raise an <code>ERestException</code> if it does not match the contract
identified in <code>mORMotClient.pas</code>.</p>
<p>The cross-platform clients are able to manage the service instance
life-time, especially the <code>sicPerClient</code> mode. In this case, an
implementation class instance will be created on the server for each client,
until the corresponding <code>interface</code> instance will released (i.e. out
of scope or assigned to <code>nil</code>), which will release the server-side
instance - just like with a regular <em>mORMot</em> client code.</p>
<p>Note that all process here is executed <em>synchronously</em>, i.e. in
blocking mode. It is up to you to ensure that your application is able to still
be responsive, even if the server does a lot of process, so may be late to
answer. A dedicated thread may help in this case.</p>Cross-Platform mORMot Clients - Generating Code Wrappersurn:md5:86cc040b427c36a5c9fec81b3bc9a9b32014-08-11T20:41:00+02:002014-08-12T08:54:36+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h3>Generating client wrappers</h3>
<p>Even if it feasible to write the client code by hand, your <em>mORMot</em>
server is able to create the source code needed for client access, via a
dedicated method-based service, and set of <em>Mustache</em>-based templates -
see <em><a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-1">Mustache
template engine</a></em>.</p>
<p>The following templates are available in the
<code>CrossPlatformtemplates</code> folder:</p>
<table>
<tbody>
<tr>
<td><strong>Unit Name</strong></td>
<td><strong>Compiler Target</strong></td>
</tr>
<tr>
<td><code>CrossPlatform.pas.mustache</code></td>
<td><em>Delphi</em> / FPC SynCrossPlatform* units</td>
</tr>
<tr>
<td><code>Delphi.pas.mustache</code></td>
<td><em>Delphi</em> Win32/Win64 <em>mORMot</em> units</td>
</tr>
<tr>
<td><code>SmartMobileStudio.pas.mustache</code></td>
<td>Smart Mobile Studio 2.1</td>
</tr>
</tbody>
</table>
<p>In the future, other wrappers may be added. And you can write your own,
which could be included within the framework source! Your input is warmly
welcome, especially if you want to write a template for <em>Java</em> or
<code>C#</code> client. The generated data context already contains the data
types corresponding to those compilers: e.g. a <em>mORMot</em>'s
<code>RawUTF8</code> field or parameter could be identified as
<code>"typeCS":"string"</code> or <code>"typeJava":"String"</code> in addition
to <code>"typeDelphi":"RawUTF8"</code> and
<code>"typePascal":"string"</code>.</p>
<h3>Publishing the code generator</h3>
<p>By default, and for security reasons, the code generation is not embedded to
your <em>mORMot</em> RESTful server. In fact, the
<code>mORMotWrapper.pas</code> unit will link both <code>mORMot.pas</code> and
<code>SynMustache.pas</code> units, and use <em>Mustache</em> templates to
generate code for a given <code>TSQLRestServer</code> instance.</p>
<p>We will start from the interface-based service <em>Sample code</em> as
defined in the<br />
"<code>SQLite3- Interface based services</code>" folder.<br />
After some minor modifications, we copied the server source code into<br />
"<code>SQLite3- CrossPlatform
ClientsProject14ServerHttpWrapper.dpr</code>":</p>
<pre>
<strong>program</strong> Project14ServerHttpWrapper;
<br /><em>{$APPTYPE CONSOLE}</em>
<br /><strong>uses</strong>
SysUtils,
Classes,
SynCommons,
mORMot,
mORMotHttpServer,
<span style="background-color:yellow;">mORMotWrappers,</span>
Project14Interface <strong>in</strong> '..\14 - Interface based services\Project14Interface.pas';
<br /><strong>type</strong>
TServiceCalculator = <strong>class</strong>(TInterfacedObject, ICalculator)
<strong>public</strong>
<strong>function</strong> Add(n1,n2: integer): integer;
<strong>end</strong>;
<br /><strong>function</strong> TServiceCalculator.Add(n1, n2: integer): integer;
<strong>begin</strong>
result := n1+n2;
<strong>end</strong>;
<br /><strong>var</strong>
aModel: TSQLModel;
aServer: TSQLRestServer;
aHTTPServer: TSQLHttpServer;
<strong>begin</strong>
<em>// create a Data Model</em>
aModel := TSQLModel.Create([],ROOT_NAME);
<strong>try</strong>
<em>// initialize a TObjectList-based database engine</em>
aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,true);
<strong>try</strong>
<span style="background-color:yellow;"><em>// add the http://localhost:888/root/wrapper code generation web page</em></span>
<span style="background-color:yellow;">AddToServerWrapperMethod(aServer,</span>
<span style="background-color:yellow;">['..\..\..\CrossPlatform\templates','..\..\..\..\CrossPlatform\templates']);</span>
<em>// register our ICalculator service on the server side</em>
aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared);
<em>// launch the HTTP server</em>
aHTTPServer := TSQLHttpServer.Create(PORT_NAME,[aServer],'+',useHttpApiRegisteringURI);
<strong>try</strong>
aHTTPServer.AccessControlAllowOrigin := '*'; <em>// for AJAX requests to work</em>
writeln(#10'Background server is running.');
writeln('You can test http://localhost:',PORT_NAME,'/wrapper');
writeln(#10'Press [Enter] to close the server.'#10);
readln;
<strong>finally</strong>
aHTTPServer.Free;
<strong>end</strong>;
<strong>finally</strong>
aServer.Free;
<strong>end</strong>;
<strong>finally</strong>
aModel.Free;
<strong>end</strong>;
<strong>end</strong>.
</pre>
<p>As you can see, we just added a reference to the <code>mORMotWrappers</code>
unit, and a call to <code>AddToServerWrapperMethod()</code> in order to publish
the available code generators.</p>
<p>Now, if you run the <code>Project14ServerHttpWrapper</code> server, and
point your favorite browser to <code>http://localhost:888/root/wrapper</code>
you will see the following page:</p>
<blockquote>
<p><strong>Client Wrappers</strong></p>
<p><strong>Available Templates:</strong></p>
<p>* <strong>CrossPlatform<br /></strong><em>mORMotClient.pas</em> -
<ins>download as file</ins> - <ins>see as text</ins> - <ins>see
template</ins></p>
<p>* <strong>Delphi<br /></strong><em>mORMotClient.pas</em> - <ins>download as
file</ins> - <ins>see as text</ins> - <ins>see template</ins></p>
<p>* <strong>SmartMobileStudio<br /></strong><em>mORMotClient.pas</em> -
<ins>download as file</ins> - <ins>see as text</ins> - <ins>see
template</ins></p>
<p>You can also retrieve the corresponding <ins>template context</ins>.</p>
</blockquote>
<p>Each of the <code>*.mustache</code> template available in the specified
folder is listed here. Links above will allow downloading a client source code
unit, or displaying it as text in the browser. The template can also be
displayed un-rendered, for reference. As true <em>Mustache</em> templates, the
source code files are generated from a <em>data context</em>, which can be
displayed, as JSON, from the "<ins>template context</ins>" link. It may help
you when debugging your own templates. Note that if you modify and save a
<code>.mustache</code> template file, just re-load the "<ins>see as text</ins>"
browser page and your modification is taken in account immediately (you do not
need to restart the server).</p>
<p>Generated source code will follow the template name, and here will always be
downloaded as <code>mORMotClient.pas</code>. Of course, you can change the unit
name for your end-user application. It could be even mandatory if the same
client would access to several <em>mORMot</em> servers at once, which could be
the case in a <em>Service-Oriented Architecture (SOA)</em> project.</p>
<p>Just ensure that you will never change the <code>mORMotClient.pas</code>
generated content by hand. If necessary, you can create and customize your own
<em>Mustache</em> template, to be used for your exact purpose. By design, such
automated code generation will require to re-create the client unit each time
the server ORM or SOA structure is modified. In fact, as stated in the
<code>mORMotClient.pas</code> comment, any manual modification of this file may
be lost after regeneration. You have been warned!</p>
<p>If you feel that the current templates have some issues or need some
enhancements, you are very welcome to send us your change requests on our
forums. Once you are used at it, <em>Mustache</em> templates are fairly easy to
work with. Similarly, if you find out that some information is missing in the
generated <em>data context</em>, e.g. for a new platform or language, we would
be pleased to enhance the official <code>mORMotWrapper.pas</code> process.</p>
<h2>Visit our source code repository</h2>
<p>There is nothing better than some real files.</p>
<p>You can take a look at the following files in our source code
repository:</p>
<ul>
<li>The <a href="https://github.com/synopse/mORMot/tree/master/CrossPlatform/templates"><em>.mustache</em>
template files</a>;</li>
<li>The <em><a href="https://github.com/synopse/mORMot/tree/master/CrossPlatform">SynCrossPlatform*.pas</a></em>
units;</li>
<li>A generated <em><a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients/mORMotClient.pas">
mORMotClient.pas</a></em> unit for <em>Delphi</em> / <em>FPC</em> -
generated from <em><a href="https://github.com/synopse/mORMot/tree/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients">
RegressionTestsServer.dpr</a></em>;</li>
<li>The same <em><a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/29%20-%20SmartMobileStudio%20Client/mORMotClient.pas">
mORMotClient.pas</a></em> unit for <em>Smart Mobile Studio</em> - also
generated from <em><a href="https://github.com/synopse/mORMot/tree/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients">RegressionTestsServer.dpr</a></em>;</li>
<li>Another simplier <a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients/SmartMobileStudio/mORMotClient.pas">
<em>mORMotClient.pas</em></a> unit for <em>Smart Mobile Studio</em> - generated
from <em><a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients/Project14ServerHttpWrapper.dpr">
Project14ServerHttpWrapper.dpr</a></em>.</li>
</ul>
<div>Enjoy!</div>Cross-Platform mORMot Clients - Units and Platformsurn:md5:150a3105836e67fd082f8e9ff8032f532014-08-11T20:40:00+02:002014-08-12T08:50:34+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h2>Involved units</h2>
The units are the following:
<table>
<tbody>
<tr>
<td><strong>Unit Name</strong></td>
<td><strong>Description</strong></td>
</tr>
<tr>
<td><code>SynCrossPlatformREST.pas</code></td>
<td>Main unit, implementing secured ORM and SOA RESTful client</td>
</tr>
<tr>
<td><code>SynCrossPlatformCrypto.pas</code></td>
<td>SHA-256 and crc32 algorithms, used for authentication</td>
</tr>
<tr>
<td><code>SynCrossPlatformJSON.pas</code></td>
<td>Optimized JSON process (not used by <em>Smart</em>)</td>
</tr>
<tr>
<td><code>SynCrossPlatformSpecific.pas</code></td>
<td>System-specific functions, e.g. HTTP clients</td>
</tr>
</tbody>
</table>
<p>This set of units will provide a solid and shared ground for the any kind of
clients:</p>
<ul>
<li>Connection to a <em>mORMot</em> server via HTTP, with full REST
support;</li>
<li>Support of weak or default authentication to secure the transfer - see
<em><a href="https://blog.synopse.info?post/post/2013/06/07/Authentication-and-Authorization">Authentication</a></em>;</li>
<li>Definition of the <code>TSQLRecord</code> class, using RTTI when available
on <em>Delphi</em> or <em>FreePascal</em>, and generated code for <em>Smart
Mobile Studio</em>;</li>
<li>Remote CRUD operations, via JSON and REST, with a
<code>TSQLRestClientURI</code> class, with the same methods as with the
<code>mORMot.pas</code> framework unit;</li>
<li>Optimized <code>TSQLTableJSON</code> class to handle a JSON result table,
as returned by <em>mORMot</em>'s REST server ORM - see <em><a href="https://blog.synopse.info?post/post/2010/07/02/JSON-format-of-a-RESTful-application">JSON (not) expanded
layouts</a></em>;</li>
<li>Batch process - see <em>BATCH sequences for adding/updating/deleting
records</em> - for transactional and high-speed writes;</li>
<li><em>Client-Server services via methods</em> with parameters
marshaling;</li>
<li><em>Client-Server services via interfaces</em> with parameters marshaling
and instance-life time;</li>
<li>Mapping of most supported field types, including e.g. ISO 8601 date/time
encoding, BLOBs and <code>TModTime</code>/<code>TCreateTime</code> - see
<em>TSQLRecord fields definition </em>in the SAD 1.18 pdf;</li>
<li>Complex <code>record</code> types are also exported and consumed via JSON,
on all platforms (for both ORM and SOA methods);</li>
<li>Some cross-platform low-level functions and types definitions, to help
share as much code as possible between your projects.</li>
</ul>
<p>In the future, C# or Java clients may be written.<br />
The <code>CrossPlatform</code> sub-folder code could be used as reference, to
write minimal and efficient clients on any platform. Our REST model is pretty
straightforward and standard, and use of JSON tends to leverage a lot of
potential marshaling issues which may occur with XML or binary formats.</p>
<p>In practice, a code generator embedded in the <em>mORMot</em> server can be
used to create the client wrappers, using the <em>Mustache template engine</em>
included on the server side. With a click, you can generate and download a
client source file for any supported platform. A set of <code>.mustache</code>
templates is available, and can be customized or extended to support any new
platform: any help is welcome, especially for targeting Java or C# clients.</p>
<h2>Available client platforms</h2>
<h3>Delphi FMX / FreePascal FCL cross-platform support</h3>
<p>Latest versions of <em>Delphi</em> include the <em>FireMonkey</em> (FMX)
framework, able to deliver multi-device, true native applications for
<em>Windows</em>, <em>Mac OSX</em>, <em>Android</em> and <em>iOS</em>
(<em>iPhone</em>/<em>iPad</em>).<br />
Our <code>SynCrossPlatform*</code> units are able to easily create clients for
those platforms.</p>
<p>Similarly, these units can be compiled with FreePascal, so that any
<em>mORMot</em> server could be consumed from the numerous supported platforms
of this compiler.</p>
<p>In order to use those units, ensure in your IDE that the
<code>CrossPlatform</code> sub-folder of the <em>mORMot</em> source code
repository is defined in your <em>Library Search Path</em>.</p>
<h3>Cross-platform JSON</h3>
<p>We developed our own cross-platform JSON process unit in
<code>SynCrossPlatformJSON.pas</code>, shared with <em>Delphi</em> and
<em>FreePascal</em>.<br />
In fact, it appears to be easier to use (since it is <code>variant</code>-based
and with <em>late-binding</em> abilities) and run much faster than the official
<code>DBXJSON.pas</code> unit shipped with latest versions of <em>Delphi</em>,
as stated by the "<code>25 - JSON performance</code>" sample:</p>
<pre>
2.2. Table content:
- Synopse crossplatform: 41,135 assertions passed 20.56ms 400,048/s 1.9 MB
- DBXJSON: 41,136 assertions passed 240.84ms 34,159/s 9.9 MB
</pre>
<p>Our <code>TSQLTableJSON</code> class is more than 10 times faster than
standard <code>DBXJSON</code> unit, when processing a list of results as
returned by a <em>mORMot</em> server.<br />
The latest value on each line above is the memory consumption. It should be of
high interest on mobile platforms, where memory allocation tends to be much
slower and sensitive than on Windows (where <em>FastMM4</em> memory manager
does wonders). Our unit consumes 5 times less memory than the RTL's
version.</p>
<p>We did not include <em>XSuperObject</em> here, which is cross-platform, but
performs even worse than <code>DBXJSON</code> in terms of speed. Other
libraries - as <em>SuperObject</em> or <em>dwsJSON</em> - are not
cross-platform.<br />
See <a href="http://blog.synopse.info/post/json-benchmark-delphi-mormot-superobject-dwsjson-dbxjson">
http://blog.synopse.info/post/json-benchmark-delphi-mormot-superobject-dwsjson-dbxjson</a>
for details about this comparison.</p>
<p>A special mention is due to <em>dwsJSON</em>, which performs very well, but
only on Windows, and is slower than <em>mORMot</em>'s implementation:</p>
<pre>
- Synopse ORM loop: 41,135 assertions passed 6.18ms 1,330,153/s 1.1 MB
- Synopse ORM list: 41,135 assertions passed 6.47ms 1,270,775/s 952 KB
- Synopse crossplatform: 41,135 assertions passed 20.56ms 400,048/s 1.9 MB
- Super object properties: 41,136 assertions passed 2.20s 3,739/s 6.3 MB
- dwsJSON: 41,136 assertions passed 32.05ms 256,628/s 4.7 MB
- DBXJSON: 41,136 assertions passed 240.84ms 34,159/s 9.9 MB
</pre>
<p>The "<code>Synopse ORM</code>" lines stand for the
<code>TSQLTableJSON</code> class as implemented in <code>mORMot.pas</code>. It
uses our optimized UTF-8 functions and classes, in-place escaping together with
our <code>RawUTF8</code> custom string type, so that it is 3 times faster than
our cross-platform units, and 40 times than <code>DBXJSON</code>, using much
less memory. Some tricks used by <code>Synopse ORM</code> rely on pointers and
are not compatible with the <em>NextGen</em> compiler or the official
<em>Delphi</em> road-map, so the <code>Synopse crossplatform</code> uses
diverse algorithm, but offers still pretty good performance.</p>
<p>This unit features a <code>TJSONVariantData</code> custom variant type,
similar to <em>TDocVariant custom variant type</em>, available in the main
<em>mORMot</em> framework.<br />
It allows writing such nice and readable code, with late-binding:</p>
<pre>
<strong>var</strong> doc: <strong>variant</strong>;
json,json2: <strong>string</strong>;
...
doc := JSONVariant('{"test":1234,"name":"Joh\"n\r","zero":0.0}');
assert(doc.test=1234);
assert(doc.name='Joh"n'#13);
assert(doc.name2=null);
assert(doc.zero=0);
json := doc; <em>// conversion to JSON text when assigned to a string variable</em>
assert(json='{"test":1234,"name":"Joh\"n\r","zero":0}');
doc.name2 := 3.1415926;
doc.name := 'John';
json := doc;
assert(json='{"test":1234,"name":"John","zero":0,"name2":3.1415926}');
</pre>
<p>The unit is also able to serialize any <code>TPersistent</code> class, i.e.
all published properties could be written or read from a JSON object
representation. It also handles nested objects, stored as
<code>TCollection</code>.<br />
See for instance in the <code>SynCrossPlatformTests</code> unit:</p>
<pre>
<strong>type</strong>
TMainNested = <strong>class</strong>(TCollectionItem)
<strong>private</strong>
fNumber: double;
fIdent: RawUTF8;
<strong>published</strong>
<strong>property</strong> Ident: RawUTF8 <strong>read</strong> fIdent <strong>write</strong> fIdent;
<strong>property</strong> Number: double <strong>read</strong> fNumber <strong>write</strong> fNumber;
<strong>end</strong>;
<br /> TMain = <strong>class</strong>(TPersistent)
<strong>private</strong>
fName: RawUTF8;
fNested: TCollection;
fList: TStringList;
<strong>public</strong>
<strong>constructor</strong> Create;
<strong>destructor</strong> Destroy; <strong>override</strong>;
<strong>published</strong>
<strong>property</strong> Name: RawUTF8 <strong>read</strong> fName <strong>write</strong> fName;
<strong>property</strong> Nested: TCollection <strong>read</strong> fNested;
<strong>property</strong> List: TStringList <strong>read</strong> fList;
<strong>end</strong>;
<br /> obj1 := TMain.Create;
obj2 := TMain.Create;
...
obj1.Name := IntToStr(i);
item := obj1.Nested.Add <strong>as</strong> TMainNested;
item.Ident := obj1.Name;
item.Number := i/2;
obj1.list.Add(obj1.Name);
json := ObjectToJSON(obj1);
<strong>if</strong> i=1 <strong>then</strong>
assert(json='{"Name":"1","Nested":[{"Ident":"1","Number":0.5}],"List":["1"]}');
JSONToObject(obj2,json);
assert(obj2.Nested.Count=i);
json2 := ObjectToJSON(obj2);
assert(json2=json);
...
</pre>
<p>Of course, this serialization feature is used for the
<code>TSQLRecord</code> ORM class.</p>
<p>Due to lack of RTTI, <code>record</code> serialization is supported via some
functions generated by the server with the code wrappers.</p>
<h3>Delphi OSX and NextGen</h3>
<p>In order to be compliant with the <a href="https://blog.synopse.info?post/post/2013/05/11/Delphi-XE4-NextGen-compiler-is-disapointing"><em>NextGen</em></a> revision,
our <code>SynCrossPlatform*</code> units follow the expectations of this new
family of cross-compilers, which targets <em>Android</em> and
<em>iOS</em>.<br />
In particular, we rely only on the <code>string</code> type for text process
and storage, even at JSON level, and we tried to make object allocation
ARC-compatible. Some types have been defined, e.g. <code>THttpBody</code>,
<code>TUTF8Buffer</code> or <code>AnsiChar</code>, to ensure that our units
would compile on all supported platforms.</p>
<p>On <em>Delphi</em>, the <em>Indy</em> library is used for HTTP requests. It
is cross-platform by nature, so should work on any supported system. For SSL
support with <em>iOS</em> and <em>Android</em> clients, please follow
instructions at <a href="http://blog.marcocantu.com/blog/using_ssl_delphi_ios.html">http://blog.marcocantu.com/blog/using_ssl_delphi_ios.html</a>
you may also download the needed <code>libcrypto.a</code> and
<code>libssl.a</code> files from <a href="http://indy.fulgan.com/SSL/OpenSSLStaticLibs.7z">http://indy.fulgan.com/SSL/OpenSSLStaticLibs.7z</a></p>
<p>Feedback is needed for the mobile targets, via FMX.<br />
In fact, we rely for our own projects on <em>Smart Mobile Studio</em> for our
mobile applications, so the <em>Synopse</em> team did not test <em>Delphi
NextGen</em> platforms (i.e. <em>iOS</em> and <em>Android</em>) as deep as
other systems. Your input would be very valuable and welcome, here!</p>
<h3>FreePascal clients</h3>
<p><code>SynCrossPlatform*</code> units support the <em>FreePascal</em>
Compiler, in its 2.7.1 revision.<br />
Most of the code is shared with <em>Delphi</em>, including RTTI support and all
supported types.</p>
<p>Some restrictions apply, though.</p>
<p>Due to a bug in <em>FreePascal</em> implementation of <code>variant</code>
late binding, the following code won't work as expected:</p>
<pre>
doc.name2 := 3.1415926;
doc.name := 'John';
</pre>
<p>Under <em>FreePascal</em>, you have to write:</p>
<pre>
TJSONVariantData(doc)['name2'] := 3.1415926;
TJSONVariantData(doc)['name'] := 'John';
</pre>
<p>In fact, the way late-binding properties are implemented in the
<em>FreePascal</em> RTL forbid to modify the content of the associated
<code>variant</code>. A private copy of the <code>variant</code> is made, which
is not only slower, but disallows modification of its stored value.<br />
Any feedback and help from the <em>FreePascal</em> maintainers may be
welcome!</p>
<p>As a result, direct access to <code>TJSONVariantData</code> instances, and
not a <code>variant</code> variable, would be faster and less error-prone when
using this compiler, until the issue is fixed.</p>
<p>Another issue with the 2.7.1 revision is how the new <code>string</code>
type is implemented.<br />
In fact, if you use a string variable containing an UTF-8 encoded text, then
the following line would reset the result code page to the system code
page:</p>
<pre>
<strong>function</strong> StringToJSON(<strong>const</strong> Text: <strong>string</strong>): <strong>string</strong>;
...
result := '"'+copy(Text,1,j-1); <em>// here FPC 2.7.1 erases UTF-8 encoding</em>
...
</pre>
<p>It sounds like if <code>'"'</code> will force the code page of
<code>result</code> to be not an UTF-8 content.<br />
With <em>Delphi</em>, this kind of statements work as expected, even for
<code>AnsiString</code> values, and <code>'"'</code> constant is handled as
<code>RawByteString</code>. We were not able to find an easy and safe
workaround for FPC yet. Input is welcome in this area, from any expert!</p>
<p>You have to take care of this limitation, if you target the <em>Windows</em>
operating system with FPC (and <em>Lazarus</em>). Under other systems, the
default code page is likely to be UTF-8, so in this case our
<code>SynCrossPlatform*</code> units will work as expected.</p>
<p>We found out the <em>FreePascal</em> compiler to work very well, and result
in small and fast executables. For most common work, timing is comparable with
<em>Delphi</em>. The memory manager is less optimized than <em>FastMM4</em> for
rough simple threaded tests, but is cross-platform and much more efficient in
multi-thread mode: in fact, it has no giant lock, as <em>FastMM4</em>
suffers.</p>
<h2>Smart Mobile Studio support</h2>
<p><em>Smart Mobile Studio</em> - see <a href="http://www.smartmobilestudio.com">http://www.smartmobilestudio.com</a> - is a
complete RAD environment for writing cutting edge HTML5 mobile applications. It
ships with a fully fledged compiler capable of compiling <em>Object Pascal</em>
(in a modern dialect call <em>SmartPascal</em>) into highly optimized and raw
<em>JavaScript</em>.</p>
<p>There are several solutions able to compile to <em>JavaScript</em>.<br />
In fact, we can find several families of compilers:</p>
<ul>
<li><em>JavaScript</em> super-sets, adding optional <em>strong typing</em>, and
classes, close to the <em>ECMAScript Sixth Edition</em>: the current main
language in this category is certainly <em><a href="http://www.typescriptlang.org/">TypeScript</a></em>, designed by Anders
Hejlsberg (father of both the <em>Delphi</em> language and <em>C#</em>), and
published by <em>Microsoft</em>;</li>
<li>New languages, dedicated to make writing <em>JavaScript</em> programs
easier, with an alternative syntax and new concepts (like classes, lambdas,
scoping, splats, comprehensions...): most relevant languages of this family are
<em><a href="http://coffeescript.org/">CoffeeScript</a></em> and <em><a href="https://www.dartlang.org/">Dart</a></em>;</li>
<li>High-level languages, like <em><a href="http://www.gwtproject.org/">Google
Web Toolkit</a></em> (compiling <em>Java</em> code), <em><a href="http://jsil.org/">JSIL</a></em> (from <em>C#</em> via <em>Mono</em>), or
<em><a href="http://smartmobilestudio.com/">Smart Mobile Studio</a></em> (from
<em>object pascal</em>);</li>
<li>Low-level languages, like <em><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Emscripten">Emscripten</a></em>
(compiling C/C++ from LLVM byte-code, using <em>asm.js</em>).</li>
</ul>
<p>Of course, from our point of view, use of modern <em>object pascal</em> is
of great interest, since it will leverage our own coding skills, and make us
able to share code between client and server sides.</p>
<h3>Beyond JavaScript</h3>
<p>The so-called <em><a href="http://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language">Smart
Pascal</a></em> language brings strong typing, true OOP to <em>JavaScript</em>,
including classes, partial classes, interfaces, inheritance, polymorphism,
virtual and abstract classes and methods, helpers, closures, lambdas,
enumerations and sets, getter/setter expressions, operator overloading,
contract programming. But you can still unleash the power of
<em>JavaScript</em> (some may say "the good parts"), if needed: the
<code>variant</code> type is used to allow dynamic typing, and you can write
some <em>JavaScript</em> code as an <code>asm .. end</code> block.<br />
See <a href="http://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language">http://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language</a></p>
<p>The resulting HTML5 project is self-sufficient with no external
<em>JavaScript</em> library, and is compiled as a single
<code>index.html</code> file (including its <code>css</code>, if needed). The
<em>JavaScript</em> code generated by the compiler (written in <em>Delphi</em>
by Eric Grange), is of very high quality, optimized for best execution
performance (either in JIT or V8), has low memory consumption, and can be
compressed and/or obfuscated.</p>
<p>The <code>SmartCL</code> runtime library encapsulate HTML5 APIs in a set of
pure pascal classes and functions, and an IDE with an integrated form designer
is available. You can debug your application directly within the IDE (since
revision 2.1 - even if it is not yet always stable) or within your browser (IE,
Chrome or FireBug have great debuggers), with step-by-step execution of the
object pascal code (if you define "<em>Add source map (for debugging)</em>" in
<code>Project Options</code> / <code>Linker</code>).</p>
<p>Using a third-party tool like <em>PhoneGap</em> - see <a href="http://phonegap.com">http://phonegap.com</a> - you would be able to supply
your customers with true native <em>iOS</em> or <em>Android</em> applications,
running without any network, and accessing the full power of any modern
<em>Smart Phone</em>. Resulting applications will be much smaller in size than
the one generated by <em>Delphi</em> FMX (a simple <em>Smart</em> RESTful
client with a login form and ORM + SOA tests is zipped as 40 KB), and will work
seamlessly on all HTML5 platforms, including most mobile (like Windows Phone,
Blackberry, Firefox OS, or webOS) or desktop (Windows, Linux, BSD, MacOS)
architectures.</p>
<p><em>Smart Mobile Studio</em> is therefore a great platform for implementing
rich client-side AJAX or <em>Mobile</em> applications, to work with our
client-server <em>mORMot</em> framework.</p>
<h3>Using Smart Mobile Studio with mORMot</h3>
<p>There is no package to be installed within the <em>Smart Mobile Studio</em>
IDE. The client units will be generated directly from the <em>mORMot</em>
server.<br />
Any edition of <em>Smart</em> - see <a href="http://smartmobilestudio.com/feature-matrix">http://smartmobilestudio.com/feature-matrix</a>
- is enough: you do not need to pay for the <em>Enterprise</em> edition to
consume <em>mORMot</em> services. But of course, the <em>Professionnal</em>
edition is recommended, since the <em>Basic</em> edition does not allow to
create forms from the IDE, which is the main point for an AJAX application.</p>
<p>In contrast to the wrappers available in the
<em>Entreprise </em>edition of Smart, for accessing <em>RemObjects</em> or
<em>DataSnap</em> servers, our <em>mORMot</em> clients are 100% written in the
<em>SmartPascal</em> dialect. There is no need to link an external
<code>.js</code> library to your executable, and you will benefit of the
obfuscation and smart linking features of the Smart compiler.</p>
<p>The only requirement is to copy the <em>mORMot</em> cross-platform units to
your <em>Smart Mobile Studio</em> installation. This can be done in three
<code>copy</code> instructions:</p>
<pre>
xcopy SynCrossPlatformSpecific.pas "c:\ProgramData\Optimale Systemer AS\Smart Mobile Studio\Libraries" /Y
xcopy SynCrossPlatformCrypto.pas "c:\ProgramData\Optimale Systemer AS\Smart Mobile Studio\Libraries" /Y
xcopy SynCrossPlatformREST.pas "c:\ProgramData\Optimale Systemer AS\Smart Mobile Studio\Libraries" /Y
</pre>
<p>You can find a corresponding BATCH file in the <code>CrossPlatform</code>
folder, and in <code>SQLite3- SmartMobileStudio
ClientCopySynCrossPlatformUnits.bat</code>.</p>
<p>In fact, the <code>SynCrossPlatformJSON.pas</code> unit is not used under
<em>Smart Mobile Studio</em>: we use the built-in JSON serialization features
of <em>JavaScript</em>, using <code>variant</code> dynamic type, and the
standard <code>JSON.Stringify()</code> and <code>JSON.Parse()</code>
functions.</p>RoadMap refreshedurn:md5:6d3da02799d7bf1bd2ca1207fb8b56672014-06-27T14:22:00+02:002014-06-27T21:06:04+02:00AB4327-GANDImORMot FrameworkblogDelphiFreePascalKylixLinuxmORMotNextGenRoadMap<p>We did some cleaning in the <a href="http://synopse.info/fossil/wiki?name=RoadMap"><em>mORMot</em> official
RoadMap</a>.<br />
Now feature requests tickets will detail all <a href="http://synopse.info/fossil/rptview?rn=5">to-do items we would like to
implement</a>.</p>
<p><img src="http://2.bp.blogspot.com/_PGvSoaafXiI/S8sDT-S1A8I/AAAAAAAAIBo/GblDpuL3yns/s1600/montana-marmot-road-exit.jpg" alt="" width="318" height="150/" /></p>
<p>Current framework <em>RoadMap</em> and implementation is in fact going into
a pragmatic direction.<br />
No need to make all framework's unit compatible at once: so we introduced some
client-dedicated units, without any dependency on <em>SynCommons.pas</em>.</p>
<p>We would like to implement (in this order):</p>
<ul>
<li>Cross-platform clients (Delphi FMX, SmartMobileStudio, FPC) <a href="http://synopse.info/fossil/info/09ae8513eb">[09ae8513eb]</a> <a href="http://synopse.info/fossil/info/168eb753e5">[168eb753e5]</a> <a href="http://synopse.info/fossil/info/d7e5521da5">[d7e5521da5]</a>;</li>
<li>MVC web support <a href="http://synopse.info/fossil/info/bd94c11ab1">[bd94c11ab1]</a>;</li>
<li>Cross-platform server via FPC <a href="http://synopse.info/fossil/info/3a79adc10f">[3a79adc10f]</a>;</li>
<li>Event-driven features <a href="http://synopse.info/fossil/info/aa230e5299">[aa230e5299]</a>.</li>
</ul>
<p>The <a href="http://synopse.info/fossil/tree?name=CrossPlatform">CrossPlatform folder</a>
already contains units which compile under all Delphi compilers (VCL and FMX),
and FPC.</p>
<p>But perhaps we would move the server to Linux, either via FPC, or using
Delphi itself!</p> <p>Recently, Linux support <a href="http://edn.embarcadero.com/article/43677">appeared on the official Embarcadero
Delphi roadmap</a>, as "<em>Linux server support for DataSnap and WebBroker,
including RTL and database access</em>".</p>
<p>Perhaps we would consider using this approach, if it is quicker to implement
than FPC.<br />
The current FPC implementation is slow down by diverse RTTI details, whereas we
may guess that Delphi would be more consistent on this point.</p>
<p>But if the Linux future Delphi compiler is NEXTGEN-based, it would be a
<a href="https://blog.synopse.info?post/post/2013/05/11/Delphi-XE4-NextGen-compiler-is-disapointing">show-stopper to
us</a>, since we reject the backward incompatibility breaks introduced by this
compiler branch.</p>
<p>We just hope that the future Linux Delphi compiler will be based on the main
x86/x64 compiler, just like Mac OS X compiler, not on the NEXTGEN compiler:
only the linking part may be diverse. Since they already add Linux support
years ago with Kylix, I hope they would be able to cross-compile to Linux
platforms.</p>
<p>Feedback <a href="http://synopse.info/forum/viewtopic.php?id=1730">is
welcome on our forum</a>, as usual.<br />
You could also enhance and follow the corresponding <a href="http://synopse.info/fossil/tktview/b07ceddab63169f3">feature request
ticket</a>.</p>Benchmarking Mustache libraries: native SynMustache vs mustache.js/SpiderMonkeyurn:md5:a30eef5edb874fe978a73b842c1195172014-05-07T17:53:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkARCDelphiGarbageCollectorGoodPracticeJavaScriptJSONmORMotMustacheNextGenperformanceSourcestring<p>I just wrote a small sample program, for benchmarking Mustache libraries:
native <em><a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-3">SynMustache</a></em>
vs <a href="https://github.com/janl/mustache.js/">mustache.js</a> running on
<a href="https://blog.synopse.info?post/post/2014/04/07/JavaScript-support-in-mORMot-via-SpiderMonkey">SpiderMonkey
24</a>...</p>
<p>And the winner is ...<em>SynMustache</em>, which is 10 times faster, uses
almost no memory during process, and handles inlined
<code>{{>partials}}</code> natively (whereas we have to handle them manually
with <em>mustache.js</em>)!</p>
<p><img src="http://www.ecovelo.info/images/road-moustache-485.jpg" alt="" /></p>
<p>Who says that <a href="https://blog.synopse.info?post/post/2013/07/24/Tempering-Garbage-Collection">Garbage Collection</a> and
<a href="https://blog.synopse.info?post/post/2013/05/11/Delphi-XE4-NextGen-compiler-is-disapointing">immutable
strings</a> in modern JITted runtimes are faster than "native" Delphi
applications?<br />
Are you still preferring the "<a href="https://blog.synopse.info?post/post/2013/05/21/Performance-issue-in-NextGen-ARC-model">NextGen</a>"
roadmap?</p> <p>The program is pretty simple.<br />
It is a good sample of <a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-2">Mustache
rendering principles</a>.</p>
<p>It renders a recursive template taken from <a href="https://blog.synopse.info?post/2014/05/07/Benchmarking-Mustache-libraries%3A-native-SynMustache-vs-mustache.js/%20http://boilingplastic.com/using-mustache-templates-for-javascript-practical-examples">
a well known web site</a>:</p>
<pre>
<h2>Example 6 : Recursively binding data to templates</h2>
<br /> <h3>Organization Structure</h3>
{{> person}}
<br />{{<person}}
<div>
<b>{{name}}</b> ({{title}})
<div style='padding-left: 15px; padding-top: 5px;'>
{{#manages}}
{{>person}}
{{/manages}}
</div>
</div>
{{/person}}
</pre>
<p>This template is executed on the following context data:</p>
<pre>
{ title : "President", name : "Perry President", manages : [
{ title : "CTO", name : "Janet TechOff", manages : [
{ title : "Web Architect", name : "Hari Archie", manages : [
{ title : "Senior Developer", name : "Brenda Senior", manages : []},
{ title : "Developer", name : "Roger Develo", manages : []},
{ title : "Junior Developer", name : "Jerry Junior", manages : []}
]}
]},
{ title : "HRO", name : "Harold HarOff", manages : [
{ title : "HR Officer", name : "Susan McHorror", manages : []},
{ title : "HR Auditor", name : "Audrey O'Fae", manages : []}
]}
]}
</pre>
<div>It will render it in loop, and then we compare the speed...</div>
<pre>
<span style="font-family: 'DejaVu Sans', 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif; font-size: 12px;">For <em>SynMustache</em>, we got:</span>
</pre>
<pre>
<span style="font-family: 'DejaVu Sans', 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif; font-size: 12px;"><a href="https://blog.synopse.info?post/public/ScreenShots/BenchmarkSynMustache.png"><img src="https://blog.synopse.info?post/public/ScreenShots/.BenchmarkSynMustache_m.jpg" alt="" title="SynMustache Benchmark, mai 2014" /></a><br /></span>
</pre>
<pre>
<span style="font-family: 'DejaVu Sans', 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif; font-size: 12px;">For <em>mustache.js</em> over <em>SpiderMonkey</em>, we got:</span>
</pre>
<pre>
<a href="https://blog.synopse.info?post/public/ScreenShots/BenchmarkMustacheJS.png"><img src="https://blog.synopse.info?post/public/ScreenShots/.BenchmarkMustacheJS_m.jpg" alt="" title="mustache.js Benchmark, mai 2014" /></a>
</pre>
<p>The native Delphi version, included within our <em>mORMot</em>
framework, is 10 times faster than the <em>JavaScript</em> optimized
library.<br />
And if you look at the process explorer during the run, <em>SynMustache</em>
does not have any memory increase, whereas the JavaScript engine will
continuously increase/decrease its memory usage, due to the garbage
collector...</p>
<p>I always prefer such benchmarks, pretty close to real world process, in
comparison to optimistic and unrealistic benchmarks like a Mandelbrot
computation.<br />
Who is calculating a fractal for its business? Unless you write a video game
(but who may use a JIT and a GC for it?), your software will very likely
process data and strings in memory, just as with this benchmark.</p>
<p>In fact, <em>JavaScript</em> runs pretty well on
latest <em>SpiderMonkey</em>, with more than 10,000
recursive blocks rendered per second.<br />
The <em>SynMustache</em> performance (around 120,000 recursive blocks
rendered per second) sounds so high it may not be worth it...</p>
<p>Just remember: 10 times faster, on a server, means 10 times more clients
served in the same time...<br />
So 10 times more <a href="http://en.wikipedia.org/wiki/Return_on_investment">Return On Investment</a>
(ROI) for the same hardware, and probably a more integrated (cheaper) solution,
since you won't need to spread the server software on several pieces of
hardware!</p>
<p><img src="http://www.10mmt.com/web/wp-content/uploads/2010/01/123-happy-manager.jpg" alt="" /></p>
<p>If your managers begin to have doubts about JIT/GC/Java/C# speed and ROI -
which is pretty much likely after years running J2EE servers around the globe -
you can show them this sample.</p>
<p>Today, managers may be confident that <em>JavaScript</em> (and mono-threaded
<em>node.js</em>) is the answer.<br />
At least, they are told so.<br />
But, even if they are far away from the technical stuff today, they know that
such solutions consume a lot of computer resources: each tab in <em>Chrome</em>
or <em>FireFox</em> exhausts their notebook or smartphone computing
power!<br />
<em>JavaScript</em> may be good enough on client side, but may reduces the ROI
on servers.<br />
RAM on servers do cost money...</p>
<p>Imagine how your business may benefit from a <a href="https://blog.synopse.info?post/post/2013/09/10/Thread-safety-of-mORMot">multi-threaded engine</a> like
<em>mORMot</em>, when compared to a mono-threaded <em>node.js</em>
server...<br />
And, in <em>mORMot</em>, every single thread is able to process 10 times more
data than its <em>JavaScript</em> version!<br />
So 10 times more clients on the same server, 10 time less money to invest, 10
times more money to win, with the stability of native code and tuned memory
process...<br />
I'm quite sure they will perhaps start to be moon-eyed again on
<em>Delphi</em>.</p>
<p>Especially if the company has still <em>Delphi</em> skilled people in its
teams.<br />
Do not let your critical software be written by young developers <a href="http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html">who do not
know about algorithms and data structures</a>.<br />
Delphi has the strengths of C/C++, but easier to work with thanks to
a <a href="https://blog.synopse.info?post/post/2010/09/20/Dll-hell%2C-WinSXS-directory-and-Delphi-paradise">cleaner
approach</a>, and all <a href="https://blog.synopse.info?post/post/2014/04/18/Introducing-mORMot-s-architecture-and-design-principles">modern
design concept at hand</a>.</p>
<p>OK... I stopped trolling, but it is not far away from my own
experiment...</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?pid=10484#p10484">welcome on our web
site, as usual</a>.</p>Introducing mORMot's architecture and design principlesurn:md5:265b0a5927ad96f526d355c5c9aca5322014-04-18T11:40:00+02:002015-01-04T10:10:34+01:00AB4327-GANDImORMot Frameworkcode-firstDatabasedatabase-firstDelphiDependencyInjectionDocumentationDomainDrivenDTOdynamic arrayEventSourcingfactoryFireDACGoodPracticehttp.sysHttpApiHTTPSinterfaceJSONLateBindingmockModelmORMotMSSQLMySQLNextGenNexusDBNoSQLODBCOleDBOpenSourceOracleORMperformancePostgreSQLRepositoryRTTIsessionshardingSOASQLSQLite3SynDBSynopseTDataSetTDocVariantTDynArraytransactionUnicodeUniDACValueObjectVirtualTableWinHTTPWinINetZEOS <p>We have just released a set of slides introducing </p>
<ul>
<li>ORM, SOA, REST, JSON, MVC, MVVM, SOLID, Mocks/Stubs, Domain-Driven Design
concepts with Delphi, </li>
<li>and showing some sample code using our Open Source <em>mORMot</em>
framework.</li>
</ul>
<p>You can follow the <a href="https://drive.google.com/folderview?id=0B0r8u-FwvxWdeVJVZnBhSEpKYkE&usp=sharing">
public link on Google Drive</a>!</p>
<p><img src="http://images.fineartamerica.com/images-medium-large/1-golden-marmot-maureen-ida-farley.jpg" width="450" height="291" alt="" /></p>
<p>This is a great opportunity to discovers some patterns you may not be
familiar with, and find out how <em>mORMot</em> try to implement them.<br />
This set of slides may be less intimidating than our huge documentation - do
not be terrified by our <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html">
Online Documentation</a>!<br />
The <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#SIDE_TITL_40">
first set of pages</a> (presenting architecture and design principles) is worth
reading.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1707">welcome on our forum, as
usual</a>.</p>Delphi XE4 NextGen compiler: using byte instead of ansichar?urn:md5:d2d6b943ad19c40aa855bac58fa69ae52013-05-11T10:59:00+02:002013-10-24T15:03:22+02:00AB4327-GANDIPascal Programming64bitAJAXArabicARCblogCCrossPlatformDatabaseDelphidynamic arrayFireMonkeyFreePascalGarbageCollectorgenericsinterfacemORMotNextGenobjectParsingperformanceRADSourcestringsyntaxUnicode<p>When I first read <a href="http://www.embarcadero.com/resources/white-papers/application-development">the
technical white paper covering all of the language changes in XE4 for mobile
development (tied to the new ARM LLVM-based Delphi compiler)</a>, I have to
confess I was pretty much confused.</p>
<p>Two great <a href="http://synopse.info/forum/viewtopic.php?id=1256"><em>mORMot</em> users</a>
just asked for XE4/iOS support of <em>mORMot</em>.</p>
<p>Win32/Win64 support for XE4 will be done as soon as we got a copy of
it.<br />
I suspect the code already works, since it was working as expected with XE3,
and we rely on our own set of low-level functions for most internal work.</p>
<p>But iOS-targetting is more complex, due to the NextGen compiler, mainly.</p> <h3>FireMonkey</h3>
<p>It is first time we are explicitly asked for non-Windows support of
<em>mORMot</em>.<br />
This is the reason why we did not put the cross-platform item of the roadmap at
first place.</p>
<p>We did not do any support for this yet, because:</p>
<ul>
<li>No one did ask for cross-platform use of <em>mORMot</em>;</li>
<li>FireMonkey was broken several times, has some part of it very poorly
written, and do not support L2R languages - is it mature enough?</li>
<li>iOS support was broken once - and I prefer FPC to this NextGen compiler
(see below);</li>
<li>We do not use FireMonkey in any of our applications;</li>
<li><a href="http://smartmobilestudio.com/">SmartMobileStudio</a> is an
innovative, fast growing, cheap, and stable alternative (with lack of
documentation and 3rd party components, I admit);</li>
<li>We also considered <a href="http://twinforms.com/products/wxformsdelphi/index.php">WxForms</a> (which
seems not supported any more, but did work well);</li>
<li>Linux support is a goal for <em>mORMot</em>, on the server side.</li>
</ul>
<h3>Immutable non-AnsiString</h3>
<p>Immutable strings are something I do not understand well, in the context of
Delphi. </p>
<p>I still do not understand any benefit, in comparison to the <em><a href="http://en.wikipedia.org/wiki/Copy-on-write">copy-on-write</a></em> paradigm
(COW) implemented in Delphi since the beginning for reference-counted value
types.<br />
With COW, you have the advantages of immutable strings and private copies and
in-place modification, if needed, e.g. for fast parsing.<br />
COW can allow your text buffer access to be safe <em>and</em> fast at the same
time. </p>
<p>Using "<em>array of byte</em>" as a workaround from
<em>AnsiString/RawByteString</em> is possible, but will be slower and less
convenient.<br />
It will implements COW (if <code>const</code> is used as expected in method
parameters), but it will fill the content with zeros, so slow down the
process.<br />
And it won't be displayed as text in the debugger, nor allow direct conversion
to string.<br />
<br />
Honestly, changing from everything from <em>AnsiChar</em> to <em>Byte</em> is
just an awful workaround and breaking change.<br />
Just like a regression from the modern/turbo Pascal paradigm to a low-level C
data type.</p>
<p>The switch introduced by NextGen/ARM/LLVM is IMHO much bigger than the one
introduced with Delphi 2009.<br />
For instance, for third party libraries (like our Open Source <em>mORMot</em>),
you can maintain an existing code base for all versions of Delphi (e.g. Delphi
6 up to XE4), but you will have to maintain two versions of the code (or nest
it with IFDEF) if you want to support NextGen syntax.</p>
<p>I understand that conversion to NextGen compiler can be easy. </p>
<p>See for instance <a href="http://www.tmssoftware.com/site/blog.asp?post=263">how TMS reported it to be
not difficult for Aurelius</a>.<br />
But... do not forget that it may be on the depend of the performance.<br />
Using pointers is not evil, if done with knowledge of it.<br />
See <a href="http://synopse.info/forum/viewtopic.php?pid=6363#p6363">this user
feedback about FireBird ODBC access using Aurelius or our Open Source
<em>mORMot</em></a> (which allows remote access, by the way, in addition to
plain ORM).</p>
<p>IMHO this is one of the great features of compiled object pascal, in
comparison to managed code, or the "NextGen" model.<br />
My point is that pointers are not evil, especially for performance.<br />
Of course, I'm speaking about typed pointers, not blank untyped pointers.</p>
<h3>Huge code modification... for nothing?</h3>
<p>We could switch the <em>mORMot</em> code to be Next-Gen compatible, but
since we use UTF-8 at the lowest level, it will need a lot of IFDEF.<br />
Using "<em>array of byte</em>" instead of "<em>AnsiString(CP_UTF8)</em>" and
"<em>byte</em>" instead of "<em>AnsiChar</em>" is just an awful regression and
compatibility break.</p>
<p>We would have to use a lot of function wrappers, or perhaps re-create at
hand a UTF-8 compatibility layer.<br />
The whole <em>mORMot</em> core is depending on UTF-8, and IMHO this was *not* a
wrong choice, on the contrary.</p>
<h3>But why go in this direction?</h3>
<p>I'm confused with the Embarcadero NextGen compiler. <br />
Performance is not a goal. The RTL is just worse at every Delphi version.<br />
... and compilation time is just dead slow, in comparison to the "PreviousGen"
compiler. More than 20 times slower.<br />
Is it worth it?</p>
<p>Deprecation of <em>AnsiString</em> was never prepared by Embarcadero.<br />
We knew it about <em>shortstring</em> - OK.<br />
We were told that the <em>with</em> keyword is the root of all evil, and should
be avoided - OK.<br />
But deprecation of <em>AnsiString</em> in the NextGen compiler sounds like a
showstopper to me.</p>
<p>And don't tell me it is required by the LLVM compiler to have immutable
strings and UTF-16 encoding.<br />
This is a pure Embarcadero choice.</p>
<p>And don't tell me it is for performance optimization.<br />
The Delphi RTL can be dead slow and not scalable.<br />
Current string process was not the main speed bottleneck.<br />
And you have <a href="https://blog.synopse.info?post/post/2011/05/20/How-to-write-fast-multi-thread-Delphi-applications">easy
alternatives to circumvent those bottlenecks and unleash your CPU
power</a>.</p>
<p>And do not tell me that <em>TStringBuilder</em> is the answer.<br />
In-place parsing of buffers is the <a href="https://blog.synopse.info?post/post/2011/06/02/Fast-JSON-parsing">fastest mean e.g. for JSON or XML
performance</a>.<br />
<em>TStringBuilder</em> just replaces classic string concatenation of mutable
strings associated with a modern memory manager.<br />
It is a workaround to circumvent a performance problem. There is no benefit of
using it.</p>
<p>I was impressed by the <a href="https://blog.synopse.info?post/post/2013/03/07/64-bit-compatibility-of-mORMot-core-units">Win64 support of
latest versions of Delphi</a>.<br />
Very small breaking changes, when adapting <em>mORMot</em> to this
platform.<br />
IDE stable enough.<br />
Resulting executable fast enough (at least when it relies on our SynCommons
unit, and with <a href="https://blog.synopse.info?post/post/2013/03/13/x64-optimized-asm-of-FillChar%28%29-and-Move%28%29-for-Win64">
some tuned asm code</a>).<br />
Even nice features where re-introduced into the compiler, after a complain in
the newsgroups, like ability to compile x64 assembler functions/methods.<br />
But here, I do not understand the direction.</p>
<h3>Future Object Pascal</h3>
<p>I do not want Delphi to be another managed-but-compiled language.<br />
I like the object pascal language because:</p>
<ul>
<li>It has the benefits of high-level languages like C# or Java,
with readability, strong typing, classes, generics, interfaces, dedicated
(ansi/wide)char type, string and dynamic array reference counted types;</li>
<li>It has some nice features I miss in C# for instance, like sets, enumerates
or array of enumerations;</li>
<li>The unit layout, with clear distinction between <em>interface</em> and
<em>implementation</em> sections, is very powerful, in respect to C# or
Java all-in-one syntax;</li>
<li>It has the low-level power of C, if needed, especially for the library
cores;</li>
<li>It has a truly working class system (you can use <code>TMyClassClass =
class of TMyClass</code> with success);</li>
<li>It has a strong typing paradigm, which mitigates e.g. the use of
<em>pointer</em>;</li>
<li>Memory can be managed just as we need, with no Garbage Collector
glitches;</li>
<li>We have the whole RTL source code at hand, still readable (the C# RTL is
much bigger and complex);</li>
<li>It compiles very quickly, even for huge projects;</li>
<li>It generates stand-alone applications, <a href="https://blog.synopse.info?post/post/2010/09/20/Dll-hell%2C-WinSXS-directory-and-Delphi-paradise">with no dll
hell</a>;</li>
<li>It has a strong backward compatible history, with huge code available on
the Internet.</li>
</ul>
<div>If I want a C# or Java syntax, I would switch to those.<br />
Trying to make Delphi more C#-like is a mistake.<br />
Just think how the attributes syntax in modern Delphi is not pascal-oriented:
it should be defined after the type definition, as in the free pascal syntax,
not before it, as in C#/Java.</div>
<h3>Please fix NextGen roadmap!</h3>
<p>Using <em>byte</em> instead of <em>AnsiChar</em> is IMHO not a
feature.<br />
This is not "next generation".<br />
This is a regression.<br />
This is a breaking show-stopper.</p>
<p>I suspect (hope?) Embarcadero will be clever enough to
re-introduce <em>AnsiString </em>process to their compiler.<br />
Forget about zero-based strings.<br />
Allow mutable strings and pointer access to their buffer.</p>
<p>Otherwise, I honestly do not have much hope of continuing in this
direction.<br />
Not worth it.</p>
<p>The "next gen" object pascal is not C#.<br />
It may be the Oxygene compiler, even if I do not find so appealing a
cross-compiler language with no cross-platform library.<br />
It may either be FreePascal and its FCL/RTL, when targeting native
compilation.<br />
Either DelphiWebScript / SmartMobileStudio, when targeting interpreted / JIT /
JavaScript platforms.</p>
<h3>Your feedback is needed!</h3>
<p>I understand that I may be one of the last one using Delphi as such.<br />
That is, using Delphi not as a RAD tool, but as a strong platform to build huge
applications.<br />
With some unique benefits, in regard to alternatives (mainly Java or C#).<br />
Huge list of users of <em>mORMot</em>, and continuous feedback on the forum,
tends to prove that I'm not the only one!</p>
<p>Knowing that x64 assembler was re-introduced to Win64 compiler.<br />
That Client-Server licensing was re-allowed after some newsgroup protest.</p>
<p>I do not want to troll, just to let Delphi have a long life!<br />
I like Delphi, I do not want to stay with Delphi 7 for ever, I want the
platform to be maintained and evolving!<br />
But this NextGen roadmap may kill my hope, and switch to FPC and SMS.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1256">welcome on our forum</a>!</p>
<p>Or even better please <a href="https://forums.embarcadero.com/thread.jspa?threadID=87171">react on the
Embarcadero forum directly</a>!</p>
<blockquote>
<p><em>Update</em>: a lot of "reaction" did occur in the Embarcadero
forum.<br />
But no Embarcadero official did react by now.<br />
Even Team-B members agreed about the need of a petition to integrate feedback
of a lot of existing Delphi users.<br />
Stay tuned!</p>
</blockquote>
<p><strong>Update:</strong> It is even worse than we thought...<br />
One version later, it appears that the compiler itself and the RTL <em>do
support AnsiString</em>!<br />
See <a href="http://andy.jgknet.de/blog/2013/10/the-return-of-the-byte-strings/">this blog
article from Andy....</a>...</p>Delphi XE3 is preparing (weak) reference counting for class instancesurn:md5:174f32909199f783ad7c9ea01dfdbac12012-10-06T14:38:00+02:002013-05-20T10:33:53+02:00AB4327-GANDIPascal ProgrammingARCblogDelphidynamic arrayFreePascalinterfaceMaxOSXmORMotmultithreadNextGenperformanceSourcestringTCollectionweak pointers<p>In Delphi, you have several ways of handling data life time, therefore
several ways of handling memory:</p>
<ul>
<li>For simple value objects (e.g. <code>byte integer double
shortstring</code> and fixed size arrays or
<code>record</code> containing only such types), the value is copied in
fixed-size buffers;</li>
<li>For more complex value objets (e.g. <code>string </code>and dynamic
arrays or <code>record</code> containing such types), there is a reference
counter handled by each instance, with copy-on-write feature and
compiler-generated reference counting at code scope level (with hidden
<code>try..finally</code> blocks);</li>
<li>For most <code>class</code> instances (e.g. deriving from
<code>TObject</code>), you have to <code>Create</code> then <code>Free</code>
each instance, and manage its life time by hand - with explicit
<code>try..finally</code> blocks;</li>
<li>For <code>class</code> deriving from <code>TInterfacedObject</code>, you
have a <code>RefCount</code> property, with <code>_AddRef _Release</code>
methods (this is the reference-counted COM model), and you can use Delphi
<code>interface</code> to work with such instances - see <a href="https://blog.synopse.info?post/post/2012/02/29/Delphi-and-interfaces">this blog article</a>.</li>
</ul>
<div>With Delphi XE3, we were told that some automatic memory handling at
<code>class</code> level are about to be introduced at the compiler and RTL
level.<br />
Even if this feature is not finished, and disabled, there are a lot of changes
in the Delphi XE3 <em>Run Time Library</em> which sounds like a
preparation of such a new feature.</div> <p>First of all, there is a new compiler conditional, disabled in XE3, which is
named <code>AUTOREFCOUNT</code>.<br />
See for instance this <a href="http://docwiki.embarcadero.com/Libraries/XE3/en/System.TObject.InitInstance">official
XE3 documentation article</a>.</p>
<p>Then, there are some attributes: <code>[Weak] [UnSafe] [Ref]</code> and
their corresponding types in
<code>system.pas</code>: <code>WeakAttribute UnsafeAttribute
RefAttribute</code>.<br />
A <code>WEAKREF</code> conditional sounds associated to those.</p>
<p>And some new low-level functions, which try to implement some <a href="https://blog.synopse.info?post/post/2012/06/18/Circular-reference-and-zeroing-weak-pointers">weak-reference
system with Zeroing, just like we did for <em>mORMot</em></a> - and for Delphi
6 and up. ;)<br />
Main implementation difference is that we maintain a per-class hashed list,
whereas the XE3 version is global, and obviously not yet profiled for speed
(there is a giant lock for the whole application, using <code>TMonitor</code> -
not the best scaling method I know).</p>
<p>The link between compiler and RTL sounds already possible, even if not
enabled:</p>
<ul>
<li>Some <code>[Weak]</code> references appear in some places in the XE3 code
(e.g. in <code>classes.pas</code> e.g. for
<code>TComponent.FOwner</code>);</li>
<li>Some low-level functions like <code>InitializeArray</code> and
<code>FinalizeArray</code> (called not only for arrays) check if type
information equals nil for any field, and in this case, handle it as a weak
reference (via the new <code>_ClosureRemoveWeakRef</code> low-level
function).</li>
</ul>
<div>
<p>I was not able to see a difference between a weak reference and a zeroing
weak reference, like what we did for <em>mORMot</em>.<br />
Most of the time, a weak reference is enough. Zeroing weak references are
safer, but also much slower, even if you manage a huge number of instances in
such a global locked list.</p>
<p>As a quick conclusion, we may say that Embarcadero future for Delphi is to
embrace the <a href="https://blog.synopse.info?post/post/2011/12/08/Avoiding-Garbage-Collector%3A-Delphi-and-Apple-on-the-same-side">
ARC model used by Apple</a>, including zeroing weak pointers.<br />
It is IMHO a better solution than a garbage-collector based memory model for an
unmanaged environment like Delphi.</p>
<p>But one issue of the implementation as previewed from XE3 sources (in
addition to its alpha stage and poor performance) is that it is global to the
application: the <code>TObject</code> memory model will be either
ARC-based or manually handled (just like before).<br />
This is a breaking change in the Delphi way of coding, and I would rather
prefer a dedicated <code>TObject / class</code> type, able to coexist with the
huge amount of third-party source code. Even if most user-level code may work
with no issue (since <code>Free</code> will use the reference counting), some
lower level code may be broken by such a change.<br />
A dedicated <code>class</code> type may allow clear distinction between the
"classic" model expecting manual <code>Free</code> of instances, and the new
ARC model with its own style of coding.</p>
<p>I like very much the <em>FreePascal</em> approach, with its syntax
<em>modes</em>: you can have several syntax and even <code>class</code> models
in the same application. It is used for instance to mix <em>objective
pascal</em> and <em>classic pascal</em> classes in the same project. Clean and
easy.<br />
It will also make the OS X integration much easier than the current
interface-based marshalling of the Objective C APIs within Delphi. A lot of
plumbing which should be leverage at compiler level.<br />
I hope Embarcadero has such features on the road map, otherwise its future
could be problematic: not compatible with legacy code (which is its main
strength), and with no obvious benefit when compared to alternatives.</p>
<p>Feedback and comments are <a href="http://synopse.info/forum/viewtopic.php?id=867">welcome on our forum, as
usual</a>.</p>
</div>Circular reference and zeroing weak pointersurn:md5:08dfeee9a77fae9ac5bfda544c5f89302012-06-18T22:06:00+02:002013-05-20T10:34:51+02:00AB4327-GANDIPascal ProgrammingARCblogDelphidynamic arrayexceptionGarbageCollectorinterfacemORMotmultithreadNextGenperformanceSOASourceweak pointers<p>The memory allocation model of the Delphi <code>interface</code> type uses
some kind of <em>Automatic Reference Counting</em> (ARC). In order to avoid
memory and resource leaks and potential random errors in the applications (aka
the terrible <code>EAccessViolation</code> exception on customer side) when
using <code>interface</code>, a SOA framework like <em>mORMot</em> has to offer
so-called <em>Weak pointers</em> and <em>Zeroing Weak pointers</em>
features.</p>
<p>Note that garbage collector based languages (like Java or C#) do not suffer
from this problem, since the circular references are handled by their memory
model: objects lifetime are maintained globally by the memory manager. Of
course, it will increase memory use, slowdown the process due to additional
actions during allocation and assignments (all objects and their references
have to be maintained in internal lists), and may slow down the application
when garbage collector enters in action. In order to avoid such issues when
performance matters, experts tend to pre-allocate and re-use objects: this is
one common limitation of this memory model, and why Delphi is still a good
candidate (like unmanaged C or C++ - and also <em>Objective C</em>) when
it deals with performance and stability.</p> <h3>Functions for handling weak pointers</h3>
<p>As <a href="https://blog.synopse.info?post/post/2011/12/08/Avoiding-Garbage-Collector%3A-Delphi-and-Apple-on-the-same-side">
we already stated in this blog</a>, such a <em>Zeroing</em> ARC model has been
implemented in <em>Objective C</em> by Apple, starting with Mac OS X 10.7 Lion,
in replacement (and/or addition) to the previous manual memory handling
implementation pattern: in its Apple's flavor, ARC is available not only for
interfaces, but for objects, and is certainly more sophisticated than the basic
implementation available in the Delphi compiler: it is told (at least from the
marketing paper point of view) to use some deep knowledge of the software
architecture to provide an accurate access to all instances - whereas the
Delphi compiler just relies on a <em>out-of-scope</em> pattern. In regard to
classic <em>garbage collector</em> memory model, ARC is told to be much more
efficient, due to its deterministic nature: Apple's experts ensure that it does
make a difference, in term of memory use and program latency - which both are
very sensitive on "modest" mobile devices. In short, thanks to ARC, your phone
UI won't glitch during background garbage recycling. So <code>mORMot</code>
will try to offer a similar feature, even if the Delphi compiler does not
implement it (yet).</p>
<p>The main issue with reference counting is the potential <em>circular
reference</em> problem. This occurs when an <code>interface</code> has a strong
pointer to another, but the target <code>interface</code> has a strong pointer
back to the original. Even when all other references are removed, they still
will hold on to one another and will not be released. This can also happen
indirectly, by a chain of objects that might have the last one in the chain
referring back to an earlier object.</p>
<p>See the following <code>interface</code> definition for instance:</p>
<pre>
IParent = <strong>interface</strong>
<strong>procedure</strong> SetChild(<strong>const</strong> Value: IChild);
<strong>function</strong> GetChild: IChild;
<strong>function</strong> HasChild: boolean;
<strong>property</strong> Child: IChild <strong>read</strong> GetChild <strong>write</strong> SetChild;
<strong>end</strong>;
<br /> IChild = <strong>interface</strong>
<strong>procedure</strong> SetParent(<strong>const</strong> Value: IParent);
<strong>function</strong> GetParent: IParent;
<strong>property</strong> Parent: IParent <strong>read</strong> GetParent <strong>write</strong> SetParent;
<strong>end</strong>;
</pre>
<p>The following implementation will definitively leak memory:</p>
<pre>
<strong>procedure</strong> TParent.SetChild(<strong>const</strong> Value: IChild);
<strong>begin</strong>
FChild := Value;
<strong>end</strong>;
<strong>procedure</strong> TChild.SetParent(<strong>const</strong> Value: IParent);
<strong>begin</strong>
FParent := Value;
<strong>end</strong>;
</pre>
<p>In Delphi, most common kind of reference-copy variables (i.e.
<code>variant</code>, <em>dynamic array</em> or <code>string</code>) solve this
issue by implementing <em>copy-on-write</em>. Unfortunately, this pattern is
not applicable to <code>interface</code>, which are not value objects, but
reference objects, tied to an implementation <code>class</code>, which can't be
copied.</p>
<p>One common solution is to use <em>Weak pointers</em>, by which the
<code>interface</code> is assigned to a property without incrementing the
reference count. In order to easily create a weak pointer, the following
function was added to <code>SQLite3Commons.pas</code>:</p>
<pre>
<strong>procedure</strong> SetWeak(aInterfaceField: PIInterface; <strong>const</strong> aValue: IInterface);
<strong>begin</strong>
PPointer(aInterfaceField)^ := Pointer(aValue);
<strong>end</strong>;
</pre>
<p>Therefore, it could be used as such:</p>
<pre>
<strong>procedure</strong> TParent.SetChild(<strong>const</strong> Value: IChild);
<strong>begin</strong>
SetWeak(@FChild,Value);
<strong>end</strong>;
<strong>procedure</strong> TChild.SetParent(<strong>const</strong> Value: IParent);
<strong>begin</strong>
SetWeak(@FParent,Value);
<strong>end</strong>;
</pre>
<p>But there are still some cases where it is not enough. Under normal
circumstances, a <code>class</code> instance should not be deallocated if there
are still outstanding references to it. But since weak references don't
contribute to an <code>interface</code> reference count, a <code>class</code>
instance can be released when there are outstanding weak references to it. Some
memory leak or even random access violations could occur. A debugging
nightmare...</p>
<p>In order to solve this issue, ARC's <em>Zeroing Weak pointers</em> come to
mind.<br />
It means that weak references will be set to <code>nil</code> when the object
they reference is released. When this happens, the automatic zeroing of the
outstanding weak references prevents them from becoming dangling pointers. And
<em>voilà</em>! No access violation any more!</p>
<p>In order to easily create a so-called zeroing weak pointer, the following
function was defined in <code>SQLite3Commons.pas</code>:</p>
<pre>
<strong>procedure</strong> SetWeakZero(aObject: TObject; aObjectInterfaceField: PIInterface;
<strong>const</strong> aValue: IInterface);
</pre>
<p>A potential use case could be:</p>
<pre>
<strong>procedure</strong> TParent.SetChild(<strong>const</strong> Value: IChild);
<strong>begin</strong>
SetWeakZero(self,@FChild,Value);
<strong>end</strong>;
<strong>procedure</strong> TChild.SetParent(<strong>const</strong> Value: IParent);
<strong>begin</strong>
SetWeakZero(self,@FParent,Value);
<strong>end</strong>;
</pre>
<p>We also defined a <code>class helper</code> around the <code>TObject</code>
class, to avoid the need of supplying the <code>self</code> parameter, but
unfortunately, the <code>class helper</code> implementation is so buggy it
won't be even able to compile before Delphi XE version of the compiler. But it
will allow to write code as such:</p>
<pre>
<strong>procedure</strong> TParent.SetChild(<strong>const</strong> Value: IChild);
<strong>begin</strong>
SetWeak0(@FChild,Value);
<strong>end</strong>;
</pre>
<p>For instance, the following code is supplied in the regression tests, and
will ensure that weak pointers are effectively zeroed when
<code>SetWeakZero()</code> is used:</p>
<pre>
<strong>function</strong> TParent.HasChild: boolean;
<strong>begin</strong>
result := FChild<><strong>nil</strong>;
<strong>end</strong>;
<br /> Child := <strong>nil</strong>; <em>// here Child is destroyed</em>
Check(Parent.HasChild=(aWeakRef=weakref),'ZEROed Weak');
</pre>
<p>Here, <code>aWeakRef=weakref</code> is <code>true</code> when
<code>SetWeak()</code> has been called, and equals <code>false</code> when
<code>SetWeakZero()</code> has been used to assign the <code>Child</code>
element to its <code>Parent</code> interface.</p>
<h3>Weak pointers implementation details</h3>
<p>The <code>SetWeak()</code> function itself is very simple (see
above). <br />
The Delphi RTL/VCL itself use similar code when necessary.</p>
<p>But the <code>SetWeakZero()</code> function has a much more complex
implementation, due to the fact that a list of all weak references has to be
maintained per <code>class</code> instance, and set to <code>nil</code> when
this referring instance is released.</p>
<p>The <em>mORMot</em> implementation tries to implement:<br />
- Best performance possible when processing the <em>Zeroing</em> feature;<br />
- No performance penalty for other classes not involved within weak
references;<br />
- Low memory use, and good scalability when references begin to define huge
graphs;<br />
- Thread safety - which is mandatory at least on the server side of our
framework;<br />
- Compatible with Delphi 6 and later (avoid syntax tricks like
<code>generic</code>).</p>
<p>Some good existing implementations can be found on the Internet:<br />
- <em>Andreas Hausladen</em> provided a classical and complete implementation
at <a href="http://andy.jgknet.de/blog/2009/06/weak-interface-references">http://andy.jgknet.de/blog/2009/06/weak-interface-references</a>
using some nice tricks (like per-instance optional speed up using a void
<code>IWeakInterface interface</code> whose VMT slot will refer to the
references list), is thread-safe and is compatible with most Delphi versions -
but it will slow down all <code>TObject.FreeInstance</code> calls (i.e. within
<code>Free / Destroy</code>) and won't allow any overriden
<code>FreeInstance</code> method implementation;<br />
- <em>Vincent Parrett</em> proposed at <a href="http://www.finalbuilder.com/Resources/Blogs/PostId/410/WeakRefence-in-Delphi-solving-circular-interfac.aspx">
http://www.finalbuilder.com/Resources/Blogs/PostId/410/WeakRefence-in-Delphi-solving-circular-interfac.aspx</a>
a <code>generic</code>-based solution (not thread-safe nor optimized for
speed), but requiring to inherit from a base class for any <code>class</code>
that can have a weak reference pointing to it;<br />
- More recently, <em>Stefan Glienke</em> published at <a href="http://delphisorcery.blogspot.fr/2012/06/weak-interface-references.html">http://delphisorcery.blogspot.fr/2012/06/weak-interface-references.html</a>
another <code>generic</code>-based solution, not requiring to inherit from a
base class, but not thread-safe and suffering from the same limitations related
to <code>TObject.FreeInstance</code>.</p>
<p>The implementation included within <em>mORMot</em> uses several genuine
patterns, when compared to existing solutions:<br />
- It will hack the <code>TObject.FreeInstance</code> at the <code>class</code>
VMT level, so will only slow down the exact <code>class</code> which is used as
a weak reference, and not others (also its inherited <code>classes</code> won't
be overriden) - and it will allow custom override of the <code>virtual
FreeInstance</code> method;<br />
- It makes use of our <a href="https://blog.synopse.info?post/post/2011/03/12/TDynArray-and-Record-compare/load/save-using-fast-RTTI"><code>
TDynArrayHashed</code> wrapper</a> to provide a very fast lookup of instances
and references, without using <code>generic</code> definitions;<br />
- The unused <code>vmtAutoTable</code> VMT slot is used to handle the
class-specific orientation of this feature (similar to <a href="https://blog.synopse.info?post/post/2011/06/07/True-per-class-variable"><code>TSQLRecordProperties</code>
lookup as implemented for DI-2.1.3</a>), for best speed and memory use.</p>
<p>See the <code>TSetWeakZeroClass</code> and <code>TSetWeakZeroInstance</code>
implementation in <code><a href="http://synopse.info/fossil/finfo?name=SQLite3/SQLite3Commons.pas">SQlite3Commons.pas</a></code>
for the details.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=749">welcome
in our forum, just as usual</a>.</p>Avoiding Garbage Collector: Delphi and Apple side by sideurn:md5:04dc2bd1459aa8c2bf3af1899cfce4cb2011-12-08T10:09:00+01:002013-05-20T10:36:21+02:00AB4327-GANDIPascal ProgrammingARCblogDelphidynamic arrayGarbageCollectorGoodPracticeinterfaceNextGenobjectperformancerecordSourceSynScaleMMTDynArrayVMTweak pointers<p>Among all <em>trolling</em> subject in forums, you'll find out the
great <em>Garbage Collection</em> theme.</p>
<p>Fashion languages rely on it. At the core of the .Net and Java framework,
and all scripting languages (like JavaScript, Perl, Python or Ruby), you'll
find a Garbage Collector. New developers, just released from schools, do
learn about handling memory only in theory, and just can't understand how is
memory allocated - we all have seen such rookies involved in Delphi code
maintenance, leaking memory as much as they type. In fact, most of them did not
understood how a computer works. I warned you this will be a trolling
subject.</p>
<p>And, in <em>Delphi</em>, there is no such collector. We handle memory in
several ways:</p>
<ul>
<li>Creating static variables - e.g. on the stack, inside a <code>class</code>
or globally;</li>
<li>Creating objects with <code>class</code> instances allocated on heap - in
at least three ways: with a <code>try..finally Free</code> block, with a
<code>TComponent</code> ownership model in the VCL, or by using
an <code>interface</code> (which creates an hidden <code>try..finally
Free</code> block);</li>
<li>Creating reference-counted variables, i.e. <code>string</code>, <code>array
of</code>, <code>interface</code> or <code>variant</code> kind of
variables.</li>
</ul>
<p>It is a bit complex, but it is also deadly powerful. You have several memory
allocation models at hand, which can be very handy if you want to tune your
performance and let program scale. Just like manual recycling at home will save
the planet. Some programmers will tell you that it's a waste of cell brain,
typing and time. Linux kernel gurus would not say so, I'm afraid.</p>
<p>Then came the big <em>Apple</em> company, which presented its new ARC model
(introduced in Mac OS X 10.7 Lion) as a huge benefit for
<em>Objective-C</em> in comparison with the Garbage Collection model. And
let's face it: this ARC just sounds like the <em>Delphi</em> memory model.</p> <h3>What is this ARC model?</h3>
<p>No, ARC is not only an old packer format, nor the company responsible of
<a href="http://www.arcdisposal.com/">solid waste removal and recycling
services for Chicago and suburbs</a> - this acronym stands for <em>Automatic
Reference Counting</em>.</p>
<p>Formerly, class instances used to be manually handled in <em>Objective
C</em> code. You had to call explicitly the <code>Release</code> method
to decrement its reference count. When an object's reference count is
zero, it is deallocated.</p>
<p>This sounds just like our well known reference-counting mechanism included
in <em>Delphi</em>, for <code>string</code>, <code>array of</code>,
<code>interface</code> or <code>variant</code>. What is new with ARC is that,
as with these <em>Delphi</em> types, the compiler will generate the
<em>release</em> code. And all those kind of variable instanced are filled with
zero (this is not the same as the famous <a href="http://lists.apple.com/archives/objc-language/2011/Jun/msg00013.html">Zeroing
Weak pointers</a> feature) - which is implemented by
<code>_InitializeRecord()</code> and <code>_FinalizeRecord()</code> in
low-level <code>System.pas</code> unit. Of course, the ARC implementation is
certainly more sophisticated than the basic implementation in the
<em>Delphi</em> compiler: it is told (at least from the marketing paper point
of view) to use some deep knowledge of the software architecture to provide an
accurate access to all instances. Whereas the Delphi compiler just relies on a
<em>out-of-scope</em> pattern.</p>
<p>There is a very interesting analysis of the ARC design available on <a href="http://arstechnica.com/apple/reviews/2011/07/mac-os-x-10-7.ars/11">Ars
Technica</a>, which is worth reading. Especially on modest hardware (I do not
know if it is honest when this concept is applied to modern smartphones,
which are much more powerful than the PCs on which the first Delphi version was
running on), Apple's experts say it does make a difference, in term of memory
use and program latency - the UI won't glitch during background garbage
recycling.</p>
<h3>So what about the issues?</h3>
<p>This just sounds too much perfect. And there is no perfection on earth, nor
in computing.</p>
<p>One common problem with reference counting is the
potential <em>circular reference</em> issue.</p>
<p>This occurs when one object has a strong pointer to another, but the target
object has a strong pointer back to the original. Even when all other
references to these objects are removed, they still will hold on to one another
and will not be released. This can also happen indirectly, by a chain of
objects that might have the last one in the chain referring back to an earlier
object.</p>
<p>This is where the ARC's <em>Zeroing Weak pointers</em> comes to mind. When
you read the <a href="http://clang.llvm.org/docs/AutomaticReferenceCounting.html">official reference
material at llvm.org</a>, there are also some other potential issues, but the
<code>__weak</code> ownership qualifier is the main point of this document.</p>
<p>The implementation pattern is a bit more complex that the one used for
<em>Delphi</em>'s <code>interface</code> (taken from <em>Ars
Technica</em>):</p>
<blockquote>
<p>The "zeroing" part means that weak references will be set to nil when the
object they reference is deallocated. (Under ARC, all object pointers are
initially set to zero.) Under normal circumstances, an object shouldn't be
deallocated if there are still outstanding references to it. But since weak
references don't contribute to an object's reference count, an object can be
deallocated when there are outstanding weak references to it. When this
happens, the automatic zeroing of the outstanding weak references prevents them
from becoming dangling pointers. (In Objective-C, sending a message to nil is a
no-op.)</p>
</blockquote>
<p>As far as I understood the problem, simple types like our reference-counted
<em>Delphi</em> variables solve the circular reference issue by providing a
copy-on-write behavior. But the <code>interface</code> kind of variable, in its
current state, may suffer from this issue.</p>
<p>Since I want to use <code>interface</code> everywhere in our <a href="http://synopse.info/fossil/wiki?name=RoadMap">next version of mORMot</a> (to
implement a true <a href="http://en.wikipedia.org/wiki/Domain-driven_design">Domain-Driven</a> architecture
- including <a href="https://blog.synopse.info?post/post/2011/11/27/SOLID-design-principles">SOLID</a>
principles), some strict rules may be defined in its usage, not to suffer from
similar issues. To my understanding, as soon as you use interfaces in the
internal scope of methods (i.e. stack-allocated), you may never suffer for
circular reference.</p>
<p>Additional investigation is certainly needed.</p>
<p>Your feedback is warmly <a href="http://synopse.info/forum/viewtopic.php?id=543">welcome on our forum</a>!</p>
<p><strong>Article update:</strong> <em>Weak
pointers</em> and <em>Zeroing Weak pointers</em> have been
introduced into <em>mORMot</em>.<br />
See <a href="http://blog.synopse.info/post/2012/06/18/Circular-reference-and-zeroing-weak-pointers">this
blog article</a>.</p>