-
Content Count
3565 -
Joined
-
Last visited
-
Days Won
120
Everything posted by Lars Fosdal
-
Cross-platform solution to forcefully end a thread
Lars Fosdal replied to aehimself's topic in Cross-platform
Threads are dangerous in the same way that travel by car is dangerous. If you prepare well, know how to drive, follow the rules and take the appropriate precautions, then you will be in control and nothing bad happens. As always, there may be external factors that can cause problems - but that goes for code that is not executed in threads as well. That said - in this case, it sounds like the third party engine has serious flaws. -
Cross-platform solution to forcefully end a thread
Lars Fosdal replied to aehimself's topic in Cross-platform
I am not sufficiently versed in Android and iOS to suggest a workaround there, but for Linux, you could write a thin wrapper executable and fork to run it. If it doesn't complete within a reasonable time, you can kill that process. On Android and iOS I guess forking would require permissions of some sort? -
Cross-platform solution to forcefully end a thread
Lars Fosdal replied to aehimself's topic in Cross-platform
DLL = Windows only. -
Cross-platform solution to forcefully end a thread
Lars Fosdal replied to aehimself's topic in Cross-platform
There doesn't seem to be a similar cross platform function. What is wrong with Thread.Terminate? You can override SetTerminate to set your own variables on termination. F.x. if you don't want to have a Terminated check in a tight loop, you could nil a variable you reference in the loop to raise an exception to get out of that loop. Ugly, yes - but not much uglier than TerminateThread. -
Firstly, I highly recommend using the new MSSQLODBC Driver 17, or the SQLNCLI 11 diver over the default one. https://docs.microsoft.com/en-us/sql/connect/odbc/windows/microsoft-odbc-driver-for-sql-server-on-windows?redirectedfrom=MSDN&view=sql-server-ver15 What generation is the SQL Server? 2008? 2012? 2016? 2017? 2019? What did you do to set up the SSL support on the client side? This article outlines the steps needed. https://techdocs.broadcom.com/content/broadcom/techdocs/us/en/ca-enterprise-software/layer7-identity-and-access-management/single-sign-on/12-52-02/configuring/policy-server-configuration/user-directories/configure-a-microsoft-sql-server-database-connection-over-ssl.html
-
I guess you have read http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_Beacons#Scanning_for_Beacons John? I've not used beacons myself, but I suspect we will evaluate them sometime next year, so I am curious to learn. What do you do with the beacon once you detect it? What happens if you remove it from the Beacon manager? What happens if your stop/start monitoring?
-
is there any "currency to words" routine for Delphi?
Lars Fosdal replied to Attila Kovacs's topic in Algorithms, Data Structures and Class Design
You could also say that the half indicates a half twenty. half tres = 2.5 x 20 = 50 half firs = 3.5 x 20 = 70 half fems = 4.5 x 20 = 90 It is logical, but to a foreigner it does feel a bit jumbled - and why isn't there two twenty somethings like, 1.5 x 20 = 30, and 2 x 20 = 40? -
is there any "currency to words" routine for Delphi?
Lars Fosdal replied to Attila Kovacs's topic in Algorithms, Data Structures and Class Design
Just to illustrate the complexity of this in a different language, look at the danes that work with n times 20s and half-20s when above 50 and below 100, and they are not entirely consistent about it either. I am not a native dane, so there may be some mistakes - but this is my understanding of their classic spoken numbering. førr = forty = 40 fem og førr = five and forty = 45 half tres / femti = fifty = 3 x 20 - 10 = 50 seks og halv tres = six and fifty = 6 + (3 x 20 - 10) = 56 seksti / tres = sixty = 3 x 20 = 60 fem og seksti = five and sixty = 65 half firs / søtti = seventy = 4 x 20 - 10 = 70 åtti / firs = eighty = 4 x 20 = 80 half fems / nitti = ninety = 5 x 20 - 10 = 90 syv og halv fems = seven and ninety = 97 hundrede / fems = hundred = 5 x 20 = 100 277 = to hundrede og syv og halv firs = two hundred and seven and seventy = 200 + 7 + 70 Modern Danish does allow saying 55 = "femti fem" instead of "fem og halv tres" - i.e. more like the English spoken "fifty five", but the classic form is widely used. Modern 277 would be "to hundrede og søtti syv", like the English "two hundred and seventy seven". -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Come to think of it, it could have been nice to be able to enforce the helper implementations with an interface. or even better, be able to reuse an interface implementation for handling attribute naming. Also, this: /SendsLetterToCompilerSanta -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
There is no definitive answer to that as YMMV. We do literally have 100+ types of enums, and we implemented record helpers for each one, simply to ensure that all enums had the same capabilities. AsString, FromString, Name, and in some cases more texts, or biz.logic associated with the enum. We actually did the last push for this model this autumn, to get rid of the last couple of dozens of standalone TypeNameToStr functions. We also introduced app wide translations (Norwegian, Swedish, English) using Sisulizer. It turned out that using attributes for naming was a bit of a challenge, since you can't use a resourcestring as an argument to an attribute - go figure. resourcestring sName = 'Name'; type AttrNameAttribute = class(TCustomAttribute) constructor Create(const aName: String); end; type TSomeType = class private FaName: string; procedure SetaName(const Value: string); public [AttrName(sName)] // <-- [dcc32 Error] : E2026 Constant expression expected property aName: string read FaName write SetaName; end; We ended up setting names explicitly in code instead. -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
How do you associate the string attribute(s) with each enum value? -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
InitHelper initializes the class var arrays for Caption and DevTitle (for the second example using class vars). You only need to call this once for the application, so you could do f.x. it in the unit init section. For your second question, see the implementation of TProjectTypeHelper.Caption. Why the extra class function CaptionOf? A habit of mine, due to the rule of only one class helper for a type in scope at a time. -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
@David Heffernan - Do you scan the rtti attributes once in a class init method/first use, or do you fetch the attributes at each use? -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
For the latter example: begin TProjectType.InitHelper; var prj: TProjectType := ptExternalDev; Writeln(prj.Caption); end; will output External Dev Project -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Both. See updated post. Let me correct myself... With a record helper, the methods of the helper become part of the helped type. begin TProjectType.InitClass; // only for the second example var prj: TProjectType = ptMain; writeln(prj.Caption); writeln(ptMain.Caption) writeln(TProjectType.CaptionOf(ptMain)) end; all output the same string. Populating a combobox can be done like this for var prj := Low(TProjectType) to High(TProjectType) do ComboBox1.AddItem(prj.Caption, Pointer(Ord(prj))); and later, if you like such ugly hacks 😉 if ComboBox1.ItemIndex >= 0 then begin var selectedprj := TProjectType(Integer(ComboBox1.Items[ComboBox1.ItemIndex])); end; -
How to manage defined list values
Lars Fosdal replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
I prefer record helpers, combined with the const arrays. I really wish I could make generic record helpers for arrays, though 😕 type TProjectType = (ptMain, ptSub, ptExternalDev, ptInternalDev); TProjectTypeHelper = record helper for TProjectType private const cProjDefValues: array[TProjectType] of string = ('main_proj', 'sub_proj', 'dev_ext', 'dev_int'); cProjCaptions: array[TProjectType] of string = ('Main Project', 'Sub Project', 'External Dev Project', 'Internal Dev Project'); public class function DefValueOf(const aProjectType: TProjectType): string; static; class function CaptionOf(const aProjectType: TProjectType): string; static; function DefValue: string; function Caption: string; end; implementation { TProjectTypeHelper } function TProjectTypeHelper.Caption: string; begin Result := CaptionOf(Self); end; class function TProjectTypeHelper.CaptionOf(const aProjectType: TProjectType): string; begin Result := cProjCaptions[aProjectType]; end; function TProjectTypeHelper.DefValue: string; begin Result := DefValueOf(Self); end; class function TProjectTypeHelper.DefValueOf(const aProjectType: TProjectType): string; begin Result := cProjDefValues[aProjectType]; end; The class functions could also be case statements - but the array[type] ensures you have values for all. The weak spot here is if you insert a value in the constant list, or reorder values - without doing the same for the strings. Or you can use class vars - which would make it easier to load up different languages at runtime, if so needed. type TProjectType = (ptMain, ptSub, ptExternalDev, ptInternalDev); TProjectTypeHelper = record helper for TProjectType private class var cProjDefValues: array[TProjectType] of string; cProjCaptions: array[TProjectType] of string; public class procedure InitHelper; static; class function DefValueOf(const aProjectType: TProjectType): string; static; class function CaptionOf(const aProjectType: TProjectType): string; static; function DefValue: string; function Caption: string; end; implementation { TProjectTypeHelper } function TProjectTypeHelper.Caption: string; begin Result := CaptionOf(Self); end; class function TProjectTypeHelper.CaptionOf(const aProjectType: TProjectType): string; begin Result := cProjCaptions[aProjectType]; end; function TProjectTypeHelper.DefValue: string; begin Result := DefValueOf(Self); end; class function TProjectTypeHelper.DefValueOf(const aProjectType: TProjectType): string; begin Result := cProjDefValues[aProjectType]; end; class procedure TProjectTypeHelper.InitHelper; begin cProjDefValues[ptMain] := 'main_proj'; cProjCaptions [ptMain] := 'Main Project'; cProjDefValues[ptSub] := 'sub_proj'; cProjCaptions [ptSub] := 'Sub Project'; cProjDefValues[ptExternalDev] := 'dev_ext'; cProjCaptions [ptExternalDev] := 'External Dev Project'; cProjDefValues[ptInternalDev] := 'dev_int'; cProjCaptions [ptInternalDev] := 'Internal Dev Project'; end; -
@Markus Kinzler - What I meant is that in UDP, there is no mechanism the ensures that the packet reaches its destination, nor is there a mechanism that allows the recipient to discover that a packet has gone missing. As you say, all of that would have to be implemented as a protocol on top of UDP. When it comes to the content, I actually don't know if UDP ensures that the content of a delivered package is guaranteed to be intact? I would tend to assume so, but I haven't actually checked.
-
UDP is per definition not reliable.
-
We use a couple of C# assemblies - mostly for being able to break down stuff like bar code scan strings into AIs, or other forms of string analysis, validation (checksums) or manipulation. It is a bit cumbersome, tbh, but it is a lot more efficient than trying to do the same stuff in T-SQL.
-
Are you using the appropriate code page for your ansistring? Can you give an example of what goes wrong, character-wise?
-
ah no - dang - I missed that it was Android. Moved the post again.
-
No worries. I moved it to VCL. What if you try: SendMessage(Memo1.Handle, EM_SCROLLCARET, 0, 0); instead of the goto/scrollto ?
-
Is there any difference in behaviour if you SetFocus GoToTextBegin ScrollToTop instead? Can you reproduce the problem in a minimal app?
-
Pointers... loops...
Lars Fosdal replied to Sonjli's topic in Algorithms, Data Structures and Class Design
Edit: I rewrote this on my blog: https://larsfosdal.blog/2019/10/31/demystifying-pointers-in-delphi/ Having a general understanding of how memory and addressing works, helps a bit for pointers. Learning assembler gives you that knowledge, but there are simpler ways to think about it. A pointer is a memory location that contains the address to the actual data you want Think of a street with houses. Make a list of the houses and their addresses. This is a list of pointers. Each pointer leads to the actual house it refers to. As you move through the list and follow each pointer, you can visit each house. Street of houses (i.e. your blocks of data, 1Kb each) 10k 11k 12k 13k 14K +------------+------------+------------+------------+------------+ |Apple |Pear | |Banana |Orange | | H1 | H2 | | H3 | H4 | | | | | | | +------------+------------+------------+------------+------------+ Your list of addresses (aka 4 byte pointers) is stored at memory address 100k var ptrlist: array[0..3] of pointer; assuming the list has been initialized with the correct addresses ptrlist[0] 100k contains 10k ptrlist[1] 100k+4 contains 11k ptrlist[2] 100k+8 contains 13k ptrlist[3] 100k+12 contains 14k for var ix := 0 to Length(ptrlist) - 1 do begin here, ptrlist[ix] = 10k,11k,13k,14k, and ptrlist[ix]^ = whatever value that is stored in the respective house the pointer addresses f.x. ptrlist[1] contains 11k (and that value is stored at 100k+4, and ptrlist[1]^ points to 'Pear', i.e. whatever is stored from address 11k Why the empty house? To exemplify that your list of pointers may be a consecutive array or linked list, but the data each pointer points to does not necessarily need to be consecutive. Now, if you address ptrlist[4] - you are out of bounds on the pointer list, and if you are so "lucky" that the address @ptrlist[4] (which is 100k+16) is not inaccesible, then ptrlist[4]^ will point you to whatever random value that pointer contains,, and most likely give you an access violation, or for sure - point you to data you are not meant to visit. -
I have a class type that I call a GridSet, and I use RTTI to match object prop names with DB field names (and attributes for overrides) for multi-row datasets, I build a list of property setters that are ordered the way the fields exist in the DB, for all the fields that exist both in the data set and the object and then I simply loop that list row by row when loading the dataset. I also have an attribute driven variation of filling a GridSet that builds the field definitions dynamically from the DataSet - so that I basically can fill the GridSet from any query, and display it in my GridView class, which attaches to a TAdvStringGrid and sets up the grid purely in code with the various event handlers for sorting and filtering. Hence I can show the result of any query in a grid with one line of code - no visual design required. The benefit of declaring the fields in a gridset, is that I can specify nicer titles, max widths, hints, etc.