-
Content Count
281 -
Joined
-
Last visited
-
Days Won
31
Posts posted by vfbb
-
-
Virtual interfaces: to create an object that support an interface in run time. See System.Rtti.TVirtualInterface
-
Sometimes I come across some delphi language enhancements that are undocumented or are poorly documented. In the last few years docwiki has improved a lot, but there are still undocumented cases, such as accessing an item in an array just by declaring a property, without any method:
TipGeolocation = record private FCoordinates: array[0..1] of Double; public property Latitude: Double read FCoordinates[0] write FCoordinates[0]; property Longitude: Double read FCoordinates[1] write FCoordinates[1]; end;
Share here any enhancements to the delphi language that you have discovered but are not documented.
-
4
-
-
Although I don't particularly like this design, you can make a descendant of TInterfacedPersistent, your class will not be arc and will be able to implement interfaces.
TBaseFrame = class(TInterfacedPersistent)
-
@Attila Kovacs You are right about compatibility, thanks for checking this out. I will put this observation. In the future I can do some backwards compatibility, but not now.
About the null values serialization, this library is just to consumes one rest api, then the serialization can occur just when you call a Post or Put and have the parameter ABody that is a record or a class or an array. In this specific case, the library will serialize the type as json, and in this case, the values that is null will be written to the json body without skip. The current implementation of System.JSON.Serializers don’t permit to ignore the fields with null values.
1 hour ago, mvanrijnen said:Ah ok, its for creating an API consumer,
(my first thought was something for declaring API's,, e.g. like in radserver (which we are using right now)).
Sorry i'll make it clearer
-
1
-
-
@Attila Kovacs Here one example without TValue.Make<T>, and with the nullables stating with IsNull True:
uses System.Rtti, System.TypInfo, System.JSON.Serializers, System.JSON.Readers, System.JSON.Writers, System.JSON.Types, iPub.Rtl.Refit; type TNullable<T> = record strict private FIsNotNull: Boolean; function GetIsNull: Boolean; procedure SetIsNull(AValue: Boolean); public Value: T; property IsNull: Boolean read GetIsNull write SetIsNull; end; TNullableConverter<T> = class(TJsonConverter) public procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); override; function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; override; function CanConvert(ATypeInf: PTypeInfo): Boolean; override; end; { TNullable<T> } function TNullable<T>.GetIsNull: Boolean; begin Result := not FIsNotNull; end; procedure TNullable<T>.SetIsNull(AValue: Boolean); begin FIsNotNull := not AValue; end; { TNullableConverter<T> } function TNullableConverter<T>.CanConvert(ATypeInf: PTypeInfo): Boolean; begin Result := ATypeInf = TypeInfo(TNullable<T>); end; function TNullableConverter<T>.ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; var LNullable: TNullable<T>; begin if AReader.TokenType = TJsonToken.Null then begin LNullable.IsNull := True; LNullable.Value := Default(T); end else begin LNullable.IsNull := False; LNullable.Value := AReader.Value.AsType<T>; end; TValue.Make(@LNullable, TypeInfo(TNullable<T>), Result); end; procedure TNullableConverter<T>.WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); var LNullable: TNullable<T>; LValue: TValue; begin LNullable := AValue.AsType<TNullable<T>>; if LNullable.IsNull then AWriter.WriteNull else begin TValue.Make(@LNullable.Value, TypeInfo(T), LValue); AWriter.WriteValue(LValue); end; end; initialization GRestService.RegisterConverters([TNullableConverter<string>, TNullableConverter<Byte>, TNullableConverter<Word>, TNullableConverter<Integer>, TNullableConverter<Cardinal>, TNullableConverter<Single>, TNullableConverter<Double>, TNullableConverter<Int64>, TNullableConverter<UInt64>, TNullableConverter<TDateTime>, TNullableConverter<Boolean>, TNullableConverter<Char>]); end.
To test:
TUser = record Name: TNullable<string>; Location: string; Id: Integer; Email: TNullable<string>; NonexistentField: TNullable<Integer>; end; [BaseUrl('https://api.github.com')] IGithubApi = interface(IipRestApi) ['{4C3B546F-216D-46D9-8E7D-0009C0771064}'] [Get('/users/{user}')] function GetUser(const AUser: string): TUser; end; procedure TForm1.FormCreate(Sender: TObject); var LGithubApi: IGithubApi; LUser: TUser; begin LGithubApi := GRestService.&For<IGithubApi>; LUser := LGithubApi.GetUser('viniciusfbb'); end;
-
4 minutes ago, Attila Kovacs said:-To be able to send and receive pure arrays "[]" , some php api's are working that strange way
Works perfectly in both cases, with the [] or with {..},{..},{..}. In both cases your function result should be a TArray<> of a record or a class.
8 minutes ago, Attila Kovacs said:-To be able to suppress the null values from the nullables (or a shouldmarshal callback/wallpaper)
Do you mean what happens with the nullable fields that were not received? Well, all fields are set to the default value before reading the received json, so there are several possible workarounds to ensure that the field not received is considered null. In sydney just add the class operator Initialize to set IsNull true, but in older versions, you can simply change the name of IsNull to IsValid, since the boolean fields started False, then the initial value of IsValid of nullable will be False. But there are other possibilities.
-
@Attila Kovacs About the version of delphi supported, according to docwiki, the units System.JSON.XXX was introduced in Delphi 10 Seattle, but again I can’t guarantee it’s compatible as it would need to be tested on these versions.
-
1
-
-
@Attila Kovacs I added support to register custom json converters.
About the nullables, is very easy to implement the nullable and the converters. See one simple solution:
uses System.Rtti, System.TypInfo, System.JSON.Serializers, System.JSON.Readers, System.JSON.Writers, System.JSON.Types, iPub.Rtl.Refit; type TNullable<T> = record IsNull: Boolean; Value: T; end; TNullableConverter<T> = class(TJsonConverter) public procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); override; function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; override; function CanConvert(ATypeInf: PTypeInfo): Boolean; override; end; { TNullableConverter<T> } function TNullableConverter<T>.CanConvert(ATypeInf: PTypeInfo): Boolean; begin Result := ATypeInf = TypeInfo(TNullable<T>); end; function TNullableConverter<T>.ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; var LNullable: TNullable<T>; begin if AReader.TokenType = TJsonToken.Null then begin LNullable.IsNull := True; LNullable.Value := Default(T); end else begin LNullable.IsNull := False; LNullable.Value := AReader.Value.AsType<T>; end; TValue.Make<TNullable<T>>(LNullable, Result); end; procedure TNullableConverter<T>.WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); var LNullable: TNullable<T>; LValue: TValue; begin LNullable := AValue.AsType<TNullable<T>>; if LNullable.IsNull then AWriter.WriteNull else begin TValue.Make<T>(LNullable.Value, LValue); AWriter.WriteValue(LValue); end; end; initialization GRestService.RegisterConverters([TNullableConverter<string>, TNullableConverter<Byte>, TNullableConverter<Word>, TNullableConverter<Integer>, TNullableConverter<Cardinal>, TNullableConverter<Single>, TNullableConverter<Double>, TNullableConverter<Int64>, TNullableConverter<UInt64>, TNullableConverter<TDateTime>, TNullableConverter<Boolean>, TNullableConverter<Char>]); end.
To use just replace the simple types to the nullable types:
TUser = record Name: TNullable<string>; Location: string; Id: Integer; Email: TNullable<string>; end; [BaseUrl('https://api.github.com')] IGithubApi = interface(IipRestApi) ['{4C3B546F-216D-46D9-8E7D-0009C0771064}'] [Get('/users/{user}')] function GetUser(const AUser: string): TUser; end; procedure TForm1.FormCreate(Sender: TObject); var LGithubApi: IGithubApi; LUser: TUser; begin LGithubApi := GRestService.&For<IGithubApi>; LUser := LGithubApi.GetUser('viniciusfbb'); end;
You can test the code above and see the user name is not null but the user email is null.
The nullable type and the nullable converter code in this example need to be implemented by the developer, I can't put it on the library because each developer has it own implementation os nullable.
-
1
-
1
-
-
21 minutes ago, Attila Kovacs said:I don't think so as I can't find System.JSON.Serializers under Berlin but under Tokyo.
I don't have older versions installed to be able to test, or do backwards compatibility, but it looks like you're right, delphi 10.2 tokyo or newer.
22 minutes ago, Attila Kovacs said:Is it possible to decorate the custom nullable fields that this internal serializer/reverter can translate them?
A one-time-registered type converter/reverter would be even better.
What exactly did you want? The json serialization of delphi is very malleable, you do not need to declare all the necessary fields and you can also use all existing attributes in System.Json.Serializers.pas.
29 minutes ago, Attila Kovacs said:A one-time-registered type converter/reverter would be even better.
It's a great option, I will implement it today.
-
A very simple way to consume one Rest Api, just declaring one interface, based on .Net Refit:
-
3
-
-
29 minutes ago, Bob Devine said:@vfbb Thanks for this looks interesting. Would be worth putting a link to DEB on the readme https://github.com/spinettaro/delphi-event-bus
Made.
33 minutes ago, Bob Devine said:There's another one here: https://github.com/THSoftPoland/DelphiMessageBus
It's not worth, it's a very limited system, synchronous, windows support only ...
-
51 minutes ago, Lars Fosdal said:I use OmniThreadLibrary queues for this.
This is just a simplistic messaging system. Don't compare a simple, lightweight 1k line system with a complete 50k line system (although messages work just as well).
-
The full explanation is on the github page. See this part:
"The problem
Delphi has its own messaging system (System.Messaging.pas) that works well but is totally synchronous and thread unsafe. In multithreaded systems we always need to communicate with other classes, sometimes synchronously, sometimes asynchronously, sometimes synchronizing with the mainthread (in the case of the UI), and doing this without a message system (communicating directly) makes the code large and complex, prone to many bugs."
-
Making it available to everyone who want a thread safe, asynchronous, cross-platform and simplistic messaging system for communication between classes / layers in delphi applications.
https://github.com/viniciusfbb/ipub-messaging
-
7
-
1
-
-
String works exactly like a bitmap (bitmap handle) from firemonkey.
When you create a string, through another, s2: = s1, this does not immediately create a copy of the string, but rather increases 1 refcount from a single original string, however when you change the string, internally it will check if the refcount is> 1 , it will create a copy, a new string with refcount 1, to make the changes, and not affect other places that are using that string. This was done to optimize, increase performance, as there are some manipulations of strings that in the end do not change the string, for example s2: = s1.PadLeft (0) will not create a copy, will return the string s1 with 1 more reference .The use of const in the parameter is an optimizer as it avoids operations with the refcount. However Marco Cantu warns of a problem generated when using const in the method argument, because if inside it you indirectly change the original string (for example by calling an event within its method) and the refcount of that string is 1 (which possibly will be ), within its method the argument with const will become invalid.
-
Right, I'm generalizing something that I shouldn't because I only work on multithreaded systems with high availability, and in this scenario, I really won't be able to use Weak.
But at least this limitation should be documented, since crashes generated by this limitation are difficult to diagnose. @loki5100 This can be your problem too, once it generate a generic crash like yours.
-
2 minutes ago, Anders Melander said:[weak] is not unsafe if you respect its limitations. Otherwise you might as well ague that strings are unsafe.
what is the scenario that does not fit to use Unsafe, that is really necessary Weak and that is really safe to use Weak? This scenario does not exist
-
35 minutes ago, Anders Melander said:If the weak reference is not thread safe then the the weak-to-strong copy also isn't thread safe.
As far as I can tell there's no way that [weak] references can be implemented thread safe so I don't see a bug here.
Yes Anders! There is no way to workaround this, the solution is redesign the code to doesn't depend or use weak attribute.
46 minutes ago, Dalija Prasnikar said:Weak references work fine and have their purpose when used in single thread scenario. Their purpose is not achieving some magical thread safety, but breaking reference cycles.
dalija, that's why Unsafe exists.
if you cannot transform WeakToStrong reference safetly, then you should not use Weak, you need to redesign your code to not use Weak, or if you just need to avoid circular reference, use Unsafe.
Don’t use Weak, it is dangerous. This is the title and this is truly, once it is not thread safe.
-
44 minutes ago, Dalija Prasnikar said:So, weak references in Delphi are not thread safe. Also even strong interface references in Delphi are not thread safe. So there is no bug here, just bad test case.
Yes! the weak reference is not thread safe. But if you cannot make a strong reference through the weak reference to check if the strong reference is null, as it is not safe, there is no benefit in having it in Delphi, nothing will differentiate you from Unsafe, except for the poor performance.
1 hour ago, FPiette said:@vfbb I had a quick look at your sample and I see a lot of threads accessing common data without synchronization. Are you sure that the issue doesn't come from this concurrent access?
The stress test was really confusing, but it's thread-safe, the only global variables that threads change are booleans.
Basically the test is to prove that this is not thread safe:
Another user found this problem and explained it in the comments of marco cantu's blog:
https://blog.marcocantu.com/blog/2016-april-weak-unsafe-interface-references.html -
Weak reference in interfaces is not thread safe and it is dangerous and should be avoided in high availability systems, this is my conclusion after some tests.
It is known that the weak attribute should always be avoided due to loss of performance, giving preference to the unsafe attribute whenever we can guarantee the existence of the interface when using the unsafe reference. On the other hand, Weak should be a powerful resource in programming, serving as a guarantee way to know if an interface still exists or not, because we create a strong reference through the weak reference and check if the strong reference is nil or not and then use it, the strong referenced interface.
It turns out that in the delphi's interfaces internal system, the ARC part is thread safe, but the object's Weak list is not. After the destruction of an interface, the Weak reference list is insecurely traversed setting nil, opening loopholes to use the Weak reference value in some part of the code after the interface is destroyed and before that Weak reference is set to nil. Although this is not clear in System.pas, we can prove it by testing.
Attached is a small stress test project that confirms this issue.
In view of this problem, what is the best alternative for the weak reference?
-
16 hours ago, Dave Novo said:Aside from breaking the application into different packages/projects, does anyone have any bright ideas.
This is the correct solution and the Delphi ide/compiler 64 bits is far.
I faced this problem on a daily basis, the project became too big, and a solution adopted by us was to divide the project into packages, so as not to overload with a single compilation. You can for example divide main project, FMX package and RTL package.
-
1
-
-
On 9/27/2020 at 7:41 PM, Dave Nottage said:You'll find you won't be able to debug with a device that has iOS 14.0. Deploying will work; just not debugging.
One more info: in release mode will give error too, but you will see that the app has been installed, just open it manually.
-
4 hours ago, PeterPanettone said:Eureka: I have found a solution at Stack Overflow:
Please vote for it!
Yes. Sorry, I forgot to tell, I do the same. This occur randomly with any control. I believe they didn't fix it because it doesn't seem easy to replicate.
-
This problem is old, it is at least 2 or 3 years old.
Android 11 Support in 10.4.2?
in Cross-platform
Posted
I'm not going to talk about delphi 10.4.2 because I still have 10.4.1. But on sdk, you don't need to use this Embarcadero tool, it is almost always a version behind. Ideally, you should install Android Studio on your own, open it and Configure> SDK Manager and install (download) the latest sdk, get the directory and configure it in your Delphi Options > Deployment > SDK Manager> Android