Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 09/03/24 in all areas

  1. Rollo62

    Cannot create directories on iOS

    Even though this is within the app sandbox, certain directories might still have restrictions. For example, there could be permissions issues with creating directories at that specific path, especially if the app has some sort of managed or limited access to certain subdirectories. I use the TPath.GetDocumentsPath mostly, as it felt like the least problematic one. This path is designed to store user-generated content, documents, and files that should be backed up by iTunes and other backup solutions. Not sure if all characters are valid there, especially the underscore, I would say yes, but better check with simpler path (e.g., without underscore, parentheses or spaces).
  2. bwalsh

    3D render engine that works with Delphi

    You can also check Afterwarp (https://afterwarp.io) framework, which can produce quite realistic results. It works with Delphi.
  3. Version V15.0.37 of MMX Code Explorer introduces Structured Difference Viewer (former available as a separate product). It is registered as an External Difference Viewer in the IDE and can be used as an alternative way to compare source files.
  4. This file is associated with Delphi 12. If you're using project that was created in Delphi 12, but in compiling it in Delphi 11.3, you should follow this procedure.
  5. Yes, sorry. I missed the context you are replying to. We agree on the rest, and that was the point of my example. To show that custom managed are initialized/finalized even if not used and that there is extra initialization for Default call on implicit temporary variable. And compiler should be smart enough not to require any temporary variable at all. In any way thing is broken.
  6. There should not be any temp, nor assign call tbh. Check out this code: {$APPTYPE CONSOLE} uses SysUtils; type TMyRec = record x: Integer; class operator Initialize(out dest: TMyRec); class operator Finalize(var dest: TMyRec); class operator Assign(var left: TMyRec; const[ref] right: TMyRec); end; function MyDefault: TMyRec; begin Writeln('default - ', IntToHex(IntPtr(@Result))); Result.x := 0; end; class operator TMyRec.Initialize(out dest: TMyRec); begin Writeln('init - ', IntToHex(IntPtr(@dest))); end; class operator TMyRec.Finalize(var dest: TMyRec); begin Writeln('finalize - ', IntToHex(IntPtr(@dest))); end; class operator TMyRec.Assign(var left: TMyRec; const[ref] right: TMyRec); begin Writeln('assign'); end; procedure Main; var r: TMyRec; begin r := MyDefault; end; begin Main; end. Now we can argue why there is no assign call because we are assigning the result of the MyDefault function to r. But even though we have declared an assign operator it does what it always does with managed return type - passing it as hidden var parameter. I said this before and I say it again - CMR are broken as designed and personally I stay the heck away from them. I remember some years ago there was a question on SO about struct default ctors serving as default initializers and why C# does not have them and I don't remember if it was Jon or Eric explained that it would cause all kinds of trouble. Edit: Ah, here it is: https://stackoverflow.com/a/333840/587106 - eventually they added them in C# 10 but there is quite some extensive language spec - when I asked for the CMR spec it was all crickets. FWIW the Delphi implementation suffers from exactly the situation that Jon describes - allocate a dynamic array of some CMR and watch all the initializers run. Which is completely bonkers - C# 10 does not do any of that for array allocation - see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/parameterless-struct-constructors#array-allocation
  7. Because whole point of custom managed records is automatic initialization/finalization. So if you merely declare such record as local variable and you don't ever use it, its initialization and finalization routines will run. Running following code makes the issue more obvious. type TMyRec = record x: Integer; class operator Initialize(out rec: TMyRec); class operator Finalize(var Dest: TMyRec); class operator Assign(var left, right: TMyRec); end; class operator TMyRec.Initialize(out rec: TMyRec); begin Writeln('init'); end; class operator TMyRec.Finalize(var Dest: TMyRec); begin Writeln('finalize'); end; class operator TMyRec.Assign(var left, right: TMyRec); begin Writeln('assign'); end; procedure Main; var r: TMyRec; begin r := Default(TMyRec); end; begin Main; end. If assignment is commented out then result will be init finalize But running code as-is will result with: init init init assign finalize finalize
  8. Remy Lebeau

    Handling TCP send buffer overflow the right way

    That will complicate your protocol, but it can work if your protocol design allows for it. Such as by breaking up the file data into small chunks and send each chunk as an individual packet, and each packet carries a header specifying its type and size so the client can differentiate one packet from another. Make sure to NEVER overlap packets - don't send a new packet until a previous packet is done. That requires putting a lock on the socket if you send packets from multiple threads. Assuming your protocol doesn't require the client to explicitly ack everything it receives (most protocols don't), then the only way to detect that condition is when SendBuf() fails with a WSAEWOULDBLOCK error because the socket's outgoing buffer filled up due to the client not removing data from that buffer. Until that buffer fills up, any data "sent" is simply buffered locally by the socket without error and is picked up by the OS behind the scenes, so you won't know if the data was actually transmitted and received until an error occurs. You have to check the socket error code to find out why SendBuf() failed. Any error code other than WSAEWOULDBLOCK should be treated as a fatal error so close the connection. Otherwise, cache all current unsent data and future data somewhere until the OnClientWrite event tells you that the socket can accept data again, then you can try re-sending your cached data, removing any bytes that are accepted by SendBuf() without error, until either the cache is empty or a new error occurs. Yes, exactly. The logic is actually really simple: When sending a new packet, if the cache is not empty then append the entire packet to the end of the cache and don't attempt to send it yet. Otherwise, send as many bytes as possible for the packet. If SendBuf() fails, stop sending the packet. If the error is WSAEWOULDBLOCK then save the remaining unsent bytes to the end of the cache. In the OnClientWrite event, if the cache is not empty then send as many bytes as possible from the cache, removing any bytes that SendBuf() accepts. If SendBuf() fails for any reason (you don't need to look at the error code here), stop sending the cache and leave the remaining unsent bytes in it for a future event to re-try. I've posted examples of this many times in the past in various forums. I use the TCustomWinSocket.Data property to hold the cache (usually using a TMemoryStream), that way the cache follows the socket as its passed around. But yes, the cache could grow very large if you don't put a max cap or timeout on it. If you try to send a lot of data to an unresponsive client, or if data stays in the cache for a long period of time, then assume the client is dead (even if the OS hasn't reported it yet) and close the connection. On a similar note, the OnClientRead event can do similar caching. It should read all available bytes from the socket and put them into a separate cache, then you can remove and process only complete packets from the cache, waiting for future events to finish incomplete packets. Packets can span across multiple OnClientRead events per socket.
  9. emileverh

    What's the general opinion on v12?

    Agree! Hotfixes and/or faster updates would be nice. Good quality goes above new features!
×