The one thing that annoys me most when producing the numerous open source libraries and tools I’m developing is the constant replication of code, in an attempt to keep each project separate from the others.
The number of times I’ve copied and pasted the same Classes and Methods surely by now constitutes tens of thousands (or more) of lines of unnecessary code, and a note-worthy (in the very least) amount of time.
Not anymore!
Introducing the LaKraven Studios Standard Library [LKSL] for Delphi
The LKSL is a behind-the-scenes library I have begun producing to bring all of the standard base Classes and Methods I use in my open source and client projects together into one place. Not only that, but it also introduces some very powerful new functionality we can all enjoy.
As a side note, the LKSL isn’t an additional project on my already long list of projects, but rather a common base to be used by them all. As such, it is being developed directly alongside all of the projects I have currently in development, as the need is encountered along the way. This means it’s not consuming more of my time, but will in fact save it.
There is one new feature introduced by the LKSL I consider so extraordinarily useful (nay, necessary) I’d like to focus on it now…
UPDATE: Due to performance issues, I will not be using Smart Types in my Public projects. At the same time, I will not be removing the code as the performance issue could potentially be solved in the future. Thank you all for your feedback (as overwhelmingly negative as it has been). I shall leave the rest of this article unaltered so people can see what the idea was, and continue to berate me for the Unicode > ANSI casting idea (which I now agree was a very silly idea)
Smart Types
“Smart Types” are Value Types just like String, Integer, Double, Single, Boolean and the like, but with a fundamental difference. The methods used to operate on normal types are encapsulated directly within Smart Types, as you can see in other languages like .NET, and in many scripting languages (such as Lua). This keeps related operations bound together under their corresponding Value Type, which not only reduces the amount of code you have to produce, but makes it easier to do so as well.
[SmartString] ANSI<>Unicode Casting
As of Delphi 2009, the standard String type in Delphi is Unicode, where in all prior versions of Delphi (2007 and earlier) it was ANSI. For many of us whom have already made the upgrade to Unicode versions of Delphi, this presented quite a lot of annoyance in larger projects, as much of the dependencies we required hadn’t been updated to support Unicode, meaning that we would get a spam of warnings about implicit casting between Unicode and ANSI strings, as well as issues at run-time with data corruption and the like.
What was previously needed to address these issues was to go through all of our code and cast between Unicode and ANSI such as:
function AnsiToUnicodeString(const AAnsiString: AnsiString): String; begin Result := String(AAnsiString); // An explicit cast to supress compiler warnings end; function UnicodeToAnsiString(const AUnicodeString: String): AnsiString; begin Result := AnsiString(AUnicodeString); // An explicit cast to supress compiler warnings, except unicode-specific chars will be omitted (truncation) end; function StringToPAnsiChar(const AValue: String): PAnsiChar; begin Result := PAnsiChar(AnsiString(AValue)); // This enables us to pass a String value as a parameter of an ANSI-C DLL method! end; function PAnsiCharToString(const AValue: PAnsiChar): String; begin Result := String(AnsiString(AValue)); // This enables us to work with values returned by ANSI-C DLL methods! end;
Now, you could either place those functions inside a unit, and call them “in-line” for every string cast, or you’d have to call the casting they contained… either way, you have to do this every single time you’re casting between Unicode and ANSI.
The LKSL’s SmartString type, however, does this internally for you, meaning all you have to do is use SmartString in place of String or AnsiString in your code, and you need never bother with the explicit (manual) casting again!
function SomeAnsiCDLLMethod(AStringValue: PAnsiChar): PAnsiChar; cdecl; external MY_DLL; // Takes an Ansi String, returns an Ansi String! var LSmartString: SmartString; LUniString: String; LAnsiString: AnsiString; begin // SmartString accepts assignments just like traditional String types! LSmartString := 'Hello World!'; // No compiler warnings, no truncation! A clean implicit cast! LUniString := LSmartString; // No compiler warnings, but Unicode-specific chars will be omitted (truncation) LAnsiString := LSmartString; // We can even call our ANSI-C DLL method too! LSmartString := SomeAnsiCDLLMethod(LSmartString); end;
As you can see above, SmartString handles all the casting for you, so there is no need to explicitly define the required String Type each time you pass the value around! This means that when you use SmartString instead of String and its kin, you don’t need to think too much about how it’s being cast.
Of course, you do need to consider that Unicode chars will be lost when the value is passed to an ANSI container, but this is always the case whether you’re casting manually or not.
NOTE: SmartString types are not an excuse to forget about the differences between Unicode and ANSI… but it provides a useful shortcut to those with large existing projects working entirely with ANSI data already.
If you wish to force your strings to be ANSI-only (disregarding all Unicode chars as a design), you can simply use SmartAnsiString instead! It works the same way as SmartString only it stores its values in ANSI. It’ll automatically cast its value to String (Unicode) containers, but it’ll only ever hold ANSI chars! This is particularly useful if you are working with third-party DLLs only available in ANSI!
[SmartString] IntToStr, FloatToStr etc… bye bye!
When you want to directly cast an Integer (or any other ordinal-type) value to a string type, currently you have to so manually (explicitly)…
var LInteger: Integer; LString: String; begin LInteger := 1337; // Explicitly cast the Integer to a String LString := IntToStr(LInteger); // Explicitly cast a String to an Integer LInteger := StrToInt(LString); end;
This is fine, and has served us all well for many years… but really it’s just more code than is necessary!
Here’s how the LKSL’s SmartString types do it:
var LInteger: Integer; LString: SmartString; begin LInteger := 1337; // Passing an Integer value directly to a String LString := LInteger; // Also works with all Floating Point types: LString := 3.14159; // You can even do it with Booleans! LString := True; // And now from SmartString to Integer... LString := 9001; LInteger := LString; end;
As you can see, it is no longer essential to explicitly declare casts when assigning compatible types to a SmartString (and vice versa), as SmartString takes care of that for us! A full list of compatible types will be provided in the upcoming LKSL documentation, or you can very easily look at the source code found in LKSL.SmartTypes.pas to see what works and how!
Should you wish to format an ordinal value for presentation purposes, this is of course still possible… only now, it’s more intuitive thanks to SmartString…
[SmartString] Composing and manipulating Values
As mentioned previously, Smart Types encapsulate common methods used to compose or manipulate their value. In fact, the Smart Types provided by the LKSL enable you to do this in two ways:
- Perform an operation to compose or manipulate a value, and return that manipulated value as a fresh “instance” of the Smart Type, leaving the original value in-tact
- Perform an operation to compose or manipulate a value, returning the manipulated value as a fresh “instance” of the Smart Type, and/or modifying the original.
This provides the greatest degree of flexibility, enabling you to produce code both to a style more suitable to yourself, and in the most logical (therefore easiest to follow) manner.
Leaving the original value in-tact…
var LFoo, LBar: SmartString; begin LFoo := ' Hello '; // Remove the preceding and proceeding whitespace LBar := LFoo.Trim; end;
The above code snip demonstrates two things. First of all, Smart Types contain methods we can execute (such as Trim) secondly, that this method returns a fresh (modified) SmartString based on the contents of the original, while leaving the original in-tact. This means that the value of LFoo will remain ‘ Hello ‘, while the value of LBar will be ‘Hello’.
Modify the original value…
var LFoo: SmartString; begin LFoo := ' Hello '; LFoo.TrimSelf; end;
The above code snip shows an example of Trim used to modify the original value, as opposed to just returning a new SmartString (though note that methods which modify the original value do also return the new value as well, for in-line usage [keep reading for more info]). All methods in the LKSL’s Smart Types which modify the original value have the Self suffix so are defined as [Method]Self.
The reason for the Self suffix is that Delphi does not allow the overloading of functions with procedures (or vice-versa), and so the method name must be different. We settled on Self because it is a universally-understood precept of Object-Oriented Programming. We have elected to make it a suffix rather than a prefix for the sake of Delphi’s Code Completion display order (which is alphanumeric).
Since Self-suffixed methods also return a copy of the value as a new Smart Type “instance”, we can use them “in-line”…
var LFoo, LBar: SmartString; begin LFoo := ' Hello '; // "in-line" value composition LBar := LFoo.TrimSelf.UpperCase; end;
The above code snip above results in LFoo‘s value being changed to ‘Hello’, and LBar‘s value being set to ‘HELLO’ (the UpperCase value of LFoo)… all with a single line of code!
It’s worth noting that while the concept of “in-line” operation such as demonstrated here is great for code production, if used irresponsibly it can make the code more difficult to read, since one line of code will be performing multiple operations. With that said, it is up to you – as a developer – to determine when it’s appropriate to make use of this feature.
[SmartString] Sub-String Subtraction (the Subtract operator)
The LKSL’s SmartString types include what I consider to be a very useful feature, enabling you to subtract a Sub-String from a value. It’s easier to understand with an example:
var LFoo, LBar: SmartString; begin LFoo := 'Hello'; // Subtract "l" from LFoo using the "subtract" operator LBar := LFoo - 'l'; end;
The above code would make the value of LBar equal to the value of LFoo, but without any instances of the Sub-String “l” (case-sensitive), which evaluates as “Heo”. This is fundamentally the same as:
var
LFoo, LBar: SmartString;
begin
LFoo := 'Hello';
// Subtract "l" from LFoo using the "subtract" method
LBar := LFoo.Subtract('l');
end;
It is also worth noting that you can perform subtraction in a case-insensitive manner:
var
LFoo, LBar: SmartString;
begin
LFoo := 'Hello';
// Subtract "l" from LFoo using the "subtract" method (case-insensitive)
LBar := LFoo.AnsiSubtract('l');
// The above is the same as...
LBar := LFoo.Subtract('l', False);
end;
The last line of the above code snip demonstrates that the Subtract method has an optional Boolean parameter ACaseSensitive (defaults to True), where AnsiSubtract just places that call internally, and exists to adhere to the convention set by Delphi in its own String methods such as AnsiCompare and AnsiPos (both of which are encapsulated by SmartString, by the way).
Compatibility
Smart Types (along with, at present, all of the LKSL) are compatible with Win32, Win64 and MacOSX. Due to the language features used in the LKSL, it’ll only work with Delphi 2007 and above.
iOS is not supported for XE2, as we have yet to even test it on this platform and we do not develop for iOS anyway. If you’re desperate to use the LKSL (any part of it, not just Smart Types) you’ll either have to submit changes for approval and integration, or you can contract me to add iOS support (since I have no personal interest in the iOS platform, I refuse to pursue this for free).
Where’s the “T”?
You’ve no doubt noticed that the LKSL’s Smart Types are not prefixed with the letter “T” (as is the custom when defining your own Types). This is because the Smart Types behave exactly like basic Value Types (none of which are prefixed with “T”). It is not only more intuitive to prefix the name of types you already know and use with “Smart” to take advantage of these new enhancements, it is easier to read.
I’ve heard all the arguments for and against, but ultimately my mind is made up and they will not be changed.
Got any ideas/requests/recommendations for Smart Type operations?
If you have an idea, request or recommendation for any sort of common operation you’d like to see included in a Smart Type (not just SmartString, but any of the Smart Types), you’re invited to leave a comment with comprehensive details about the idea, how it’s done (where appropriate/possible), and (most importantly) why it should be added.
Please do not post requests etc for any case-specific operations! An example of an inappropriate suggestion would be to include encryption/decryption operations directly into SmartString. This is not a common String operation, and therefore will not be added to the type itself!
There’s no need to suggest anything contained within .NET as we’re already introducing all of the suitable operations found in .NET 4′s Value Types already.
When can I get my hands on the LKSL?
The first roll-out of the LKSL will coincide with the release of Lua4Delphi, as it is the first of my public projects to make extensive use of the LKSL. Of course, the LKSL will continue to grow both along-side other public/private projects, as well as on its own.
Is the LKSL FREE (Open Source) or Commercial?
It’s FREE and Open Source, subject to a fairly liberal license. The only restrictions are that you may not sell the LKSL (its source or the compiled DCUs) for a fee (and that includes a fee for distribution), and you may not make changes to the LKSL’s units yourself. You can, of course, derive from them in your own units, but you must not under any circumstances modify the original sources themselves.
You are (as always) welcome to submit requests and suggested changes to myself (and LaKraven Studios Ltd) for approval and integration, but it is absolutely crucial that you do not modify the sources yourself. This is because any changes you make could very easily break compatibility with other projects which make use of the LKSL. I have stipulated this restriction as a legal requirement of the license primarily for liability purposes, but also because I do not want to be receiving bug reports when said bug is caused by your own modifications to my code.
The final restriction is that derived sources must contain a provided copyright block at the very top of each derived unit, for the sake of attribution.
Those are the only restrictions… beyond that, you may enjoy it as you wish!
Is this the start of a commercial “LaKraven Studios SDK”?
Yes, it is! More info on that when the time is right, but to say that the LKSL itself will continue to be free and Open Source under the aforementioned license.
December 10, 2011 at 11:41 pm
I actually don’t see any benefit of using record based types with lots of operator overloads and methods as a replacement for the built-in types in order to get rid of calling conversion routines when assigning one type to another.
Also I actually don’t like open source that forbids me to modify it for my own purpose.
December 11, 2011 at 12:38 am
If you want to have your own modified version of something in the LKSL you can (in the case of class-based stuff) inherit from it and add/tweak things as you see fit, or (in the case of non-class stuff) you can create a copy of the unit you may modify as you wish (leaving the original in-tact).
The reason for the “no modification of the original source” restriction is literally because any modification you do make could break other libraries which use it. I’m not stopping anyone from copying the code into a new unit and changing it however they wish…. they just have to leave the original units alone. A fair compromise in my opinion.
As for the Smart Types, the automatic casting is more a “happy bi-product” of their true function (which is to encapsulate all of the common operations you would perform on that type within the type itself). You’re not FORCED into using them that way… I merely point out that you can (and there’s no real reason not to).
December 10, 2011 at 11:44 pm
I’m not sure that it is good idea to let directly assign integer or float values to the string type. What’s the purpose of this? In what circumstances it can be useful? IMO it can only confuse you while reading the code. Also I’m pretty sure that your string type could be slow in situations when lots of concatenations are needed. OTOH it is great to see that Delphi’s open source community are expanding. Hope it will grow in the future as well.
December 11, 2011 at 12:45 am
We will be exhaustively benching the Smart Types in both Performance and Overhead, using both “synthetic benchmarks” (standard benchmark routines used to measure raw performance), along with real-world performance testing. Thus far (if anything) the memory overhead is near-0 (since the Smart Types are freed once they go out of scope, just like any other Value Type variable), and the performance certainly appears 100% equal to that of the normal types. I’ll post results once the benchmarks are written and tested.
You ask what the purpose of casting Integer, Float (and other ordinal type) values to String is… well, take a look at any project or library… somewhere in there, at some point, someone will be calling IntToStr or FloatToStr etc. It’s one of those things people do all the time. SmartString just allows you to do it without having the extra code. I see no reason why this would be confusing to anyone… certainly doesn’t confuse me or the other developers I have playing with this code (and helping to develop it further).
Again, nobody is forcing you to use SmartString in this way! You can still call IntToStr etc. if you like (with no overhead in doing so). The idea here is to provide options enabling people to develop code in the best way to suit them, while at the same time expanding on what’s possible.
December 11, 2011 at 7:12 am
The few silent string autocasts in unicode Delphi (constants f.I.) where one of the sources of headaches when converting to unicode, silent lossy conversions ate a big no-no IME.
Also, two other issues:
- with so many implicit casts, why not use Variant?
- the compiler handling of records with managed types (like string) is very inefficient
December 11, 2011 at 8:01 am
1) Variants have their own overhead issues, and the implicit casts are inlined (so essentially the ASM markup is identical to explicitly casting in your own code). Also, Variants accept Pointers, Object and Class References – all of which SmartString does not want to handle (for obvious reason)
2) Without inlining, basic assignment of a value to SmartString with 100,000 test cycles weighs in at either 0ms or 16ms total. However, the exact same thing is true when assigning a value to a standard String. Taking that at face value, the overhead for at least value assignment (in performance terms) appears to be immeasurably small (to the point where even a sample size of 100,000 passes draws no clear conclusion). With that in mind, where exactly is the performance hit? I sure as hell am not seeing one!
The Smart Types are being tested and optimized to the very limit of what’s possible… so I’m extremely confident people will be able to use them with absolutely no performance hit.
As for silent String conversions… well this issue exists whether you’re using SmartString or String. As I said in the article, just because it handles the casts for you does not mean you can ignore the differences between ANSI and Unicode. Ultimately it’s the developer’s responsibility to ensure that their routines are not truncating String values. What I MIGHT do is have it present a Compiler Warning (in Debug build config only) when casting a Unicode String to ANSI… though these really do tend to flood the compiler message box when porting an existing (ANSI-Delphi) project over to Unicode-Delphi.
December 11, 2011 at 1:10 pm
Assignments don’t suffer from performance penalty but add operator does. You can try to spin 100000 iterations and in the loop write this code: FSmartString := FSmartString + ‘a’; In my tests it is very slow (takes more than 1 second where native string executes only in a few milliseconds). I guess the performance penalty comes from record copy.
December 11, 2011 at 4:18 pm
I ran your test and got a similar result… which I have to admit does not appear too promising
December 11, 2011 at 5:36 pm
You can try our – a bit – faster implementation of record copy. See http://blog.synopse.info/post/2010/03/23/CopyRecord-faster-proposal
December 11, 2011 at 5:57 pm
Cheers for the link. For the time being I am going to leave Smart Types (I won’t be using them due to performance hits on standard operators)
I’m thinking a String Foundry class might be a better approach in terms of performance… but at this point I cannot afford to invest any more of my time in this, now that I’ve essentially wasted 2 days, and not advanced Lua4Delphi as a result
Oh well! One relatively minor mis-step, but I learned a few things along the way so it’s not all bad (silver linings and all that).
December 11, 2011 at 1:53 pm
“Variants accept Pointers, Object and Class References”
No they cannot. You seem to be conflating Variant with TValue (TValue accepts practically anything, but doesn’t do the auto-conversions you’ve implemented).
More generally, the D2009+ compiler warnings are a *feature* – adding a cast when assigning between different string types doesn’t actually do anything substantive, it justs removes the compiler warning. It was wrong of previous Delphis to perform the implicit conversions they did without any indication they were doing them (while there was no UnicodeString, there was WideString where exactly the same issues arose). Adding a cast is just the way to tell the compiler that yes, you realise a conversion is taking place, or yes, you realise a potentially lossy conversion is taking place. If you really hate them, you can disable the compiler warnings involved.
“these really do tend to flood the compiler message box when porting an existing (ANSI-Delphi) project over to Unicode-Delphi”
Again, that’s a *good* thing… The approach taken in D2009 was a sensible one IMO: old code could still run, but the compiler issued warnings for many of the places you should be looking at to ensure the code actually ran correctly.
December 11, 2011 at 4:26 pm
Okay… the Variant type confused me because of vtPointer, vtObject, vtClass and vtInterface… all of which are injected when doing case VarType(AVariant) of. If Variant doesn’t accept those types, why are they possible results of the VarType method?
Even without that problem, I just tested doing everything in the SmartString using Variant instead of all the separate (specific) casts, and it took 120% longer in every test… so that alone is a step in the wrong direction.
Further-more, having specific control over each cast allows flexibility and case-specific handling (such as in the case of casting String to Char, where a check needs to be performed to ensure that the String value is exactly 1 char in length)
As for Unicode > Ansi casting, I have been convinced that this is a bad idea, and am removing this for certain. I will, however, leave Ansi > Unicode in place, as there is NO data loss when passing an Ansi value to a Unicode container.
December 11, 2011 at 11:40 pm
The vtXXX constants are for TVarRec, i.e. the elements of an ‘array of const’ parameter. It’s the varXXX constants that pertain to variants. When Delphi doesn’t adhere to its usual strong typing, things can be confusing, eh?
Relatedly, there’s a TVarData alongside TVarRec. TVarData is the internal representation of a variant, which is different to the internal representation of an ‘array of const’ element.
December 12, 2011 at 7:32 pm
Wasn’t aware of this… but this is VERY valuable to know, so sincerest thanks! I can actually use this information in what I’m doing right now
December 11, 2011 at 3:05 pm
The overhead isn’t just with the operator, but also the calls and function entry/exit as record are maintained through rtti, and are not inlined or compiled. Assigning a smartstring to a smartstring is thus more expensive than assigning a string to a string f.i, and implicit cleanup slower.
As for ansi/unicode compiler warnings, I’ve not seen one that wasn’t beneficial to fix (ie they were all related to actual dataloss), of course if your app is strictly english, it’s just noise to you, but for international code, every single warning is an actual issues, and some warnings ate actually missing…
Explicit base type casts are also IME also a good thing, they’re what make strongly typed languages more solid than dynamic ones in the field, better spend a few seconds explicitly casting where needed than hours hunting for an incorrect cast that happened automatically, ymmv but I have never seen a project held back by explicit tostring/fromstring conversions
December 11, 2011 at 4:21 pm
You have convinced me, and I shall certainly remove explicit casting TO Ansi types (however there is no data loss when assigning an Ansi value to a Unicode String, so this can certainly remain)
December 12, 2011 at 5:23 am
Alas ansi to unicode isn’t safe either, there are a few rtl/vcl arising from that same assumption.
That’s because to convert to unicode you need to make assumptions about the codepage, and that codepage can be known oftimes only at runtime, and can be different from the system codepage (and the compile time codepage per ansi string introduced by d2009 just don’t cut it, actually, they don’t make sense most of the time you’re dealing with real-word international ansi strings).
So no it’s not safe, only utf 8/16/32 casts are
December 12, 2011 at 7:31 pm
Worth knowing… I was absolutely unaware of this. Doesn’t matter now, though, as Smart Type development has been indefinitely suspended due to Operator Overload performance nightmares!
December 11, 2011 at 8:43 am
Explicit warnings about string conversion where never a problem for me – on the contrary, it shows explicitly where a conversion is to be made.
I just went into another direction for cross-IDE Open Source handling of string. All my Open Source code relies on a specific RawUTF8 kind of string, which is UTF-8 encoded. It works very well from Delphi 5 up to XE2. I wrote a lot of optimized functions (faster than the standard versions – e.g. IntToStr). And made a clear distinction between RawUTF8 (using by the Business and Storage layers), and plain string (using by the UI layer, i.e. the VCL).
To summary, you wanted to get rid of the strong typing of Delphi – whereas to my understanding, strong typing is not a weakness. Your “smartstring” should have been better named “weakstring”. My choice to have an explicit RawUTF8 type was in fact quite the opposite: I want to rely on strong typing, which fits very well on big projects.
Since you do not supply any download link, and no SVN address either yet – http://svn.lakraven.com:81/svn/OpenSource was the only one I found – I was not able to review your code.
Of course, I think you will support Delphi 2006 and up only – since you need operator overloading for your type.
I share the idea of having a main common library at hand for several Open Source projects. I first put all needed code in my SynPdf library, then used my external SynCommons unit – about 30% of the SynPdf code was duplicated in both versions. But I put a lot more than low-level stuff (strings and structures) in there: Iso 8601 date & time, logging, unit testing, SQL and JSON parsing or creation, binary serialization, direct access to dynamic array and records using RTTI…
But I do not share the idea of mixing open and closed source. All my code is released under 3 licenses, including MPL for commercial use.
December 11, 2011 at 4:33 pm
1) Dropping Unicode > Ansi auto-casting, but leaving Ansi > Unicode auto-casting in place (because there’s no data loss passing Ansi value to Unicode container)
2) If you feel like sharing, I’d be very interested t osee your optimized methods (though there’s of course no obligation) since this could save time in the long-run.
3) I didn’t want to get rid of Strong Typing in Delphi…. quite the contrary, my goal is to make it easier in terms of the String type to assign values without having to explicitly declare that you’re casting an Ordinal Type to a String. It seems like a waste of time to me constantly declaring “IntToStr” when you KNOW you’re assigning an Integer to a String, and the compiler clearly has the ability to know that too.
4) If you’d read the article in its entirety, you’d have noticed that the LKSL hasn’t been released yet, and is going to be released at the same time as Lua4Delphi (since L4D uses the LKSL for common features such as Managed Arrays etc.)
5) I no longer support any version of Delphi older than 2007.
6) I’m not mixing Open and Closed source in the LKSL! It IS Open Source, but with the one restriction that you cannot modify the original units themselves. You can, of course, copy the content of a unit, give it a different name, and change it until your heart’s content. The LKSL is designed to be a base for MY OTHER PROJECTS, and so it needs to be consistent on everyone’s machine if they are to use one of my other projects.
It’s either that, or I stop doing open source entirely and slap a pricetag on everything!
December 11, 2011 at 5:55 pm
Thanks for your answer.
1) There is no data loss in conversion from Unicode, but there is some potential speed loss (especially because it uses some slow Win32/Win64 APIs, and reallocate a string).
2) All the Open Source code of several projects is available – with a lot of documentation (more than 700 pages) at http://synopse.info
3) There are also plenty of patterns when the compiler would make a StrToInt call then making an exception at runtime. Or add some performance problem. Or let you not use the RTTI for auto-persistence (is it a string? a number?). I always prefer knowing what is the generated code.
4) I did not notice this statement. I’m still more looking after DWS for better code reuse (and JavaScript code generation).
5) —
6) So a fork is allowed. But I still do not understand well the purpose of the emphasis about not modifying “your” branch. How could anyone do this? I’d rather see which kind of license you’re using. My little experiment in this is not to create its own license, but use existing. Several licenses help link your code to any project, even with a “viral” license like GPL. That’s why I was told by several users to publish in GPL, LGPL and MPL at once.
It is very nice to see Open Source code published in Delphi!
December 11, 2011 at 6:02 pm
I may ammend the license between now and release. Having thought about it I could just expressly state that I will not provide support for modified versions of the source, and explicitly mention that in the disclaimer of liability.
Surely you can understand the reasoning behind not wanting people to modify it when the vast majority of “bug reports” I’ve had related to other open source projects have been caused by the person tampering with the inner workings of the code. It’s especially annoying when they don’t declare that they’ve made changes when reporting their bug, and me wasting time chasing a bug that they caused, only to find out (after a lot of “are you sure you didn’t change anything?”) that they caused it, not my code.
December 11, 2011 at 7:21 pm
About people changing the code, then complying about it not working any more… we are 100% on the same line!
That’s why I always ask to use the latest trunk version in case of bug report, and post exact code to reproduce the issue.
I’ve even some users sending on the same time the issue report and a potential fix! Our forum states this – especially with SynPdf and mORMot. I’m a lucky open-sourcer, I think.
December 11, 2011 at 7:28 pm
It’s not just that they could break compatability with other sources (or even their usage of my stuff by their own code)… a lot of this LKSL is being extended by IDE plugins which require a specific architectural layout to the LKSL (for, primarily, the generation of code).
Those plugins are intended to be mostly commercial, and closed source. If people modify the actual design of any code in the LKSL required by those plugins, then I’d end up getting refund demands.
It’s far easier for me to stipulate “you must not modify the source” than deal with the consequences of failing to state that later.