Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 01/13/22 in all areas

  1. PeterBelow

    Interface question

    @David IMO you are mixing data with UI in a way that only causes problems. You should use a list class instance (nonvisual) to hold objects that represent a student, each of which holds a list of the classes taken with the score obtained. The list class can then have a method to compose the items for a listview you pass as parameter to the method. If you need that you can store the reference to the student object in the corresponding listitem's Data property, but the lifetime management of the objects is the duty of the list class, not that of the UI listview.
  2. In case anyone is interested, and better, want to contribute, I've upgraded THunSpell which was originally written by Stefan Ascher to support Unicode Delphi and created a github repository. Attached Demo screenshot.
  3. Uwe Raabe

    DPI and Scaling Discussion

    In Delphi 11 there is an option to speed up VCL styled applications which is probably not commonly known, because it wasn't communicated much: Vcl.Themes.TStyleManager.UseParentPaintBuffers While the linked doc is pretty much useless, the common description gives some more insight: Styles Performance Improvements
  4. Clément

    Interface question

    Why don't you use the TListView in Virtual mode (OwnerData := True)? Use the OnData event with Item.Index as your index and use an objectList like TStudentList = TObjectList<TStudent>; get the data from this list, for example: procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem); begin var lStudent := glbStudentList[ Item.Index ]; Item.Caption := lStudent.Name; Item.Subitems.Add(''); // lStudent.Score1 ? Item.Subitems.Add(''); // lStudent.Score2 ? Item.Subitems.Add(''); // lStudent.Score3 ? Item.Subitems.Add(''); // Total ? end; Remember to "feed" your listview using the count property: MyListVview.Items.Count := glbStudentList.Count; This assignment is required everytime you add or remove a student from the list.
  5. When I open any particular forum item it always opens on the first page. I guess this is a good idea if it's the first time one is reading that set of postings. But surely it is more common that you are going back to see recent additions to and ongoing discussion ( I do this regularly ). It would be much more convenient if, when opening and individual forum item, it opened on the last page (and perhaps even showing the most recent (ie the last) entry ?)....
  6. aehimself

    Interface question

    To be honest I don't see the reason why interfaces are required here. If the objects are freed up multiple times that clearly indicates a coding issue; using interfaces for lifecycle management in this case seems a bit like duct tape on a totaled car. I am also storing objects in .Data properties, using the OnDelete handler to manually free these up and set the .Data to nil. Never had a single issue with multiple release attempts or leaks (except if VCL styles are active but that's a different topic). Out of curiosity you can add a FreeNotification handler and put a breakpoint in that. That way you could examine what is freeing your objects up when you don't want them to, yet.
  7. Epo

    Interface question

    Delphi in a nutshell - Ray Lischner - Nutshell.pas
  8. Uwe Raabe

    Interface question

    Storing an interface in a pointer stinks right from the beginning.
  9. Uwe Raabe

    Interface question

    Well, as I said, I couldn't test it. Fortunately the class helper approach lets us implement a working solution at one place: procedure TListItemHelper.SetSnapTally(Value: ISnapTally); var I: ISnapTally; P: Pointer absolute I; begin P := Data; I := Value; if Assigned(I) then I._AddRef; // because when I goes out of scope the refcount is decremented Data := P; end;
  10. Uwe Raabe

    Interface question

    Assigning an interface to a pointer skips reference counting. Thus the instance behind snap_Tally is destroyed when leaving the scope (that's the end in your code snippet). Next, when you retrieve li.Data, you cast it as TSnapTally despite the fact that you assigned an ISnapTally before (disregarding that the instance is already freed). That won't work either, because assigning nil to a Pointer doesn't trigger the reference counting even if there is an interface instance behind it. I cannot test it in the moment, but you might succeed with the following approach: if not Assigned( ARDomainsLV_ItemWithCaption( deets[n], li ) ) then begin var snap_tally : ISnapTally; snap_tally := TSnapTally.Create( aSnap ); li := AnalysisResultsDomains_lview.Items.Add; li.Caption := deets[n]; li.SubItems.Add( '' ); // #detail recs li.SubItems.Add( '' ); // LM (1,2,3) li.SubItems.Add( '' ); // Org 1-10 li.SubItems.Add( '' ); // Org 11-20 ISnapTally(li.Data) := snap_tally; // this should add the ref count properly end; TallyToListItem( li, dom_deets, aSnap ); . . . procedure TallyToListItem( li : TListItem; aDomDeets : TDomainDetails; aSnap : TLSHMSnapshot ); var st : ISnapTally; begin st := ISnapTally( li.Data ); // proper ref count st.IncNumDetailRecs; . . . ISnapTally( li.Data ) := nil; // proper ref count For simplicity I would introduce a class helper for TListItem that captures all these castings: type TListItemHelper = class helper for TListItem function GetSnapTally: ISnapTally; procedure SetSnapTally(Value: ISnapTally); property SnapTally: ISnapTally read GetSnapTally write SetSnapTally; end; function TListItemHelper.GetSnapTally: ISnapTally; begin Result := ISnapTally(Data); end; procedure TListItemHelper.SetSnapTally(Value: ISnapTally); begin ISnapTally(Data) := Value; end;
  11. Sliding into off-topic, but I avoid parameters to constructors. The primary reason is that parameterless constructors are a good fit for polymorphic constructs, as well as instantiations for generics. For configurations, I often give the instance an interface to fetch its own config from. DI FTW.
  12. Uwe Raabe

    REST Request is not working well on Delphi 11

    The second call seems to have some query parameters inside the URL while the first one does not. This issue has a workaround in the comments: https://quality.embarcadero.com/browse/RSP-35365
  13. Fair enough, it was more an example to show that relying on the parameter type is sometimes not enough. But as a library writer I don't think it's bad to give a better error message to the user of my classes (whose code I have no control of) telling him what he did wrong, instead of raising a cryptic AV and waiting him to realize what he did wrong or even blame the framework itself.
  14. A very common case is when you inject objects and interfaces in the constructor. At the very least you would want to check if they are not nil.
  15. The way I am reading that is: if you pass parameters in constructor to initialize fields and you validate them there, raising exception if they are not valid, then you can avoid checking those fields when you are using them in other places because you know they cannot hold invalid values.
  16. Wow, what a long thread and discussion for something "simple". Use the 25-year old pattern for Delphi memory management: Foo := TFoo.Create; try finally Foo.Free; end; And let the exception raising begin. Anywhere.
  17. No, by having a destructor that can handle partly constructed objects.
  18. Again, can somebody explain what problem is caused by raising in a constructor. We all seem fine with TFileStream doing this, so all I can see is dogma.
  19. What do you think about TFileStream.Create raising an exception if the file can be opened in the requested mode for whatever reason? I cannot see the problem with this, or indeed any constructor raising. If you are going to admit exceptions in your program at all, you must be resilient to them. Why is it any different to get the exception raised in a constructor than anywhere else.
  20. The problem with this pattern is that most methods of the class now has to perform some kind of validation before doing their job.
  21. Yes. All fields are zero initialized. There is no double safety if something cannot ever happen. If you have extra initialization code then such unnecessary code can obscure other important code. Keep it simple. If you are not sure what is initialized and when, it is better to check and learn than to use code just in case. It is fine to construct as many instances as you need and protect them with single try...finally. You don't need to pay attention to the order of destruction, unless there is some special reason where one instance still holds reference to other one you are destroying sooner, where you might have problem with dangling pointers. There is no functional difference, only one nil assignment more in your case - if you feel like you can more easily follow the code if multiple instances are all initialized before try..finally, that is fine. Remember, try...finally is used just for cleanup in case of exception, not for handling the exception. If exception is raised inside try..finally it will be propagated to the next exception handler block - try...except just the same it would if it is raised outside try..finally. Again, if there is exception raised in constructor, assignment to the variable will never happen. So if LA is nil and the you call LA := TRaiseOnMinus.Create(-2) in finally block LA will still be nil. Automatic cleanup for that unassigned object instance is happening in hidden code inserted by compiler. If the instance is successfully created, then automatic cleanup will not happen. Raising exceptions in constructor is not evil nor bad practice. It is perfectly fine thing to do. Whether you will raise exception in constructor or not, is matter of each particular class functionality. If you pass invalid parameter to a constructor it makes sense that such constructor raises exception, because otherwise your object is not properly functional instance, If you don't raise exception then, you would have to sprinkle your code all over inside that class and where you use it with checks for that particular invalid condition. When you have some condition that causes failure failing soon is always better than failing later on. If you have some reasons not to raise exception or there is nothing special that would require raising exception that is also fine. Constructing objects can always raise OutOfMemory so you always need to be prepared to do the cleanup anyway. Take for instance TFileStream - if constructor fails to open or create file it will raise an exception. And you can handle that failure at that point. If you don't have to handle it at that point, that means you have opened file too soon.
  22. Some bullet points: if Assigned(Obj) then Obj.Free is redundant, you can just call Obj.Free - Free is static and can be called on nil instance and also Free has nil check within before calling destructor constructors are allowed to raise exceptions destructors must never raise exceptions (without handling them within with try...except) destructors can be called on half constructed instance (if the constructor fails, destructor chain will be automatically called) and must be able to handle such scenarios without causing trouble - raising exceptions when constructor fails assignment to variable will never happen local object variables are never automatically initialized and they can contain random garbage object instance fields are always zero initialized when constructor chain is being called Because of the above correct construction pattern is var LA: TRaiseOnMinus; begin LA := TRaiseOnMinus.Create( -2 ); try //some code finally LA.Free; end; end; With multiple constructors there are multiple options, but I prefer using only single try...finally because destructors must not raise exceptions, single try finally is faster and cleaner var LA, LB: TRaiseOnMinus; begin LB := nil; LA := TRaiseOnMinus.Create( -2 ); try LB := TRaiseOnMinus.Create( -3 ); //some code finally LB.Free; LA.Free; end; end; Correct constructor and destructor for your class would be: constructor TRaiseOnMinus.Create( AParam : Integer ); begin inherited; if AParam < 0 then raise Exception.Create( 'Bum' ); FMyList := TStringList.Create; end; destructor TRaiseOnMinus.Destroy; begin FMyList.Free; inherited; end; Only if you need to do some additional work with FMyList in destructor, then you must check it for nil before using it. In such cases you can find call to Free within the same check, but not because it wouldn't work outside. destructor TRaiseOnMinus.Destroy; begin if Assigned(FMyList) then begin FMyList.DoSomethingImportant; FMyList.Free; end; inherited; end;
  23. Serge_G

    error on Interbase 2020 server

    WITH dt2 AS ( select goods_id, sum(qty) as inc, cast(0 as float) as sale, cast(0 as float) as writeoff from income where cast(recdate as date) <= :d group by goods_id union all select goods_id, cast(0 as float), sum(qty) as sale, cast(0 as float) from sales where cast(recdate as date) <= :d group by goods_id union all select goods_id, cast(0 as float), cast(0 as float), sum(Qty) as writeoff, from writeoff where cast(recdate as date) <= :d group by w.goods_id) ) select G.Goods, G.Goods_id, coalesce(dt2.incomes,0)as incomes, coalesce(dt2.sales,0) as sales, coalesce(dt2.writeoffs,0) as writeoffs, coalesce(dt2.endqnts,0) as endqnts From Goods G JOIN dt2 on G.Goods_id=dt2.Goods_id on G.Goods_id=dt2.Goods_id order by G.Goods I think that, if CTE is really effective in Interbase 2020, this one is more clear Some notes : - Using alias in a union is not useful - I change the where clause in the last part of the union : where cast(recdate as date) <=:d ( hoping that this column exists in the table "writeoff") Question the JOIN between Goods and dt2, it shouldn't rather be a LEFT JOIN
×