You are not logged in.
Pages: 1
To each their own - all I can say buying the V5 license at my company was a no-brainer and we have not looked back a single second.
Anyway, what I was getting at is that FastMM5 is open source - it is on Github and you can read the code, which is the definition of open source. Is it free? Well, that depends on what license you will choose. (yes, nitpicking, but I too often see people confuse open source with free)
And its license is not Open Source
This is the second time I hear/read this (the other person was Macro on the Delphi 11 webinar) stating this: it is not accurate - FastMM5 is dual licensed, either GPL3 or a commercial paid license - see https://github.com/pleriche/FastMM5/blo … md#licence
While there might be some issue with mORMot we are using it for quite a while now on hundreds of customer installations on tenthousands of workstations.
Please consider the another alternative to fix it in the recent Delphi compilers with Managed Record inside TAutoFree.
Will not work - see this code:
type
autofree = record
class operator finalize(var value: autofree);
end;
class operator autofree.finalize(var value: autofree);
begin
Writeln('finalize');
end;
function get: autofree;
begin
end;
procedure Main(const i: Integer);
begin
if i = 42 then
get;
Writeln('end');
end;
begin
Main(42);
Readln;
end.
Will write 'finalize' before 'end'
You are confusing something - the comment you have in mind was to another JIRA report that claimed a codegen error while in fact the code returned a pointer to a temporary Variant variable.
Also List and PList are not internal but part of the TList<T> public API since a few versions.
What is really terrible about this change is that it created a lot of unnecessary codegen regressions left and right rather than making anything better.
Like this: https://quality.embarcadero.com/browse/RSP-34930 and this: https://quality.embarcadero.com/browse/RSP-31615
FWIW it was this bug that caused this: http://qc.embarcadero.com/wc/qcmain.aspx?d=79943
Thanks, will have someone more experienced with mobile look into it
I was looking into this approach for some feature I am implementing currently as it seemed the most practical solution to my problem (per class metadata similar to your use case).
However I only know how to write protected memory on Windows and not how that would be done on the Mobile platforms. Do you have any information about that?
I know you don't but some people do when it comes to these comparisons.
Yes it did not get worse. But is that good? Other compilers are getting better or faster over time and we are happy if it doesn't get worse? I know that is not true.
But not only the compiler but also the RTL. I know there have been some - well let's say it polite - bugs in some places that made one or another routine dead slow.
FPC/Lazarus is very promising!
Yeah, for over ten years now...
What I am missing with all these recent performance comparisons is an exact analysis what makes the difference if there is one and what parts could be improved.
Like for example you say D7 is fastest which is because of the optimized RTL. I see a different of like 10% give or take between D7 and XE6.
Is it really the RTL that is making these 10%? Or is it something different? Maybe the RTL would make 20% but the generated code got 10% worse?
So imho without further analysis we can only guess and claim that poor code generation in some parts are "slowing it down" and that compiler X or Y is sooo much better.
Could not agree more!
Instead of trying to implement every possible OOP feature into records they should focus on bringing object on par with classes.
The problem in this case is not even the missing member of the class type.
What is missing is additional RTTI for generic types (like the typeinfo for the generic parameters) just similar to the information you get for array types.
When you have a argument of the type IList<TClient> you have to retrieve the type info for TClient to be able to create instances of that class when deserializing.
In that case you can use a class like I posted above.
Currently afaik you only can get the generic parameter by analyzing the class name (in our example extract the TClient from IList<TClient> and then iterate all known types and compare it - but even then you cannot be sure if you get the correct type unless its a public type which is fully qualified).
Anyway I got the IList<T> support partially working - just trying to solve the problem I just explained.
The thing is TObjectList and not typesafe and ugly to use in client side code (yes, can write typesafe derived classes for that but come on...) and already on the way of getting removed from the RTL (only for nextgen though but anyway) and TCollection requires the items to be inherited from TCollectionItem which may not be possible.
Let's do it step by step - I can register custom json serializers if I want for example to use Generics.Collections.TList<TCustomer>, right?
How does that work?
I do not see much benefit of this IList<> kind of structures in comparison to the existing content.
And in the current implementation, both TList and TObjectList are supported by ObjectToJSON().
On its side, JSONToObject() is already able to un-serialize a TObjectList class, when a class type is supplied as TObjectListItemClass optional parameter for its items, or if a "ClassName": member is already present at JSON serialization level.
So you are already able to serialize/unserialize any TObjectList (or inherited) instances...
The problem is when the client side exclusively works with IList<> as our software currently does (would be the same if we were using the Generics.Collections ones though). That would mean extra work to create wrappers which mORMot can work with.
So the better way imo would be to work with these types directly. So what this needs is some mechanism to register custom types if you don't want the framework to have more dependencies than needed (which I understand) - I guess this is also responsible for creating the instances on the server side (haven't looked into it yet).
Of course with generics this is a bit more complicated as you cannot just register a type info.
I wonder if you can take advantage of the enumerator for serializing and the existence of the GetElementType and the Add method for deserializing.
Much like I have done for XML serialization:
TXmlWriter.WriteEnumerable and TXmlReader.ReadEnumerable
I think an array of objects currently is not supported also, isn't it?
Serverside you can use an implementation like this:
uses
Spring.Collections,
Spring.Collections.Lists,
TypInfo;
type
TList = class(TObjectList<TPersistent>)
private
fElementType: TClass;
protected
function GetElementType: PTypeInfo; override;
public
constructor Create(elementType: TPersistentClass);
end;
constructor TList.Create(elementType: TPersistentClass);
begin
inherited Create;
fElementType := elementType;
end;
function TList.GetElementType: PTypeInfo;
begin
Result := fElementType.ClassInfo;
end;
Using it would be like this:
var
list: IList<TCustomer>;
begin
IList<TPersistent>(list) := TList.Create(TCustomer);
list.Add(TCustomer.Create);
end;
As you can see this is code that can be invoked dynamically as it does not involve any generics anymore (given the restriction that only IList<T: TPersistent> is allowed as parameter.
It could also be TObject if the JSONObject routine would allow TObject (which can have the $M+ be defined - which happens automatically when adding published properties)
You can take a look at Spring4d as I was talking about those IList<T> types (http://code.google.com/p/delphi-spring- … ctions.pas).
IList<T> inherits from IEnumerable which has the GetElementType method that returns the PTypeInfo of T.
T can basically be any type but I am mostly interested in class types.
So I can write a service that has methods like:
function LoadCustomers(out customers: IList<TCustomer>): Boolean
But this already fails at startup because interface parameters are currently not supported.
Does the framework support JSON serialization for custom collection types (I am thinking of IList<T>)?
Is there a way of using TInterfaceStub or TInterfaceObjectFake to create a proxy for an interface by just specifying the OnInvoke callback (just like you can do with TVirtualInterface)?
With TVirtualInterface I can create something like this (quick & dirty implementation):
TVirtualInterface.Create(TypeInfo(IMyInterface),
procedure(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue)
var
LArgs: TArray<TValue>;
i: Integer;
begin
SetLength(LArgs, Length(Args) - 1);
for i := Low(LArgs) to High(LArgs) do
LArgs[i] := Args[i + 1];
Result := Method.Invoke(TValue.From<IInterface>(instance), LArgs);
end).QueryInterface(IMyInterface, proxy);
Looks better now however I still prefer an extra property for the Result of a function (instead of having it be the last item of the Output array) since changing the signature of the method can break existing test code just because Output[1] is not the result anymore but Output[2] for example.
Also why not return the values of var/out parameters the same value it was passed in (instead of having them separate in Input and Output which does not have matching indizes)?
... won't compile.
I was talking about the IFoo.Bar signature. If you have a unit test which just includes the interface definition which has been changed, it will compile for sure.
I suspect you don't recreate the TVirtualInterface class before each call.
Just try to do benchmark whole fake class instance creation, and you will see a bigger time difference in favor of TInterfaceStub.
Execution should be more or less the same. What is pretty slow is the RTTI method information retrieval and the interceptor as implemented by Embarcadero.
Yes, I was just referring to the execution. The caching of the rtti information could be done in my implementation aswell but currently I am more focused on the execution speed (since I am using this whole mechanism for AOP provided by a DI container).
Is JSONEncodeArrayOfConst() so "ugly"?
"Ugly" in the sense of I cannot handle parameters as I would do it in the real implementation and it easily can break because of handling them as strings.
Change the var to const for example. Suddenly i is not part of the result anymore and the method is returning the wrong value.
Btw when just testing TVirtualInterface vs TInterfaceStub (so just raw Rtti.pas stuff with no DSharp.Interception) on a parameterless method the performance is very close. Rtti implementation gets slower the more parameters are used - most likely because of copying around TValue.
Thanks for explanation.
I made a quick test to see how it feels compared to my solution.
Here is what i wrote:
unit TestFoo;
interface
uses
TestFramework, mORMot;
type
TFooTestCase = class(TTestCase)
private
function ExecuteBar(var Params: TOnInterfaceStubExecuteParams): Boolean;
published
procedure TestBar;
end;
implementation
uses
Diagnostics,
DSharp.Testing.Mock,
SynCommons,
SysUtils;
type
{$M+}
IFoo = interface
['{26D6D411-5994-4EEB-8EE1-9D6DCA8D5EA8}']
function Bar(var i: Integer): Integer;
end;
function TFooTestCase.ExecuteBar(var Params: TOnInterfaceStubExecuteParams): Boolean;
var
P: PUTF8Char;
begin
P := Pointer(Params.Params);
Params.Params := IntToString(GetNextItemCardinal(P) + 1);
Params.Result := '[42]';
Result := True;
end;
{ TFooTestCase }
procedure TFooTestCase.TestBar;
const
CallCount = 100000;
var
foo: IFoo;
fooMock: Mock<IFoo>;
sw: TStopwatch;
i, k: Integer;
begin
k := 0;
sw := TStopwatch.StartNew;
TInterfaceStub.Create(TypeInfo(IFoo), foo).Executes('Bar', ExecuteBar);
for i := 1 to CallCount do
begin
CheckEquals(42, foo.Bar(k));
CheckEquals(i, k);
end;
Status(Format('mORMot: %d ms', [sw.ElapsedMilliseconds]));
// -------------------------------------------
k := 0;
sw := TStopwatch.StartNew;
fooMock.WillExecute(
function(var Args: array of TValue): TValue
begin
Result := 42;
Args[0] := Args[0].AsInteger + 1;
end).Any.WhenCallingWithAnyArguments.Bar(k);
foo := fooMock;
for i := 1 to CallCount do
begin
CheckEquals(42, foo.Bar(k));
CheckEquals(i, k);
end;
Status(Format('DSharp: %d ms', [sw.ElapsedMilliseconds]));
end;
initialization
RegisterTest(TFooTestCase.Suite);
end.
You said, RTTI cannot handle var and out parameters. That is not true (at least not with my implementation).
This code does not run it raises "EInterfaceFactoryException at $0052CF3B Invalid "IFoo" interface call: returned item".
mORMot is approx 4-5 times faster than the DSharp implementation.
While it's great that mORMot runs on older Delphi versions its handling is really ugly imho as you can see in the ExecuteBar method (if it would work).
I think the TOnInterfaceStubExecuteParams should provide the params and the result in an easier to use way (like Variants or something like that).
Honestly, I'm still not convinced by the parameters dummy values to be set.
Is it worth it?Most tests (i.e. just stubbing a method without any parameters expectations) will be more difficult and verbose to write IMHO.
I think it depends on what you are doing ("Mocks aren't stubs")
and how restrictive and specific you want to use a mock and its expectations.
My concern is that you can not just call SmsSender.Send() with no parameters...
Two possibilities:
- Make all parameters optional (may introduce issues);
- Force putting some parameters (code will inflate and be difficult to write).How to resolve this?
Imo the benefit of having to pass the parameter is that you can easily define expectations for matching parameters.
If you don't care about parameters just say so (I called the method WhenCallingWithAnyArguments in my mocks) and pass dummy values (empty string, 0, nil, ...).
By setting a flag during the SmsSender.Send() fake execution, which may be retrieved by When()?
Could make sense... an idea to explore, I suspect.But then, when the instances are injected to TLoginController, they should behave as expected.
For instance, all the internal journaling should be reset before execution.Was it what you proposed?
Exactly
How refactoring will break the code in your opinion?
You do not need to specify all parameters every time, and you can add methods to any interface without breaking the stubbing/mocking code.
Renaming a method or changing signature will cause the test to fail at runtime, not at compiletime. And that is for every test that uses a mock of the changed interface.
Using strings as identifier is one of the major weak points of certain mock frameworks, while others solve that problem by avoiding that.
I think even without generics (although they allow a much nicer fluent interface style to define expectations) this is possible (not perfect though)
procedure TMyTest.ForgotMyPassword;
var
SmsSender: ISmsSender;
UserRepository: IUserRepository;
begin
TInterfaceStub.Create(TypeInfo(ISmsSender), SmsSender);
When(SmsSender.Send()).Returns(True);
TInterfaceMock.Create(TypeInfo(IUserRepository), UserRepository, Self);
When(UserRepository.Save()).ExpectsCount(qoEqualTo, 1);
with TLoginController.Create(UserRepository, SmsSender) do
try
ForgotMyPassword('toto');
finally
Free;
end;
end;
The method call to the mock/stub interface which is done before calling When captures the method/instance and Returns and ExpectsCount apply the expectation to this method/instance.
This should work down to Delphi 6 for any function with the matching overloads for When, just not for procedures.
Edit: changed the order of the calls
The biggest problem with that implementation is that any typo or refactoring will break your code (isn't that one of the biggest complaints about LiveBindings also?)
Pages: 1