Mike Torrettinni
Members-
Content Count
1509 -
Joined
-
Last visited
-
Days Won
3
Everything posted by Mike Torrettinni
-
Done, thanks.
-
The micro-optimization is not the goal, in this case. I use my version because it seems clearer, even at the expense of 1 extra line. Quick look at the code and I know how localVar is set.
-
Hi I'm refactoring a lot of my code and trying to consolidate, group a lot of utility methods. Now I got the point where I have, for example, 20 units using 5 different set of grouped methods in separate units, Utilities1.. Utilities5. Changing this is a big task, so I would like to ask if anybody has experience how different designs work better or worse in regarding Delphi 10.2, 10.3 error insight, compiling... if anybody has any comments, I would like any advice before I decide to any of the below designs: Example A: In all 20 units I use all 5 Utilities units. So, when I add another Utilities unit, I need to add it to all that use the methods in them. Unit1 uses Utilities1, Utilities2, Utilities3, Utilities4, Utilities5 Unit2 uses Utilities1, Utilities2, Utilities3, Utilities4, Utilities5 ... -> This example is annoying that I need to add every single Utilities1-5 unit to be used in all units. I have a feeling that error insight might have a problem with cross referencing all these methods... Example B: I make one Main Utilities unit that contains all Utilities methods, so 20 Units will use only 1 looong unit. MainUtilities contains all methods from Utilities1, Utilities2, Utilities3, Utilities4, Utilities5 - so becomes 1 looong unit Unit1 uses MainUtilities Unit2 uses MainUtilities ... -> This example is hard to maintain since all utilities methods are in 1 long unit, instead of grouped nicely in separate units. Example 😄 I make one Main Utilities that only have calls to other 5 Utilities, like a 'bridge' calls to methods from Utilities1-5 units MainUtilities uses Utilities1, Utilities2, Utilities3, Utilities4, Utilities5 MainUtilities methods call all methods from Utilities1-5, like this: procedure CalculateXFactors(params): integer: begin Result := Utilities1.CalculateXFactors(params); -> the real code is still in all Utilities1-5 units, grouped and managed end; Unit1 uses MainUtilities (and calls CalculateXFactors method) Unit2 uses MainUtilities (and calls other methods) ... -> This example seems good, because I have all methods grouped and maintained in separate units, and I avoid the need to use all Utilities1-5 in all units. But!, I need to replicate all method calls in this MainUtilities. So, this is simple example, I have a lot more than 20 units, so maintenance is becoming a hassle. Any comments on this? Thanks!
-
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Aha, makes sense. Thanks for the details. Right now I still compile/build full project every release. I don't use DCUs only, all pas files. I only release for 32 bit. -
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
@KeithLatham So you have that project 'my_UtilityGenerator.dpr' just for compiling common units? I have a folder structure something like this: \Projects\ \Projects\Commons\ \Projects\A\ \Projects\B\ \Projects\C\ and all projects include units like this: Utilities1in '..\Commons\Utilities1.pas' in each project.dpr and all units that use this unit. So, every time I compile any of the projects will compile with all used common units. What's the difference with your setup and mine, I mean I don't understand the advantage of putting all in separate project... units get compiled when changed and each project compiles with these common units used. -
For Sorting in VirtualTreeview I use CompareNodes method, which compares column values and sorts lines. In CompareNodes I use CompareValues method to compare Integer or String values - 2 overloaded methods: function CompareValues(const aValue1, aValue2: string): integer; overload; begin Result := AnsiCompareText(aValue1, aValue2); end; function CompareValues(const aValue1, aValue2: integer): integer; overload; begin Result := CompareValue(aValue1, aValue2); end; This works as expected. But now I need to compare Variant values which can hold Integer or String, so I setup this method: function CompareValues(const aValue1, aValue2: Variant): integer; overload; var vVarType1, vVarType2: integer; Begin Result := 0; vVarType1 := VarType(aValue1); vVarType2 := VarType(aValue2); // if ANY of the values is string, should compare as strings! if (vVarType1 = varString) or (vVarType1 = varUString) or (vVarType2 = varString) or (vVarType2 = varUString) then Result := CompareValues(String(VarAsType(aValue1, varString)), String(VarAsType(aValue2, varString))) // <- without String cast if thinks the values are Variant and calls itself! else if VarType(aValue1) = varInteger then Result := CompareValues(Integer(VarAsType(aValue1, varInteger)), Integer(VarAsType(aValue2, varInteger))); // <- have to cast as Integer! End; I set it up like this because when trying to sort values in column that have integers and strings, like: var vVariant1, vVariant2: Variant; begin vVariant1 := 5; vVariant2 := 'Yes'; if CompareValues(vVariant1, vVariant2) = 0 then Showmessage('Variant values are equal.') else Showmessage('Variant values are NOT equal.') it works. But, why does it: a. String 'Yes' recognizes as varUString and not varString, so I need to check Variant for both, just in case b. If I don't cast values when calling CompareValues to Integer or String, it calls as Variant types again and I get Stack overflow error, of course. So, I have the following questions: 1) Can I force VarType to return varString for varString or varUString? So I will only have 1 comparison, like VarType(aValue1) = varString, and not need to compare with 2 different string types... 2) There are far more number types of Variant (varByte, varWord, varShortInt, varInteger...)... how can I be sure that all numbers will always be recognized as varInteger? 3) Why do I need to cast VarAsType? String(VarAsType(aValue1, varString)) and Integer(VarAsType(aValue1, varInteger)), even though I specify I want varString or varInteger... Any advice is welcome! Thanks!
-
How to deal with different types of Variant?
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
Ok, makes sense. -
How to deal with different types of Variant?
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
Hm, so you have different fields for display and data? Like 'Customer Order no' or 'Sum of invoice amounts'... these are all numbers, do you have a display layer where you convert all to text and then show in VTT? How do you then deal with sorting of numeric columns? Do you keep information about which column data is numeric and sort accordingly (convert text to number and then sort)? Well, me asking you usually means I'm lacking some experience and understanding, but this is really interesting. -
How to deal with different types of Variant?
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
@Uwe Raabe Works good on my data. Very rarely I use Variants, so I didn't see these methods. Thanks! @David Heffernan Thank you, I see now that VarAsType returns a new Variant and not as the type you specify, odd. (I should read documentation more precise) As for the why I need this: I have a VirtualTreeview report that shows 20+ different categories of Customer data and 3-30 different columns, depending on the selected category. So, I need at least OnGetText, OnCompareNodes to show and sort data, then I have my own Copy, Search.. and Export data methods. So instead of assigning data to column in each method (like CellText := Name; or CellText := IntToStr(UnpaidInvoices)), I'm testing with only ONCE specifying what data is in each column of the view, so I have: function ReturnColumnData(Category, ColumnNo): Variant; and I can use it any of the methods that needs data for each column, And Export method can create Excel file and I need to export numbers as numbers to avoid that annoying warning indication in Excel that value is integer and not string. and the CompareValues are used in OnCompareNodes: Result := CompareValues(ReturnColumnData(Index1, Column), ReturnColumnData(Index2, Column)); So I need all in Variants as it seems that all methods that need data already know how to handle Variant types correctly for their purpose - show data, sort (compare) data, export to csv, excel.,, as I tested even sort is fast as before using Variants. -
Is this method good to delete last character?
Mike Torrettinni posted a topic in Algorithms, Data Structures and Class Design
I use a lot of export to csv, so of course a lot of methods that are creating csv text lines add extra comma at the end, so it needs to be deleted. In a lot of cases the loop prevents it to know exactly which string is last in the line, so I can't always avoid adding this extra comma. So, I setup 2 utility methods: procedure DeleteLastChar(var aText: string; const aLastChar: string); begin If aText <> '' then if aText[Length(aText)] = aLastChar then SetLength(aText, Length(aText) - 1); // old: aText := Copy(aText, 1, Length(aText) - 1) end; procedure DeleteLastComma(var aText: string); begin DeleteLastChar(aText, ','); end; And I call DeleteLastComma(TextLine) in each method, to just make sure I don't leave last comma in the text. The reports could be long, so this could be called 10000x of times... with small or large lines of text. Since I use var it will avoid copying string parameters and also SetLength just reduces the length of the string and doesn't do the actual string copying like Copy would do, right? -
Hi My application processes log files and generates reports. Now I have a customer that is not getting expected results and I can't reproduce it, since there are some specifics how application works based on the system and other connecting components, which I can't replicate. I'm preparing a version that will log methods that I think are involved in causing the issue/odd results. I'm adding LogLine calls in these methods and log: LogLine('start method processA'); ... LogLine('accessing log file = '+filename); .. LogLine('in For loop = +inttostr(i)); LogLine(details of loop); ... LogLine('close log file = '+filename); ... LogLine('end method processA'); The I can show, save the full log. So, basically I'm adding logging before/after any line I want to know what is going on. This is tedious work. And at the end I don't want to have all these LogLine lines in my methods, since the code becomes unreadable and a mess. So, I delete these lines when I don't need them anymore... and then add them to another places I need to debug. Anybody else is doing similar thing and found easier way to manage these kind of ad-hoc need for detailed logging? Any suggestions? Thanks!
-
Detailed logging (for debugging)
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
Thanks, will look into that. -
Is this method good to delete last character?
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
@Clément Thanks, another good suggestion how to do it without comma. -
Is this method good to delete last character?
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
That's' why I switched from using Copy to SetLength, because it is getting called many many times. But didn't notice any significant performance difference, but I hope there will be less memory fragmentation using SetLength. -
Is this method good to delete last character?
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Thanks for the feedback. The performance if good for me, I could not see any significant difference with TrimEnd. I will look into String.Join. -
None of the customer complained, yet, but I have a suspicion that my desktop applications will not be displayed correctly on 4K monitors... either the menu icons or 3rd party controls. I'm not interested in using 4K monitor as development monitor, but I saw a cheap 4K monitor ($180) that I would only use to see how my applications would look like. So, I don't need high end monitor, just the cheapest 4K option. Is that good enough, can I assume my applications will look good on all 4K monitors, if it looks good on my cheap one? Or are there other things to be careful of, with high dpi/high resolution monitors? I've seen many resources how to prepare applications for high dpi resolutions, so I'm not looking that content right now, just if it makes sense buying this monitor or not. I use Delphi 10.2.3. Thanks!
-
Do I need to test my applications on 4K monitor?
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
Great, thanks for advice! -
Do I need to test my applications on 4K monitor?
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
Hm, if I understand correctly, then the whole issues is about DPI and not the resolution? I can switch DPI on my 21" 1920x1200 and test different DPI settings... is that any different than on 4K monitor? -
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Thank you, that's how I looked at those namespaces. The documentation makes it seem a lot more complex and advanced level than regular names, without dots. -
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
@Ghostwalker Thank you, but I'm not understanding the explanation in the documentation: http://docwiki.embarcadero.com/RADStudio/Rio/en/Using_Namespaces_with_Delphi These namespace are just filenames with ',' in the name, right? No other specific change needed to go from uses UtilsFactoring (UtilsFactoring.pas) to uses Utils.Factoring (Utils.Factoring.pas)? -
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
It got to be annoying adding all newly-refactored units to all units that used to use one or two big units, and was thinking if maybe it's not supposed to be like that... but now I see it works good for me. Also, after using unit uses cleaner, it removed a lot of unused units and now adding new units seems better than before when it was already using a lot of units, and adding new uses just looked strangely complex for the error insight (and me). Now I'm also removing circular references, but this will take some time, it's manual work. So, to answer your question again: it was a just a thought in the process of refactoring the projects and wanted to check with you guys that I'm not going completely off the road. Thanks for the feedback! -
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Thanks! I already started working on Example A design. Example C would probably really be a mess, this will cause 2 places to maintain. -
Units design
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
No, these are not real names. The methods are grouped and named by the purpose, like calculations, math methods, then one unit is all about graphics, drawing, visual stuff, then another unit is exporting, reporting unit... not all units use all of these utilities units, but a portion of them do use all of them. So I want to design a good units design that will work best for maintenance and perhaps less confusing for error insight... to test each concept would mean so much extra work, that I would like to avoid it for now, especially if there is advice from experienced developers. -
User settings - split logic and UI
Mike Torrettinni replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
It's app for others. Now with new settings the export is extremely easy, while before it was quite a work. Usually they send screenshot of the issue, version number, so the export settings from app is not much work for them, and I think is better as it can export also details of app version, installation folder details... which usually is not stored in traditional ini file. -
Detailed logging (for debugging)
Mike Torrettinni replied to Mike Torrettinni's topic in General Help
@Markus Kinzlerthank you, good tool. Right now I'm more looking for advice on how to manage adding ('injecting') and removing these log lines, not the actual logging mechanism. I just added 50+ lines into a few methods I want to get detailed log. I will need to remove all these lines, once I'm done debugging because I don't want to leave them in the code. A tedious job. So, any advice how to simplify this, is most welcome!