-
Content Count
54 -
Joined
-
Last visited
-
Days Won
1
Everything posted by pmcgee
-
How can I force a record to be allocated on the heap ?
pmcgee replied to dormky's topic in Algorithms, Data Structures and Class Design
I just meant this type of situation where the C declarations are impenetrable, and Delphi is so much clearer: -
How can I force a record to be allocated on the heap ?
pmcgee replied to dormky's topic in Algorithms, Data Structures and Class Design
I have no problem with pointer to record ... I have been pretty scathing about C-style function declarations that are not broken down into more readable sub-types. We could separate ownership from access with something like : begin var o := TRecObject.Create; begin var p:PRec := @o.r; // use p for whatever end o.Free; end That pointer could be copied, passed to functions, whatever ... and simply pass out of scope. It wouldn't have ownership of the data object, and has no responsibility to release it. [edit - forgot] Or of course, you can have methods in the class to control and implement the access to the data object. I think this also highlights that the outer wrapper can be something other than manually managed. -
How can I force a record to be allocated on the heap ?
pmcgee replied to dormky's topic in Algorithms, Data Structures and Class Design
Yes. 100% I'm not arguing about it requiring management as is, ... or added structure to automate that. But of the code below, the new / dispose is (imo) ugly code that is unsuited to 2023, and to the long-term goal of regaining wider recognition of Delphi as a modern and relevant language. My standard rant / pedestal is that over the coming years we need to see Delphi improve it's language .. and it's practice ... to not be left behind by the general progress of other languages. Currently Delphi doesn't really qualify as a good teaching language any more - which I think is really sad. Without some more modern language facilities, it would be unfair to modern (say university level) students, and I'd like to see that change. begin var p:PRec := New(p); try p.x := 5; finally Dispose(p); end; end; begin var o := TRecObject.Create; try o.r.x := 5; finally o.Free; end; end -
How can I force a record to be allocated on the heap ?
pmcgee replied to dormky's topic in Algorithms, Data Structures and Class Design
Yes. I was arguing from a consistency and code-style view point. I don't agree. I don't think it is the generally accepted meaning of 'raw pointer'. Maybe you are thinking of 'void pointer' ? >> A raw pointer is a pointer whose lifetime isn't controlled by an encapsulating object, such as a smart pointer. A raw pointer can be assigned the address of another non-pointer variable, or it can be assigned a value of nullptr. Microsoft Learn - Raw Pointers -
Hi Ian. A nullable or option type basically adds a single possible "uninhabited" state to a return type of any kind. You can think of a function that can throw an exception in almost exactly the same way ... it has two possible states ... the normal return or the exception. I was operating on the assumption that the boolean you returned from your original function was indicating whether you successfully created a date range.
-
Not to be negatively critical, but can I suggest that there's a discussion to be had as to whether out parameters should be an out-dated usage style. @Ian Branch Can I take it that what you want returned from the function is EITHER a failure condition/error result OR a tuple of (startdate, enddate) ? If so, this is really a function with a single return type ... function GetWeekDates(const GivenDate: TDateTime; var SOWDay: string = 'SU') -> FAIL | (startdate, enddate) This could be known as a Nullable type, or Option type ... in Rust it is the Result type. When you return from this function, you either have (effectively) no result, or an inhabited tuple. There are quite a few published examples in Delphi of Nullable types .. .from Allen Bauer through to Spring4D. I'm not sure all of them capture the idea in the most functional way.
-
How can I force a record to be allocated on the heap ?
pmcgee replied to dormky's topic in Algorithms, Data Structures and Class Design
I would say, in probably every language, the recommendation should be to code in a way that is familiar and idiomatic for that language. If we wanted stack-allocation and hands-off memory safety, then we can use records .. even custom managed records. But if we want to use heap memory, then I think it only makes sense to do it in the same idiomatic language as all our other Delphi code. I don't think it's a wild opinion in 2023 that pretty much nobody outside of C code should be cooking up raw pointers. At least C++ has unique pointer and shared pointer. I guess we could cook up a smart pointer record to look after the heap-allocated memory ... that would still be avoiding raw pointers. In summary, I think the lesson from the C++ ecosystem is: pointers can be ok ... but {owning, raw} pointers are something to avoid. -
How can I force a record to be allocated on the heap ?
pmcgee replied to dormky's topic in Algorithms, Data Structures and Class Design
Wouldn't this be better expressed as a class containing a record? type TMyRec = record ... end; TMyRecC = class r : TMyRec; end; If C++ can declare "no raw pointers", then we certainly should avoid it in Delphi, right? This way you can maintain the usual convention with TMyRecC.Create and .Free, have constructor & destructor ... or use an interface ... or create your own smart pointer class ... -
Well. I learnt about something today. Recursive regexes.
-
The regex solution above has an issue with nested parentheses ... but could be repeated.
-
Result := MyStr.Split( ['<'] ) [0];
-
Detect stack full for recursive routine
pmcgee replied to david_navigator's topic in RTL and Delphi Object Pascal
@david_navigator no-one mentioned that a) if you can arrange for calculation by tail-calls, Delphi can eliminate runaway stack increases. b) there are techniques to linearise some recursive functions by decomposing the single function into multiple functions with extra state. c) it may be possible to memoise calculations that might be repeated. -
Playing with Windows Fibers by emulating Python Generators
pmcgee replied to darnocian's topic in I made this
Fibers under the magnifying glass Gor Nishanov (gorn@microsoft.com) - author? of c++ coroutines (Advocating coroutines over fibers.) Abstract "Fibers (sometimes called stackful coroutines or user mode cooperatively scheduled threads) and stackless coroutines (compiler synthesized state machines) represent two distinct programming facilities with vast performance and functionality differences. This paper highlights efficiency, scalability and usability problems of fibers and reaches the conclusion that they are not an appropriate solution for writing scalable concurrent software." https://open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1364r0.pdf -
Converting code source in R to Delphi
pmcgee replied to de la Mancha's topic in Algorithms, Data Structures and Class Design
@de la Mancha did you sort this out satisfactorily ? -
Micro optimization: IN vs OR vs CASE
pmcgee replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Neater and equally fast ... type Conts = (edit, button, check, form, frame, listbox, pgctrl, tabctrl, radiobtn, combobox); Ctest = set of Conts; const Strs : array[Conts] of string = ( 'Edit', 'Button', 'Check', 'Form', 'Frame', 'ListBox', 'PageControl', 'TabControl', 'RadioBtn', 'ComboBox' ); function IsIN(aID: Conts): boolean ; // inline; begin const test : Ctest = [button, form, listbox, tabctrl, combobox]; IsIn := aId in test; end; -
Micro optimization: IN vs OR vs CASE
pmcgee replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
As Stefan said ... without const ... procedure AssignControlsRec; begin xControlsRec.EditID := 1; xControlsRec.EditStr := 'Edit' ; xControlsRec.ButtonID := 2; xControlsRec.ButtonStr := 'Button' ; xControlsRec.CheckID := 4; xControlsRec.CheckStr := 'Check' ; xControlsRec.FormID := 8; xControlsRec.FormStr := 'Form' ; xControlsRec.FrameID := 16; xControlsRec.FrameStr := 'Frame' ; xControlsRec.ListBoxID := 32; xControlsRec.ListBoxStr := 'ListBox' ; xControlsRec.PageControlID := 64; xControlsRec.PageControlStr := 'PageControl'; xControlsRec.TabControlID := 128; xControlsRec.TabControlStr := 'TabControl' ; xControlsRec.RadioBtnID := 256; xControlsRec.RadioBtnStr := 'RadioBtn' ; xControlsRec.ComboBoxID := 512; xControlsRec.ComboBoxStr := 'ComboBox' ; end; function IsIN(aID: integer): boolean ; // inline; begin const test = xControlsRec.ButtonID + xControlsRec.FormID + xControlsRec.ListBoxID + xControlsRec.TabControlID + xControlsRec.ComboBoxID; IsIn := (test and aID) <> 0; end; -
Just wondering if anyone knows a ballpark figure for the "size" of (any of) the Delphi compilers?
-
Generic circular buffer library released
pmcgee replied to TurboMagic's topic in Algorithms, Data Structures and Class Design
To be fair, there's a Catch-22 in that idea. ie don't implement Generics until you have experience implementing Generics. (and the same would apply for trying to use a c++ like move feature) If someone publishes open source ... not part of a (reasonably big) commercial product like RAD ... and it is useful to maybe 80% of people, then sure it's valuable to point out edge cases - but it doesn't mean it should be discouraged. We need *more* Delphi out there. And more understanding of these elements that weren't even part of Delphi in say 2005. -
A bit of a challenge ... to produce Delphi code equaling the performance of C++'s next_permutation. Hopefully more readable in Delphi ... unit D_Next_perm_string; interface procedure swapCh(var a:Ansichar; var b:Ansichar); procedure reverse(var s:AnsiString; const a,x:word); function D_next_permutation(var s:AnsiString; const first, end_:word) : boolean; implementation //uses System.Diagnostics; procedure swapCh( var a : Ansichar; var b : Ansichar ); inline; var c : Ansichar; begin if a<>b then begin c := a; a := b; b := c; end; end; procedure reverse(var s:AnsiString; const a,x:word); inline; var i,j : word; //t : string; begin // x is one past the end of string if a = x-1 then exit; j := ( x-a ) shr 1; //trunc((x-a)/2); for i := 1 to j do swapCh( s[a-1+i] , s[x-i] ); end; function D_next_permutation(var s : AnsiString; const first, end_ : word) : boolean; inline; var i, ii, j : word; begin if first = end_ then begin result := false; exit; end; i := first + 1; if i = end_ then begin result := false; exit; end; i := end_ - 1; while true do begin ii := i; dec(i); if s[i] < s[ii] then begin j := end_; repeat dec(j); until s[i] < s[j]; swapCh( s[i] , s[j] ); reverse(s, ii, end_); result := true; exit; end; if i = first then begin reverse(s, first, end_); result := false; exit; end; end; end; end. Ballpark, we're looking at about 6s vs 2s for s = "abcdefghijkl" (12 bytes). Increases by about a factor of 10 per character beyond that. Note the c++ end() iterator is **one past the end of string**, so ... D_next_permutation(s,1,13)
-
std::next_permutation
pmcgee replied to pmcgee's topic in Algorithms, Data Structures and Class Design
Sure ... byte / ansichar is a simple case ... but you need to start with simple and solve it the best you can before tackling a bigger / harder problem. The testing is made faster by just using a shorter string. The increase in time is about x10 at the 12 mark ... so 11 is quicker, and 10 is almost immediate. -
std::next_permutation
pmcgee replied to pmcgee's topic in Algorithms, Data Structures and Class Design
I'm a little confused. First post *is* the Delphi code. I've been learning about C++ for a couple years, alongside 20 with Delphi, and the ideas from both are interesting to me. Naively, it seems like a Delphi version should be able to produce about the same performance. Teasing that out should be educational. -
std::next_permutation
pmcgee replied to pmcgee's topic in Algorithms, Data Structures and Class Design
The calling code from c++ : int main() { const std::string Test = "abcdefghijkl"; int i = 0; std::string v = Test; Timer timer; timer.start(); do { ++i; } while ( std::next_permutation( v.begin(), v.end() ) ); timer.stop(); std::cout << "C++ " << i << "\n"; std::cout << "Seconds: " << timer.elapsedSeconds() << std::endl; AnsiString s = Test.c_str(); int len = s.Length()+1; i = 0; Timer timer2; timer2.start(); do { ++i; } while ( D_next_permutation(s, 1, len ) ); timer2.stop(); std::cout << "Delphi " << i << "\n"; std::cout << "Seconds: " << timer2.elapsedSeconds() << std::endl; getchar(); return 0; -
std::next_permutation
pmcgee replied to pmcgee's topic in Algorithms, Data Structures and Class Design
@Kas Ob. posted this in the original thread : procedure reverse2(var s: AnsiString; const a, x: word); inline; var i, j: word; tmpChar: Byte; SBytes: pByte absolute s; begin if a = x - 1 then exit; j := (x - a) shr 1; // trunc((x-a)/2); for i := 1 to j do begin tmpChar := SBytes[a - 1 + i]; SBytes[a - 1 + i] := SBytes[x - i]; SBytes[x - i] := tmpChar; end; end; var st: AnsiString; i: Integer; D: Uint64; begin st := '1234567890ab'; Writeln(st); d := GetTickCount; for i := 1 to 479001600 do // 12! reverse(st, 1, 12); D := GetTickCount - D; Writeln(d); st := '1234567890ab'; Writeln(st); d := GetTickCount; for i := 1 to 479001600 do reverse2(st, 1, 12); D := GetTickCount - D; Writeln(d); Readln; end. With these results ... (32 bit? 64 bit? compile) 1234567890ab 14125 1234567890ab 4891 -
std::next_permutation
pmcgee replied to pmcgee's topic in Algorithms, Data Structures and Class Design
I didn't recall correctly. This was a significant speed up : procedure swapByte( a:Pbyte ; b:Pbyte ); inline; begin if a <> b then begin a^ := a^ + b^; b^ := a^ - b^; a^ := a^ - b^ ; end; end; swapByte( @s[a-1+i] , @s[x_-i] ); -
Micro optimization - effect of defined and not used local variables
pmcgee replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Maybe this is OT for this thread. I'll look up where to start a new one, and add a link.