Jump to content

Dalija Prasnikar

Members
  • Content Count

    1062
  • Joined

  • Last visited

  • Days Won

    91

Everything posted by Dalija Prasnikar

  1. 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.
  2. 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;
  3. Dalija Prasnikar

    File-> Open recent: way off the top of the screen

    Yes, there is. I forgot about that. I have a need for large number of projects and files and when I get into a trouble with the menu, it is easier for me to remove specific files than to have smaller number of files remembered.
  4. Dalija Prasnikar

    File-> Open recent: way off the top of the screen

    This issue seems to happen when you have too many files in the recent menu. Open properties - most bottom option in the menu and delete few files (how many depends on the size of your screen) from the recent list. That should fix it. If it is not fixed, you will need to remove more files. Reported as https://quality.embarcadero.com/browse/RSP-36702
  5. Dalija Prasnikar

    Parallel processing question

    Instead of anonymous methods, you can also use plain TNotifyEvent and pass list item as Sender. Also if you don't like the code in completion handler because of TThread.Queue, this call can be moved to UpdateItem. There are many ways to write this functionality, this is just one.
  6. Dalija Prasnikar

    Parallel processing question

    But it is completely decoupled in that code. The only connection is in attached completion handler that can hold any kind of code and is not part of processing class.
  7. Dalija Prasnikar

    Parallel processing question

    Yes, you can use that approach, but it is not flexible because your processing function depends on the specific UI type. I wouldn't create task inside Execute method because processing functionality itself should be implemented as clean synchronous code - that gives you more flexibility to combine its functionality as needed. I would use something like: TMyClass = class public procedure Execute(OnCompleted: TProc); end; procedure TMyClass.Execute(OnCompleted: TProc); begin // process data //... if Assigned(OnCompleted) then OnCompleted; end; procedure TMyForm.ProcessItem(li: TListViewItem); begin TTask.Run( procedure begin TMyClass(li.Data).Execute( procedure begin TThread.Queue(nil, procedure begin UpdateItem(li); end); end); end); end; procedure TMyForm.UpdateItem(li: TListViewItem); begin li. ... end; procedure TMyForm.ProcessView; begin for var li in lview1.Items do begin ProcessItem(li); end; end;
  8. Dalija Prasnikar

    Parallel processing question

    You are right that the processing logic and updating UI don't belong together. When I have processing functionality as part of the class (and I usually do) I also declare completion handlers in that class. And then you can easily attach any kind of additional logic when processing is finished - including updating UI. The most important part is that all individual pieces of code can be easily unit tested (except for UI) and that there is minimal gluing code where you can make mistakes when you put all of the code together.
  9. Dalija Prasnikar

    Parallel processing question

    That is true, there is no OnCompleted event You can do that inside the task code. You can either send message with some messaging system or call appropriate code directly (make sure it is synchronized with the main thread for UI interaction). To free thread for further processing it is better to use TTask.Queue instead of TThread.Synchronize, but if you need to perform some cleanup, Synchronize will do as long as it is not too slow. Something like: TTask.Run( procedure begin // your background work ... // task is completed TThread.Queue(nil, procedure begin // update the UI end); end);
  10. Dalija Prasnikar

    When free isn't free anymore..

    Since Delphi 10.4 memory management across all platforms has been unified. That means Free and FreeAndNil behave on android just like then always behaved on Windows. There is still DisposeOf, but it just calls Free now. You can change those calls, but you don't have to. The only place where you can have leaks on Android due to this change is if you have been relying on ARC to release your objects and you were just assigning nil to them or you were just letting them go out of scope.
  11. Because of this https://stackoverflow.com/a/42305385/4267244 But, once you have determined your weak reference is valid, you can take strong reference out of it. Once you have strong reference you can do what you need with that reference. procedure TObjectWithTest.QueueCallSafe([weak]AInterface: ITest); begin TThread.ForceQueue(nil, procedure var Strong: ITest; begin if AInterface = nil then ShowMessage('nil') else begin Strong := AInterface; ShowMessage('not nil'); end; end); end; Again, this only works if there are no background threads involved.
  12. Depends what you mean by fixing it? If you are fine that under some circumstances you will get nil inside QueueCallSafe and you check for such situation then you have fixed it. Be aware that this code will only work if object is released in the context of main thread - Obj.Free line. If you have that kind of code in some background thread then code in QueueCallSafe will not be safe.
  13. Dalija Prasnikar

    Long term availability of Community Edition

    There is no guarantee it will be available in the future. We can only hope it will be
  14. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    Yes.... forget the fake interface approach... brain fart...
  15. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    Possible solution for interface problem would be using fake interface instance as magic value as reference counting on that would not cause any problems (Note: I haven't tested such solution). function NopAddRef(Inst: Pointer): Integer; stdcall; begin Result := -1; end; function NopRelease(Inst: Pointer): Integer; stdcall; begin Result := -1; end; function NopQueryInterface(Inst: Pointer; const IID: TGUID; out Obj): HResult; stdcall; begin Result := E_NOINTERFACE; end; const FakeInterfaceVTable: array [0 .. 2] of Pointer = (@NopQueryInterface, @NopAddRef, @NopRelease); FakeInterfaceInstance: Pointer = @FakeInterfaceVTable; At this point, solution with lock seems like simpler approach if it makes sense to avoid unnecessary instance construction. On the other hand, if you are to write some universal lazy initialized container that will work on different types, then this solution even though more complicated would pay off.
  16. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    It will crash at FSingleton := TSingleton.Create during assignment. At that point FSingleton value is pointer(1). FSingleton is interface, and assigning new value will try to call _Release on any non-nil old value to maintain appropriate reference count and that old value is invalid address.
  17. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    That is better solution (it would require additional if to prevent calling CompareExchange for every access, similar to original example (full code)) , but it does not work with interfaces. Your example used additional lock, so it wasn't really clear what you are objecting to. Also, with lazy initialization code path where possible unnecessary object construction happens is extremely rare situation and even if it does happen lock in memory manager not something that would bother me. Because it happens rarely, even cost of explicit lock is negligible so I never used that "potential" performance issue as something worth measuring and proving which solution is faster. The only considerable argument is how costly is the construction of the object that might get discarded, but using lock in memory manager is a poor way to prove that point.
  18. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    InterlockedCompareExchangePointer is part of Windows API. AtomicCmpExchange is added to provide cross-platform support. There is also TInterlocked class in System.SyncObjs that implements various static functions for performing interlocked operations, including CompareExchange that basically calls AtomicCmpExchange, but it is more convenient to use for some types.
  19. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    And how exactly would you do that in this scenario? Not to mention that you are opposing simple full pointer exchange because it is too "clever", and your solution would be bit fiddling. Comfortable, prefer, personally use... those are all the same reasons, just different words. You are reading into words way too much. You don't want to use something because you have subjective reasons. I have no problem with that and I am doing the same in many places. The only reason why am I here objecting, is you saying this is not good pattern and this is not true and it sends wrong message to others that are not familiar with this kind of code. By you saying this is bad code, someone else might avoid such coding pattern because they will wrongly think it is a bad one. The only place where I wouldn't use this pattern is in places where constructed object is extremely heavy and where constructing object that would possibly be immediately discarded would consume plenty of resources or would take a lot of time. I already said why it is good, but I will repeat it. It is lock-free, so the whole operation can be done with single reference (I am not talking about local variables, but broader scope where final reference is declared). There is no additional, separate entity involved. That is reason enough.
  20. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    It is still separate entity. I don't mind you or anyone else writing code they are more comfortable with. However, I will firmly defend this pattern as good code simply because it is. All lock-free code is "clever", there is no way around it and this is the simplest pattern as it gets for lock-free. And this pattern is also recognized and used across different languages. For anyone that wants to learn more and get out of their comfort zone, this is a good example to learn from.
  21. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    The critical section or any other kind of lock requires another object that needs to be constructed and maintained separately for no reason. There is no need for introducing locks of any kind here. This is not only working code, but also good code for such situation.
  22. Dalija Prasnikar

    SudokuHelper - Example for uncoupled design via interfaces

    It is thread-safe. This is commonly used pattern for thread-safe lazy instantiation.
  23. Dalija Prasnikar

    My Experience with D10.4

    We all use what works for us. I used Delphi 7 for a long time. You cannot directly compare Android Studio and Delphi. In Android Studio, different parts of the whole toolset are more pluggable and exchangeable. In Delphi main problem is that IDE and core frameworks are firmly tied together. Because changes in platforms often require changes in frameworks - RTL and FMX, they cannot be patched unless changes are limited to implementation parts of the units. So you cannot use one version of the IDE with other version of the frameworks. Also debugger and compiler, are more tied with the rest of the toolset (even though more easily "patchable") than this is the case with Android Studio. Again, without going into specific issues which may be more or less problematic depending on your code, but if you want to develop and release applications for non Windows platforms, you need paid version with subscription. Whether this is an option or not, everyone needs to decide for themselves. Only you know what are the upsides and downsides in your case. Nobody else can make that decision for you. I wish Delphi would be better option and would have less issues, but that does not depend on me or any of us here.
  24. Dalija Prasnikar

    My Experience with D10.4

    That helps with future releases, it doesn't help for backporting changes to existing release. I am not saying that it makes it impossible, but it depends on the actual problems. There were similar fixes done in the past when it was possible to fix parts of the toolset. I also know for sure that sometimes some issues occur in late beta stages when Apple (usually there is more problems with iOS and macOS) reorganizes something that does not have direct impact on native toolset users, but it breaks 3rd party ones.
  25. Dalija Prasnikar

    My Experience with D10.4

    I agree with that. Now, without going into whether Embarcadero is currently giving stable updates or not at the end of the line... In reality stability is possible only on Windows and Linux platforms because those platforms are stable enough. And even on Windows there will be issues in releases when major Windows version changes or some larger feature is introduced. Not all such compatibility issues can be easily backported without making breaking interface changes. On Android, iOS and macOS situation is completely different. New major OS versions are released on yearly basis, breaking havoc in both backward compatibility and ability to run applications on new OS versions. Often even building applications with new tools required will be broken. Porting back is often mission impossible. Delphi IDE is highly integrated environment and you cannot just easily swap and update only some parts that are broken. For those fast moving platforms only viable option is moving forward, having active subscription and participating in beta testing when it is publicly available. Or using other toolsets if Delphi does not fit well for certain use cases.
×