diff --git a/uppsrc/Core/srcdoc.tpp/Tutorial_en-us.tpp b/uppsrc/Core/srcdoc.tpp/Tutorial_en-us.tpp index 62e7055ba..ce043bea2 100644 --- a/uppsrc/Core/srcdoc.tpp/Tutorial_en-us.tpp +++ b/uppsrc/Core/srcdoc.tpp/Tutorial_en-us.tpp @@ -26,56 +26,62 @@ topic "U++ Core Tutorial"; [s2;%% U`+`+ Core Tutorial&] [s0;3 &] [s0;3 &] -[s0; [^`#Chapter`_1^ 1. Basics]&] -[s0; ___[^`#Section`_1`_1^ 1.1 Logging]&] -[s0; ___[^`#Section`_1`_2^ 1.2 String]&] -[s0; ___[^`#Section`_1`_3^ 1.3 StringBuffer]&] -[s0; ___[^`#Section`_1`_4^ 1.4 WString]&] -[s0; ___[^`#Section`_1`_5^ 1.5 Date and Time]&] -[s0; ___[^`#Section`_1`_6^ 1.6 AsString, ToString and operator<<]&] -[s0; ___[^`#Section`_1`_7^ 1.7 CombineHash]&] -[s0; ___[^`#Section`_1`_8^ 1.8 SgnCompare and CombineCompare]&] +[s0; [^Chapter`_1^ 1. Basics]&] +[s0; ___[^Section`_1`_1^ 1.1 Logging]&] +[s0; ___[^Section`_1`_2^ 1.2 String]&] +[s0; ___[^Section`_1`_3^ 1.3 StringBuffer]&] +[s0; ___[^Section`_1`_4^ 1.4 WString]&] +[s0; ___[^Section`_1`_5^ 1.5 Date and Time]&] +[s0; ___[^Section`_1`_6^ 1.6 AsString, ToString and operator<<]&] +[s0; ___[^Section`_1`_7^ 1.7 CombineHash]&] +[s0; ___[^Section`_1`_8^ 1.8 SgnCompare and CombineCompare]&] [s0; &] -[s0; [^`#Chapter`_2^ 2. Array containers]&] -[s0; ___[^`#Section`_2`_1^ 2.1 Vector basics]&] -[s0; ___[^`#Section`_2`_2^ 2.2 Vector operations]&] -[s0; ___[^`#Section`_2`_3^ 2.3 Transfer issues]&] -[s0; ___[^`#Section`_2`_4^ 2.4 Client types in U`+`+ containers]&] -[s0; ___[^`#Section`_2`_5^ 2.5 Array flavor]&] -[s0; ___[^`#Section`_2`_6^ 2.6 Polymorphic Array]&] -[s0; ___[^`#Section`_2`_7^ 2.7 Bidirectional containers]&] -[s0; ___[^`#Section`_2`_8^ 2.8 Index]&] -[s0; ___[^`#Section`_2`_9^ 2.9 Index and client types]&] -[s0; ___[^`#Section`_2`_10^ 2.10 VectorMap, ArrayMap]&] -[s0; ___[^`#Section`_2`_11^ 2.11 One]&] -[s0; ___[^`#Section`_2`_12^ 2.12 Any]&] -[s0; ___[^`#Section`_2`_13^ 2.13 InVector, InArray]&] -[s0; ___[^`#Section`_2`_14^ 2.14 SortedIndex, SortedVectorMap, SortedArrayMap]&] -[s0; ___[^`#Section`_2`_15^ 2.15 Tuples]&] +[s0; [^Chapter`_2^ 2. Streams]&] +[s0; ___[^Section`_2`_1^ 2.1 Streams basics]&] +[s0; ___[^Section`_2`_2^ 2.2 Special streams]&] +[s0; ___[^Section`_2`_3^ 2.3 Binary serialization]&] [s0; &] -[s0; [^`#Chapter`_3^ 3. Ranges and algoritims]&] -[s0; ___[^`#Section`_3`_1^ 3.1 Range]&] -[s0; ___[^`#Section`_3`_2^ 3.2 Algorithms]&] -[s0; ___[^`#Section`_3`_3^ 3.3 Sorting]&] +[s0; [^Chapter`_3^ 3. Array containers]&] +[s0; ___[^Section`_3`_1^ 3.1 Vector basics]&] +[s0; ___[^Section`_3`_2^ 3.2 Vector operations]&] +[s0; ___[^Section`_3`_3^ 3.3 Transfer issues]&] +[s0; ___[^Section`_3`_4^ 3.4 Client types in U`+`+ containers]&] +[s0; ___[^Section`_3`_5^ 3.5 Array flavor]&] +[s0; ___[^Section`_3`_6^ 3.6 Polymorphic Array]&] +[s0; ___[^Section`_3`_7^ 3.7 Bidirectional containers]&] +[s0; ___[^Section`_3`_8^ 3.8 Index]&] +[s0; ___[^Section`_3`_9^ 3.9 Index and client types]&] +[s0; ___[^Section`_3`_10^ 3.10 VectorMap, ArrayMap]&] +[s0; ___[^Section`_3`_11^ 3.11 One]&] +[s0; ___[^Section`_3`_12^ 3.12 Any]&] +[s0; ___[^Section`_3`_13^ 3.13 InVector, InArray]&] +[s0; ___[^Section`_3`_14^ 3.14 SortedIndex, SortedVectorMap, SortedArrayMap]&] +[s0; ___[^Section`_3`_15^ 3.15 Tuples]&] [s0; &] -[s0; [^`#Chapter`_4^ 4. Value]&] -[s0; ___[^`#Section`_4`_1^ 4.1 Value]&] -[s0; ___[^`#Section`_4`_2^ 4.2 Null]&] -[s0; ___[^`#Section`_4`_3^ 4.3 Client types and Value, RawValue, RichValue]&] -[s0; ___[^`#Section`_4`_4^ 4.4 ValueArray and ValueMap]&] +[s0; [^Chapter`_4^ 4. Ranges and algorithms]&] +[s0; ___[^Section`_4`_1^ 4.1 Range]&] +[s0; ___[^Section`_4`_2^ 4.2 Algorithms]&] +[s0; ___[^Section`_4`_3^ 4.3 Sorting]&] [s0; &] -[s0; [^`#Chapter`_5^ 5. Function and lambdas]&] -[s0; ___[^`#Section`_5`_1^ 5.1 Function]&] -[s0; ___[^`#Section`_5`_2^ 5.2 Capturing U`+`+ containers into lambdas]&] +[s0; [^Chapter`_5^ 5. Value]&] +[s0; ___[^Section`_5`_1^ 5.1 Value]&] +[s0; ___[^Section`_5`_2^ 5.2 Null]&] +[s0; ___[^Section`_5`_3^ 5.3 Client types and Value, RawValue, RichValue]&] +[s0; ___[^Section`_5`_4^ 5.4 ValueArray and ValueMap]&] [s0; &] -[s0; [^`#Chapter`_6^ 6. Multithreading]&] -[s0; ___[^`#Section`_6`_1^ 6.1 Thread]&] -[s0; ___[^`#Section`_6`_2^ 6.2 Mutex]&] -[s0; ___[^`#Section`_6`_3^ 6.3 ConditionVariable]&] -[s0; ___[^`#Section`_6`_4^ 6.4 CoWork]&] -[s0; ___[^`#Section`_6`_5^ 6.5 CoPartition]&] -[s0; ___[^`#Section`_6`_6^ 6.6 Parallel algorithms]&] -[s0;^`#Section`_6`_6^ &] +[s0; [^Chapter`_6^ 6. Function and lambdas]&] +[s0; ___[^Section`_6`_1^ 6.1 Function]&] +[s0; ___[^Section`_6`_2^ 6.2 Capturing U`+`+ containers into lambdas]&] +[s0; &] +[s0; [^Chapter`_7^ 7. Multithreading]&] +[s0; ___[^Section`_7`_1^ 7.1 Thread]&] +[s0; ___[^Section`_7`_2^ 7.2 Mutex]&] +[s0; ___[^Section`_7`_3^ 7.3 ConditionVariable]&] +[s0; ___[^Section`_7`_4^ 7.4 CoWork]&] +[s0; ___[^Section`_7`_5^ 7.5 AsyncWork]&] +[s0; ___[^Section`_7`_6^ 7.6 CoPartition]&] +[s0; ___[^Section`_7`_7^ 7.7 Parallel algorithms]&] +[s0;^Section`_7`_7^ &] [s22;:Chapter`_1: 1. Basics&] [s3;:Section`_1`_1: 1.1 Logging&] [s5; Logging is a useful technique to trace the flow of the code @@ -118,9 +124,9 @@ variable name and value:&] [s7; DUMPHEX(h);&] [s0; &] [s17; x `= 0x7b&] -[s17; h `= Memory at 0x0x7ffcb01abe30, size 0x3 `= 3&] -[s17; `+0 0x00007FFCB01ABE30 66 6F 6F - foo &] +[s17; h `= Memory at 0x0249FA18, size 0x3 `= 3&] +[s17; `+0 0x0249FA18 66 6F 6F + foo &] [s0; &] [s5; To log the value of a container (or generic Range), you can either use normal [*C@5 LOG] / [*C@5 DUMP]:&] @@ -363,9 +369,9 @@ data, including zeroes:&] [s7; &] [s7; DUMPHEX(a);&] [s0; &] -[s17; a `= Memory at 0x0x7ffcb01abd20, size 0x5 `= 5&] -[s17; `+0 0x00007FFCB01ABD20 31 32 33 34 00 - 1234. &] +[s17; a `= Memory at 0x0249FA78, size 0x5 `= 5&] +[s17; `+0 0x0249FA78 31 32 33 34 00 + 1234. &] [s0; &] [s3;H4;:Section`_1`_3: 1.3 StringBuffer&] [s5; If you need a direct write access to [*C@5 String]`'s C`-string @@ -467,7 +473,7 @@ concrete types.&] [s7; &] [s7; DUMP(date);&] [s0; &] -[s17; date `= 07/19/2017&] +[s17; date `= 12/08/2017&] [s0; &] [s5; All data members of [*C@5 Date] structure are public:&] [s0; &] @@ -478,8 +484,8 @@ would log&] [s7; DUMP((int)date.day); // as characters&] [s0; &] [s17; (int)date.year `= 2017&] -[s17; (int)date.month `= 7&] -[s17; (int)date.day `= 19&] +[s17; (int)date.month `= 12&] +[s17; (int)date.day `= 8&] [s0; &] [s5; Dates can be compared:&] [s0; &] @@ -494,15 +500,15 @@ ing goes to the next/previous day:&] [s7; DUMP(`-`-date);&] [s7; DUMP(`+`+date);&] [s0; &] -[s17; date `+ 1 `= 07/20/2017&] -[s17; `-`-date `= 07/18/2017&] -[s17; `+`+date `= 07/19/2017&] +[s17; date `+ 1 `= 12/09/2017&] +[s17; `-`-date `= 12/07/2017&] +[s17; `+`+date `= 12/08/2017&] [s0; &] [s5; Subtraction of dates yields a number of days between them:&] [s0; &] [s7; DUMP(date `- Date(2000, 1, 1));&] [s0; &] -[s17; date `- Date(2000, 1, 1) `= 6409&] +[s17; date `- Date(2000, 1, 1) `= 6551&] [s0; &] [s5; There are several [*C@5 Date] and calendar related functions:&] [s0; &] @@ -528,7 +534,7 @@ ing goes to the next/previous day:&] [s0; &] [s7; DUMP(DayOfWeek(date)); // 0 is Sunday&] [s0; &] -[s17; DayOfWeek(date) `= 3&] +[s17; DayOfWeek(date) `= 5&] [s0; &] [s0; &] [s7; DUMP(LastDayOfMonth(date));&] @@ -538,11 +544,11 @@ ing goes to the next/previous day:&] [s7; DUMP(DayOfYear(date)); // number of days since Jan`-1 `+ 1&] [s7; DUMP(DayOfYear(Date(2016, 1, 1)));&] [s0; &] -[s17; LastDayOfMonth(date) `= 07/31/2017&] -[s17; FirstDayOfMonth(date) `= 07/01/2017&] +[s17; LastDayOfMonth(date) `= 12/31/2017&] +[s17; FirstDayOfMonth(date) `= 12/01/2017&] [s17; LastDayOfYear(date) `= 12/31/2017&] [s17; FirstDayOfYear(date) `= 01/01/2017&] -[s17; DayOfYear(date) `= 200&] +[s17; DayOfYear(date) `= 342&] [s17; DayOfYear(Date(2016, 1, 1)) `= 1&] [s0; &] [s0; &] @@ -553,10 +559,10 @@ between two dates&] partial months`' between two dates&] [s7; DUMP(AddYears(date, 2));&] [s0; &] -[s17; AddMonths(date, 20) `= 03/19/2019&] +[s17; AddMonths(date, 20) `= 08/08/2019&] [s17; GetMonths(date, date `+ 100) `= 3&] [s17; GetMonthsP(date, date `+ 100) `= 4&] -[s17; AddYears(date, 2) `= 07/19/2019&] +[s17; AddYears(date, 2) `= 12/08/2019&] [s0; &] [s0; &] [s7; DUMP(GetWeekDate(2015, 1));&] @@ -596,11 +602,11 @@ time:&] [s7; DUMP((int)time.minute);&] [s7; DUMP((int)time.second);&] [s0; &] -[s17; time `= 07/19/2017 22:55:10&] -[s17; (Date)time `= 07/19/2017&] -[s17; (int)time.hour `= 22&] -[s17; (int)time.minute `= 55&] -[s17; (int)time.second `= 10&] +[s17; time `= 12/08/2017 09:02:28&] +[s17; (Date)time `= 12/08/2017&] +[s17; (int)time.hour `= 9&] +[s17; (int)time.minute `= 2&] +[s17; (int)time.second `= 28&] [s0; &] [s5; Times can be compared:&] [s0; &] @@ -627,10 +633,10 @@ but numbers represent seconds (using [*C@5 int64] datatype):&] is in days&] [s7; DUMP(time `- ToTime(date)); // Time `- Time is in seconds&] [s0; &] -[s17; time `+ 1 `= 07/19/2017 22:55:11&] -[s17; time `+ 24 `* 3600 `= 07/20/2017 22:55:10&] +[s17; time `+ 1 `= 12/08/2017 09:02:29&] +[s17; time `+ 24 `* 3600 `= 12/09/2017 09:02:28&] [s17; time `- date `= 0&] -[s17; time `- ToTime(date) `= 82510&] +[s17; time `- ToTime(date) `= 32548&] [s0; &] [s5; [*C@5 Time] defines era limits too:&] [s0; &] @@ -685,9 +691,9 @@ items predefined by U`+`+:&] [s7; DUMP(LoadFile(ConfigFile(`"test.txt`")));&] [s7; DUMP(sout);&] [s0; &] -[s17; LoadFile(ConfigFile(`"test.txt`")) `= 1.23 07/19/2017 07/19/2017 -22:55:10&] -[s17; sout `= 1.23 07/19/2017 07/19/2017 22:55:10&] +[s17; LoadFile(ConfigFile(`"test.txt`")) `= 1.23 12/08/2017 12/08/2017 +09:02:28&] +[s17; sout `= 1.23 12/08/2017 12/08/2017 09:02:28&] [s0; &] [s5; Getting client types involved into this schema is not too difficult, all you need to do is to add [*C@5 ToString] method:&] @@ -904,8 +910,367 @@ be further simplified using [*C@5 CombineCompare] helper class:&] [s17; o !`= p `= true&] [s17; SgnCompare(o, p) `= `-1&] [s0; &] -[s22;:Chapter`_2: 2. Array containers&] -[s3;:Section`_2`_1: 2.1 [C@5 Vector] basics&] +[s22;:Chapter`_2: 2. Streams&] +[s3;:Section`_2`_1: 2.1 Streams basics&] +[s5; U`+`+ stream working with files is [*C@5 FileStream]. It has 3 +derived classes, [*C@5 FileIn], [*C@5 FileOut] and [*C@5 FileAppend], +for the most common uses.&] +[s0; &] +[s7; FileIn in(GetDataFile(`"test.txt`"));&] +[s7; if(!in) `{&] +[s7; -|LOG(`"Failed to open the file`");&] +[s7; -|return;&] +[s7; `}&] +[s0; &] +[s5; The most basic operations of streams are [*C@5 Put] and [*C@5 Get]. +[*C@5 Get] works in the same ways as good old C getc `- it returns +negative number on eof or error:&] +[s0; &] +[s7; String h;&] +[s7; int c;&] +[s7; while((c `= in.Get()) >`= 0)&] +[s7; -|h.Cat(c);&] +[s7; DUMP(h);&] +[s0; &] +[s17; h `= Lorem ipsum dolor sit amet, consectetur adipiscing elit,&] +[s17; sed do eiusmod tempor incididunt ut labore et dolore magna&] +[s17; aliqua. Ut enim ad minim veniam, quis nostrud exercitation&] +[s17; ullamco laboris nisi ut aliquip ex ea commodo consequat.&] +[s17; Duis aute irure dolor in reprehenderit in voluptate velit&] +[s17; esse cillum dolore eu fugiat nulla pariatur. Excepteur&] +[s17; sint occaecat cupidatat non proident, sunt in culpa qui&] +[s17; officia deserunt mollit anim id est laborum.&] +[s0; &] +[s5; U`+`+ streams provide no formatting capabilities (that is deferred +to text utilities), but they have some unique features. U`+`+ +does not distinguish between `'text`' and `'binary`' mode streams, +methods are well suited to work with both in common mode.&] +[s5; [*C@5 GetLine] returns [*C@5 String] of single line read (lines +separator being `'`\n`', `'`\r`' is ignored):&] +[s0; &] +[s7; in.Seek(0);&] +[s7; while(!in.IsEof())&] +[s7; -|DUMP(in.GetLine());&] +[s0; &] +[s17; in.GetLine() `= Lorem ipsum dolor sit amet, consectetur adipiscing +elit,&] +[s17; in.GetLine() `= sed do eiusmod tempor incididunt ut labore et +dolore magna&] +[s17; in.GetLine() `= aliqua. Ut enim ad minim veniam, quis nostrud +exercitation&] +[s17; in.GetLine() `= ullamco laboris nisi ut aliquip ex ea commodo +consequat.&] +[s17; in.GetLine() `= Duis aute irure dolor in reprehenderit in voluptate +velit&] +[s17; in.GetLine() `= esse cillum dolore eu fugiat nulla pariatur. +Excepteur&] +[s17; in.GetLine() `= sint occaecat cupidatat non proident, sunt in +culpa qui&] +[s17; in.GetLine() `= officia deserunt mollit anim id est laborum.&] +[s0; &] +[s5; [*C@5 Peek] can be used to look at the next character without +actually moving on to the next one:&] +[s0; &] +[s7; in.Seek(0);&] +[s7; DDUMP((char)in.Peek());&] +[s7; DDUMP(in.GetLine());&] +[s0; &] +[s17; (char)in.Peek() `= L&] +[s17; in.GetLine() `= Lorem ipsum dolor sit amet, consectetur adipiscing +elit,&] +[s0; &] +[s5; [*C@5 Get] method reads at most specified number of bytes from +the stream and returns them as [*C@5 String]:&] +[s0; &] +[s7; in.Seek(0);&] +[s7; DUMP(in.Get(10));&] +[s0; &] +[s17; in.Get(10) `= Lorem ipsu&] +[s0; &] +[s5; If there is not enough characters in the Stream as required +by Get, everything till EOF is returned:&] +[s0; &] +[s7; in.Seek(0);&] +[s7; DUMP(in.Get(999999).GetCount());&] +[s0; &] +[s17; in.Get(999999).GetCount() `= 452&] +[s0; &] +[s5; In contrast, [*C@5 GetAll] method fails when there is not enough +characters in the Stream and returns Void [*C@5 String] if Stream +is not in [*C@5 LoadThrowing] mode:&] +[s0; &] +[s7; in.Seek(0);&] +[s7; h `= in.GetAll(100);&] +[s7; DUMP(h.GetCount());&] +[s0; &] +[s17; h.GetCount() `= 100&] +[s0; &] +[s0; &] +[s7; h `= in.GetAll(999999);&] +[s7; DUMP(h.IsVoid());&] +[s0; &] +[s17; h.IsVoid() `= true&] +[s0; &] +[s5; In [*C@5 LoadThrowing] mode, [*C@5 Stream] throws [*C@5 LoadingError] +exception when there is problem with input [*C@5 Stream]:&] +[s0; &] +[s7; in.LoadThrowing();&] +[s7; try `{&] +[s7; -|in.GetAll(999999);&] +[s7; `}&] +[s7; catch(LoadingError) `{&] +[s7; -|LOG(`"Loading error`");&] +[s7; `}&] +[s0; &] +[s17; Loading error&] +[s0; &] +[s5; Template variant of [*C@5 Stream`::operator<<] is using [*C@5 AsString] +to convert data to text:&] +[s0; &] +[s7; String fn `= GetHomeDirFile(`"test.txt`");&] +[s7; FileOut out(fn);&] +[s7; if(!out) `{&] +[s7; -|LOG(`"Failed to open the file`");&] +[s7; -|return;&] +[s7; `}&] +[s7; out << `"Some number `" << 321 << `" and Point `" << Point(1, +2);&] +[s7; out.Close();&] +[s0; &] +[s5; When writing to the [*C@5 Stream], the good way to check for errors +is to write all data, close the stream and then check for [*C@5 IsError]:&] +[s0; &] +[s7; if(out.IsError()) `{ // check whether file was properly written&] +[s7; -|LOG(`"Error`");&] +[s7; -|return;&] +[s7; `}&] +[s7; DUMP(LoadFile(fn));&] +[s0; &] +[s17; LoadFile(fn) `= Some number 321 and Point `[1, 2`]&] +[s0; &] +[s5; [*C@5 FileAppend] can be used to append data to the file:&] +[s0; &] +[s7; FileAppend out2(fn);&] +[s7; out2 << `"`\nSomething more`";&] +[s7; out2.Close();&] +[s7; DUMP(LoadFile(fn));&] +[s0; &] +[s17; LoadFile(fn) `= Some number 321 and Point `[1, 2`]&] +[s17; Something more&] +[s0; &] +[s5; Important and often used type of [*C@5 Stream] is [*C@5 StringStream] +which works with [*C@5 String] as input/output.&] +[s5; [*C@5 Stream] also provides methods to store/load primitive types, +in both little`-endian and big`-endian modes:&] +[s0; &] +[s7; StringStream ss;&] +[s7; ss.Put32le(0x12345678);&] +[s7; ss.Put32be(0x12345678);&] +[s7; DUMPHEX(ss.GetResult());&] +[s0; &] +[s17; ss.GetResult() `= Memory at 0x0249F548, size 0x8 `= 8&] +[s17; `+0 0x0249F548 78 56 34 12 12 34 56 78 + xV4..4Vx &] +[s0; &] +[s0; &] +[s7; StringStream ss2(ss.GetResult());&] +[s7; DUMPHEX(ss2.Get32le());&] +[s7; DUMPHEX(ss2.Get32be());&] +[s0; &] +[s17; ss2.Get32le() `= 0x12345678&] +[s17; ss2.Get32be() `= 0x12345678&] +[s0; &] +[s3;H4;:Section`_2`_2: 2.2 Special streams&] +[s5; [*C@5 SizeStream] counts the number of bytes written to the stream:&] +[s0; &] +[s7; SizeStream szs;&] +[s7; szs << `"1234567`";&] +[s7; DUMP(szs.GetSize());&] +[s0; &] +[s17; szs.GetSize() `= 7&] +[s0; &] +[s5; [*C@5 CompareStream] can be used to compare the content of some +stream with data written to [*C@5 CompareStream]:&] +[s0; &] +[s7; StringStream in(`"123456`");&] +[s7; CompareStream cs(in);&] +[s7; cs.Put(`"12345`");&] +[s7; DUMP(cs.IsEqual());&] +[s0; &] +[s17; cs.IsEqual() `= true&] +[s0; &] +[s0; &] +[s7; cs.Put(`"7`");&] +[s7; DUMP(cs.IsEqual());&] +[s0; &] +[s17; cs.IsEqual() `= false&] +[s0; &] +[s5; [*C@5 OutStream] buffers output data to bigger blocks, then outputs +them via [*C@5 Out] virtual method:&] +[s0; &] +[s7; struct MyOutStream : OutStream `{&] +[s7; -|virtual void Out(const void `*data, dword size) `{&] +[s7; -|-|DUMPHEX(String((const char `*)data, size));&] +[s7; -|`}&] +[s7; `};&] +[s7; &] +[s7; MyOutStream os;&] +[s7; os << `"This is a test `" << 12345;&] +[s7; os.Close();&] +[s0; &] +[s17; String((const char `*)data, size) `= Memory at 0x05C51D10, size +0x14 `= 20&] +[s17; `+0 0x05C51D10 54 68 69 73 20 69 73 20 61 20 74 65 73 74 +20 31 This is a test 1&] +[s17; `+16 0x05C51D20 32 33 34 35 + 2345 &] +[s0; &] +[s5; [*C@5 TeeStream] sends output data to two separate streams:&] +[s0; &] +[s7; StringStream ss1;&] +[s7; StringStream ss2;&] +[s7; TeeStream tee(ss1, ss2);&] +[s7; tee << `"Tee stream test`";&] +[s7; tee.Close();&] +[s7; DUMP(ss1.GetResult());&] +[s7; DUMP(ss2.GetResult());&] +[s0; &] +[s17; ss1.GetResult() `= Tee stream test&] +[s17; ss2.GetResult() `= Tee stream test&] +[s0; &] +[s5; [*C@5 MemReadStream] can be used to convert read`-only memory +block to stream data:&] +[s0; &] +[s7; static const char s`[`] `= `"Some line`\nAnother line`";&] +[s7; MemReadStream ms(s, sizeof(s) `- 1);&] +[s7; while(!ms.IsEof())&] +[s7; -|DUMPHEX(ms.GetLine());&] +[s0; &] +[s17; ms.GetLine() `= Memory at 0x0249F3CC, size 0x9 `= 9&] +[s17; `+0 0x0249F3CC 53 6F 6D 65 20 6C 69 6E 65 + Some line &] +[s17; ms.GetLine() `= Memory at 0x0249F3CC, size 0xC `= 12&] +[s17; `+0 0x0249F3CC 41 6E 6F 74 68 65 72 20 6C 69 6E 65 + Another line &] +[s0; &] +[s3;H4;:Section`_2`_3: 2.3 Binary serialization&] +[s5; Serialization is a mechanism that converts structured data to/from +binary stream. In U`+`+, loading and storing of data is performed +by single code, in most cases represented by method [*C@5 Serialize]. +Serialization is performed directly with basic [*C@5 Stream]. To +this end, [*C@5 Stream] features a single boolean representing +the direction of serialization process. The direction can be +checked using [*C@5 IsLoading] and [*C@5 IsStoring] methods and changed +with [*C@5 SetStoring] and [*C@5 SetLoading] methods. Direction is +usually set properly by derived classes (e.g. FileOut sets it +to storing, FileIn to loading).&] +[s5; Shortcut to calling [*C@5 Serialize] method is [*C@5 operator%], +which is templated overload that calls [*C@5 Serialize] for given +variable (primitive types have direct overload in [*C@5 Stream] +class):&] +[s0; &] +[s7; StringStream ss;&] +[s7; &] +[s7; int x `= 123;&] +[s7; Color h `= White();&] +[s7; &] +[s7; ss % x % h;&] +[s7; &] +[s7; StringStream ss2(ss.GetResult());&] +[s7; &] +[s7; int x2;&] +[s7; Color h2;&] +[s7; &] +[s7; ss2 % x2 % h2;&] +[s7; &] +[s7; DUMP(x2);&] +[s7; DUMP(h2);&] +[s0; &] +[s17; x2 `= 123&] +[s17; h2 `= Color(255, 255, 255)&] +[s0; &] +[s5; When serialization fails to load the data (e.g. because of wrong +structure or not enough data in the stream), [*C@5 Stream`::LoadError] +is invoked, which can trigger the exception if the stream is +[*C@5 LoadThrowing]:&] +[s0; &] +[s7; ss2.Seek(0);&] +[s7; ss2.LoadThrowing();&] +[s7; try `{&] +[s7; -|ss2 % x2 % h2 % x2;&] +[s7; `}&] +[s7; catch(LoadingError) `{&] +[s7; -|LOG(`"Deserialization has failed`");&] +[s7; `}&] +[s0; &] +[s17; Deserialization has failed&] +[s0; &] +[s5; Examples so far serve mostly like basic demonstration of serialization. +In practice, the implementation is usually represented by [*C@5 Serialize] +method of class that is to be compatible with this concept. To +that end, it is a good idea to provide means for future expansion +of such class:&] +[s0; &] +[s7; struct MyFoo `{&] +[s7; -|int number;&] +[s7; -|Color color;&] +[s7; -|&] +[s7; -|void Serialize(Stream`& s) `{&] +[s7; -|-|int version `= 0;&] +[s7; -|-|s / version; // allow backward compatibility in the future&] +[s7; -|-|s.Magic(31415); // put magic number into the stream to check +for invalid data&] +[s7; -|-|s % number % color;&] +[s7; -|`}&] +[s7; `};&] +[s7; &] +[s7; MyFoo foo;&] +[s7; foo.number `= 321;&] +[s7; foo.color `= Blue();&] +[s0; &] +[s5; [*C@5 StoreAsFile], [*C@5 StoreAsString], [*C@5 LoadFromFile] and +[*C@5 LoadFromString] are convenience functions that simplify storing +/ loading objects to / from the most common forms of storage:&] +[s0; &] +[s7; String data `= StoreAsString(foo);&] +[s7; MyFoo foo2;&] +[s7; LoadFromString(foo2, data);&] +[s7; DUMP(foo2.number);&] +[s7; DUMP(foo2.color);&] +[s0; &] +[s17; foo2.number `= 321&] +[s17; foo2.color `= Color(0, 0, 128)&] +[s0; &] +[s5; Now if [*C@5 MyFoo] was to be extended to [*C@5 MyFoo2] and we wanted +to maintain the ability to load it from binary data stored by +original [*C@5 MyFoo], we can branch on previously stored [*C@5 version]:&] +[s0; &] +[s7; struct MyFoo2 `{&] +[s7; -|int number;&] +[s7; -|Color color;&] +[s7; -|String text;&] +[s7; -|&] +[s7; -|void Serialize(Stream`& s) `{&] +[s7; -|-|int version `= 1;&] +[s7; -|-|s / version;&] +[s7; -|-|s % number % color;&] +[s7; -|-|if(version >`= 1)&] +[s7; -|-|-|s % text;&] +[s7; -|`}&] +[s7; `};&] +[s7; MyFoo2 foo3;&] +[s7; LoadFromString(foo3, data);&] +[s7; DUMP(foo3.number);&] +[s7; DUMP(foo3.color);&] +[s0; &] +[s17; foo3.number `= 0&] +[s17; foo3.color `= Color(Null)&] +[s0; &] +[s5; Note: [*C@5 operator/] is Stream method with several overloads +optimized for small value `- in this case [*C@5 int] is stored +as single byte if possible (and as 5 bytes if not).&] +[s22;:Chapter`_3: 3. Array containers&] +[s3;:Section`_3`_1: 3.1 [C@5 Vector] basics&] [s5; [*C@5 Vector] is the basic container of U`+`+. It is the random access container similar to [*C@5 std`::vector] with one important performance related difference: There are rules for elements of @@ -990,7 +1355,7 @@ returns a reference to a new element in [*C@5 Vector]:&] [s17; 6&] [s17; 7&] [s0; &] -[s3;H4;:Section`_2`_2: 2.2 [C@5 Vector] operations&] +[s3;H4;:Section`_3`_2: 3.2 [C@5 Vector] operations&] [s5; You can [*C@5 Insert] or [*C@5 Remove] elements at random positions of Vector (O(n) complexity):&] [s0; &] @@ -1030,7 +1395,7 @@ value.&] [s7; &] [s7; DUMP(v);&] [s0; &] -[s17; v `= `[972, 1037, 983, 993, 1009, 981, 1002, 1033, 963, 1027`]&] +[s17; v `= `[1030, 963, 1014, 1007, 978, 1042, 954, 998, 983, 1031`]&] [s0; &] [s5; You can apply algorithms on containers, e.g. Sort&] [s0; &] @@ -1038,9 +1403,9 @@ value.&] [s7; &] [s7; DUMP(v);&] [s0; &] -[s17; v `= `[963, 972, 981, 983, 993, 1002, 1009, 1027, 1033, 1037`]&] +[s17; v `= `[954, 963, 978, 983, 998, 1007, 1014, 1030, 1031, 1042`]&] [s0; &] -[s3;H4;:Section`_2`_3: 2.3 Transfer issues&] +[s3;H4;:Section`_3`_3: 3.3 Transfer issues&] [s5; Often you need to pass content of one container to another of the same type. U`+`+ containers always support [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ p ick semantics] (synonym of std`::move), and, depending on type @@ -1106,7 +1471,7 @@ instead of explicit [*C@5 clone]. You can easily achieve that using [s17; v `= `[1, 2`]&] [s17; v2 `= `[1, 2`]&] [s0; &] -[s3;H4;:Section`_2`_4: 2.4 Client types in U`+`+ containers&] +[s3;H4;:Section`_3`_4: 3.4 Client types in U`+`+ containers&] [s5; So far we were using int as type of elements. In order to store client defined types into the [*C@5 Vector] (and the Vector [^topic`:`/`/Core`/src`/Overview`$en`-us^ f lavor]) the type must satisfy [^topic`:`/`/Core`/src`/Moveable`$en`-us^ moveable] @@ -1144,15 +1509,15 @@ to it:&] [s7; DUMPC(dist);&] [s0; &] [s17; dist:&] -[s17; -|`[0`] `= Test 5: `[1940, 2038, 2008, 2034, 1980`]&] -[s17; -|`[1`] `= Test 6: `[1643, 1672, 1672, 1635, 1718, 1660`]&] -[s17; -|`[2`] `= Test 7: `[1458, 1420, 1432, 1369, 1471, 1403, 1447`]&] -[s17; -|`[3`] `= Test 8: `[1229, 1288, 1283, 1256, 1237, 1221, 1223, -1263`]&] -[s17; -|`[4`] `= Test 9: `[1069, 1126, 1075, 1090, 1112, 1126, 1139, -1148, 1115`]&] -[s17; -|`[5`] `= Test 10: `[1058, 982, 984, 973, 982, 988, 1009, 1030, -990, 1004`]&] +[s17; -|`[0`] `= Test 5: `[2010, 1987, 2010, 2000, 1993`]&] +[s17; -|`[1`] `= Test 6: `[1620, 1673, 1682, 1690, 1654, 1681`]&] +[s17; -|`[2`] `= Test 7: `[1384, 1393, 1426, 1454, 1460, 1421, 1462`]&] +[s17; -|`[3`] `= Test 8: `[1257, 1250, 1267, 1285, 1244, 1229, 1188, +1280`]&] +[s17; -|`[4`] `= Test 9: `[1063, 1077, 1096, 1123, 1128, 1133, 1155, +1108, 1117`]&] +[s17; -|`[5`] `= Test 10: `[1010, 1013, 943, 999, 1001, 980, 969, 1054, +1011, 1020`]&] [s0; &] [s5; Another possibility is to use [*C@5 Vector`::Add(T`&`&)] method, which uses pick`-constructor instead of deep`-copy constructor. @@ -1169,7 +1534,7 @@ E.g. [*C@5 Distribution] elements might be generated by some function:&] [s0; &] [s7; -|dist.Add() `= CreateDist();&] [s0; &] -[s3;H4;:Section`_2`_5: 2.5 Array flavor&] +[s3;H4;:Section`_3`_5: 3.5 Array flavor&] [s5; If elements are not [*C@5 Moveable] and therefore cannot be stored in [*C@5 Vector] flavor, they can still be stored in [*C@5 Array] flavor. Another reason for using Array is the need for referencing @@ -1192,7 +1557,7 @@ standard library knows nothing about U`+`+ Moveable concept):&] [s17; s.c`_str() `= Test&] [s17; s.c`_str() `= Test&] [s0; &] -[s3;H4;:Section`_2`_6: 2.6 Polymorphic [C@5 Array]&] +[s3;H4;:Section`_3`_6: 3.6 Polymorphic [C@5 Array]&] [s5; [*C@5 Array] can even be used for storing polymorphic elements:&] [s0; &] [s7; struct Number `{&] @@ -1249,7 +1614,7 @@ a.Get() < b.Get(); `});&] [s0; &] [s17; num `= `[1.1, 3, 15.5`]&] [s0; &] -[s3;H4;:Section`_2`_7: 2.7 Bidirectional containers&] +[s3;H4;:Section`_3`_7: 3.7 Bidirectional containers&] [s5; [*C@5 Vector] and [*C@5 Array] containers allow fast adding and removing elements at the end of sequence. Sometimes, same is needed at begin of sequence too (usually to support FIFO queues). @@ -1302,7 +1667,7 @@ needed at begin of sequence too (usually to support FIFO queues). [s0; &] [s17; num `= `[World, 3, Hello, 2`]&] [s0; &] -[s3;H4;:Section`_2`_8: 2.8 [C@5 Index]&] +[s3;H4;:Section`_3`_8: 3.8 [C@5 Index]&] [s5; [*C@5 Index] is the the foundation of all U`+`+ associative operations and is one of defining features of U`+`+.&] [s5; [*C@5 Index] is a container very similar to the plain [*C@5 Vector] @@ -1452,7 +1817,7 @@ Index)&] [s0; &] [s17; ndx `= `[test, alfa, foo, delta, one, three`]&] [s0; &] -[s3;H4;:Section`_2`_9: 2.9 Index and client types&] +[s3;H4;:Section`_3`_9: 3.9 Index and client types&] [s5; In order to store elements to [*C@5 Index], they type must be [*C@5 Moveable], have deep copy and defined the [*C@5 operator`=`=] and a [*C@5 GetHashValue] function or method to compute the hash @@ -1482,7 +1847,7 @@ b.name `&`& surname `=`= b.surname; `}&] [s0; &] [s17; p.Find(Person(`"Paul`", `"Carpenter`")) `= 1&] [s0; &] -[s3;H4;:Section`_2`_10: 2.10 [C@5 VectorMap], [C@5 ArrayMap]&] +[s3;H4;:Section`_3`_10: 3.10 [C@5 VectorMap], [C@5 ArrayMap]&] [s5; [*C@5 VectorMap] is nothing else than a simple composition of [*C@5 Index] of keys and [*C@5 Vector] of values. You can use [*C@5 Add] methods to put elements into the [*C@5 VectorMap]:&] @@ -1685,7 +2050,7 @@ Array is better fit for value type (e.g. they are polymorphic):&] [s0; &] [s17; am `= `{key: new person`}&] [s0; &] -[s3;H4;:Section`_2`_11: 2.11 [C@5 One]&] +[s3;H4;:Section`_3`_11: 3.11 [C@5 One]&] [s5; [*C@5 One] is a container that can store none or one element of T or derived from T. It is functionally quite similar to [*C@5 std`::unique`_ptr], but has some convenient features.&] @@ -1764,7 +2129,7 @@ contained element:&] [s0; &] [s17; s`->Get() `= Derived2&] [s0; &] -[s3;H4;:Section`_2`_12: 2.12 [C@5 Any]&] +[s3;H4;:Section`_3`_12: 3.12 [C@5 Any]&] [s5; [*C@5 Any] is a container that can contain none or one element of [/ any] type. [*C@5 Any`::Is] method matches exact type ignoring class hierarchies (unlike [*C@5 One`::Is]). You can use [*C@5 Get] @@ -1787,7 +2152,7 @@ to retrieve a reference to the instance stored:&] [s17; Any is now Color: Color(0, 0, 128)&] [s17; Any is now String: Hello!&] [s0; &] -[s3;H4;:Section`_2`_13: 2.13 [C@5 InVector], [C@5 InArray]&] +[s3;H4;:Section`_3`_13: 3.13 [C@5 InVector], [C@5 InArray]&] [s5; [*C@5 InVector] and [*C@5 InArray] are container types quite similar to [*C@5 Vector]/``Array``, but they trade the speed of [*C@5 operator`[`]] with the ability to insert or remove elements at any position @@ -1814,7 +2179,7 @@ basically match the performace of [*C@5 Find`*Bound] on simple [s0; &] [s17; v.FindLowerBound(55) `= 56&] [s0; &] -[s3;H4;:Section`_2`_14: 2.14 [C@5 SortedIndex], [C@5 SortedVectorMap], +[s3;H4;:Section`_3`_14: 3.14 [C@5 SortedIndex], [C@5 SortedVectorMap], [C@5 SortedArrayMap]&] [s5; [*C@5 SortedIndex] is similar to regular [*C@5 Index], but keeps its elements in sorted order (sorting predicate is a template @@ -1863,7 +2228,7 @@ based equivalents to [*C@5 VectorMap]/``ArrayMap``:&] [s17; -|`[2`] `= (zulu) 11&] [s17; m.Get(`"zulu`") `= 11&] [s0; &] -[s3;H4;:Section`_2`_15: 2.15 Tuples&] +[s3;H4;:Section`_3`_15: 3.15 Tuples&] [s5; Template class [*C@5 Tuple] allows combining 2`-4 values with different types. These are principally similar to [*C@5 std`::tuple], with some advantages. Unlike [*C@5 std`::tuple], individual elements @@ -2008,8 +2373,8 @@ tuple based on the first value ([*C@5 a]) (linear O(n) search):&] [s0; &] [s17; FindTuple(map, `_`_countof(map), 3)`->b `= one&] [s0; &] -[s22;:Chapter`_3: 3. Ranges and algoritims&] -[s3;:Section`_3`_1: 3.1 Range&] +[s22;:Chapter`_4: 4. Ranges and algorithms&] +[s3;:Section`_4`_1: 4.1 Range&] [s5; Unlike STL, which interface algorithms with data using [*C@5 begin] / [*C@5 end] pair, U`+`+ algorithms usually work on [/ Ranges]. Range is an object that has [*C@5 begin] / [*C@5 end] methods providing @@ -2059,12 +2424,13 @@ easier:&] [s7; DUMP(typeid(ConstIteratorOf).name());&] [s7; DUMP(typeid(SubRangeOf>).name());&] [s0; &] -[s17; typeid(ValueTypeOf).name() `= i&] -[s17; typeid(ValueTypeOf).name() `= i&] -[s17; typeid(IteratorOf).name() `= Pi&] +[s17; typeid(ValueTypeOf).name() `= int&] +[s17; typeid(ValueTypeOf).name() `= int&] +[s17; typeid(IteratorOf).name() `= int `*&] [s17; typeid(ConstIteratorOf).name() -`= Pi&] -[s17; typeid(SubRangeOf>).name() `= N3Upp13SubRangeClassIPiEE&] +`= int `*&] +[s17; typeid(SubRangeOf>).name() `= class Upp`::SubRangeClass&] [s0; &] [s5; While containers themselves and SubRange are the two most common range types, U`+`+ has two special ranges. [*C@5 ConstRange] simply @@ -2122,7 +2488,7 @@ certain condition:&] [s17; ReverseRange(FilterRange(x, `[`](int x) `{ return x < 4; `})) `= `[3, 2, 1, 3, 1, 2`]&] [s0; &] -[s3;H4;:Section`_3`_2: 3.2 Algorithms&] +[s3;H4;:Section`_4`_2: 4.2 Algorithms&] [s5; In principle, is is possible to apply C`+`+ standard library algorithms on U`+`+ containers or ranges.&] [s5; U`+`+ algorithms are tuned for U`+`+ approach `- they work on @@ -2210,7 +2576,7 @@ the index of element with given value or `-1 if not found:&] [s17; FindUpperBound(data, 10) `= 4&] [s17; FindBinary(data, 10) `= `-1&] [s0; &] -[s3;H4;:Section`_3`_3: 3.3 Sorting&] +[s3;H4;:Section`_4`_3: 4.3 Sorting&] [s5; Unsurprisingly, [*C@5 Sort] function sorts a range. You can specify sorting predicate, default is [*C@5 operator<]:&] [s0; &] @@ -2282,8 +2648,8 @@ a.x < b.x; `});&] [s0; &] [s5; All sorting algorithms have they `'Stable`' variant, so there is [*C@5 StableIndexSort], [*C@5 GetStableSortOrder] etc...&] -[s22;:Chapter`_4: 4. Value&] -[s3;:Section`_4`_1: 4.1 Value&] +[s22;:Chapter`_5: 5. Value&] +[s3;:Section`_5`_1: 5.1 Value&] [s5; Value is sort of equivalent of polymorphic data types from scripting languages like Python or JavaSript. [*C@5 Value] can represent values of concrete types, some types also have extended interoperability @@ -2316,11 +2682,11 @@ is for the most part seamless:&] [s0; &] [s17; a `= 1&] [s17; b `= 2.34&] -[s17; c `= 07/19/2017&] +[s17; c `= 12/08/2017&] [s17; d `= hello&] [s17; x `= 1&] [s17; y `= 2.34&] -[s17; z `= 07/19/2017&] +[s17; z `= 12/08/2017&] [s17; s `= hello&] [s0; &] [s5; As for primitive types, Value seamlessly works with [*C@5 int], @@ -2352,7 +2718,7 @@ as it is supported by these types):&] [s0; &] [s17; i `= 1&] [s17; j `= 2&] -[s17; k `= 07/19/2017 00:00:00&] +[s17; k `= 12/08/2017 00:00:00&] [s17; t `= hello&] [s0; &] [s5; To determine type of value stored in [*C@5 Value], you can use @@ -2386,7 +2752,7 @@ functions are defined:&] [s17; IsDateTime(c) `= true&] [s17; IsString(d) `= true&] [s0; &] -[s3;H4;:Section`_4`_2: 4.2 [C@5 Null]&] +[s3;H4;:Section`_5`_2: 5.2 [C@5 Null]&] [s5; U`+`+ defines a special [*C@5 Null] constant to represent an empty value. This constant is convertible to many value types including primitive types [*C@5 double], [*C@5 int] and [*C@5 int64] (defined @@ -2435,7 +2801,7 @@ coalesce (ifnull, Nvl), which returns the first non`-null argument [s0; &] [s17; Nvl(a, b, c) `= 123&] [s0; &] -[s3;H4;:Section`_4`_3: 4.3 Client types and [C@5 Value], [C@5 RawValue], +[s3;H4;:Section`_5`_3: 5.3 Client types and [C@5 Value], [C@5 RawValue], [C@5 RichValue]&] [s5; There are two Value compatibility levels. The simple one, [*C@5 RawValue], has little requirements for the type used `- only copy constructor @@ -2539,7 +2905,7 @@ correct type, then uses&] [s0; &] [s17; loaded `= 54321&] [s0; &] -[s3;H4;:Section`_4`_4: 4.4 [C@5 ValueArray] and [C@5 ValueMap]&] +[s3;H4;:Section`_5`_4: 5.4 [C@5 ValueArray] and [C@5 ValueMap]&] [s5; [*C@5 ValueArray] is a type that represents an array of [*C@5 Value]s:&] [s0; &] [s7; ValueArray va`{1, 2, 3`};&] @@ -2804,8 +3170,8 @@ e`":5`}`}&] [s17; AsJSON(j) `= `{`"array`":`[1,`{`"key`":1`},3`],`"value`":`{`"one`":1,`"four`":4,`"thr ee`":3,`"five`":5`}`}&] [s0; &] -[s22;:Chapter`_5: 5. Function and lambdas&] -[s3;:Section`_5`_1: 5.1 Function&] +[s22;:Chapter`_6: 6. Function and lambdas&] +[s3;:Section`_6`_1: 6.1 Function&] [s5; U`+`+ [*C@5 Function] is quite similar to [*C@5 std`::function] `- it is a function wrapper that can store/copy/invoke any callable target. There are two important differences. First, invoking @@ -2923,7 +3289,7 @@ less verbose&] [s17; Foo`::Test 1, using lambda&] [s17; Foo`::Test 2, using THISFN&] [s0; &] -[s3;H4;:Section`_5`_2: 5.2 Capturing U`+`+ containers into lambdas&] +[s3;H4;:Section`_6`_2: 6.2 Capturing U`+`+ containers into lambdas&] [s5; Capturing objects with pick/clone semantics can be achieved using [/ capture with an initializer]:&] [s0; &] @@ -2945,8 +3311,8 @@ using [/ capture with an initializer]:&] [s17; x `= `[1, 2`]&] [s17; y `= `[one, two`]&] [s0; &] -[s22;:Chapter`_6: 6. Multithreading&] -[s3;:Section`_6`_1: 6.1 [C@5 Thread]&] +[s22;:Chapter`_7: 7. Multithreading&] +[s3;:Section`_7`_1: 7.1 [C@5 Thread]&] [s5; Since C`+`+11, there is now a reasonable support for threads in standard library. There are however reasons to use U`+`+ threads instead. One of them is that U`+`+ high performance memory allocator @@ -2979,16 +3345,16 @@ thread to [*C@5 Wait] for its completion:&] [s0; &] [s17; In the main thread 0&] [s17; In the thread 0&] -[s17; In the thread 1&] [s17; In the main thread 1&] +[s17; In the thread 1&] [s17; In the thread 2&] [s17; In the main thread 2&] -[s17; In the main thread 3&] [s17; In the thread 3&] -[s17; In the main thread 4&] +[s17; In the main thread 3&] [s17; In the thread 4&] -[s17; About to wait for thread to finish&] +[s17; In the main thread 4&] [s17; In the thread 5&] +[s17; About to wait for thread to finish&] [s17; In the thread 6&] [s17; In the thread 7&] [s17; In the thread 8&] @@ -3016,7 +3382,7 @@ true; `});&] [s17; Wait for thread done&] [s0; &] [s5; (method used here is horrible, but should demonstrate the point).&] -[s3;H4;:Section`_6`_2: 6.2 [C@5 Mutex]&] +[s3;H4;:Section`_7`_2: 7.2 [C@5 Mutex]&] [s5; Mutex (`"mutual exclusion`") is a well known concept in multithreaded programming: When multiple threads write and read the same data, the access has to be serialized using Mutex. Following invalid @@ -3036,7 +3402,7 @@ code demonstrates why:&] [s7; t.Wait();&] [s7; DUMP(sum);&] [s0; &] -[s17; sum `= 1560532&] +[s17; sum `= 1050423&] [s0; &] [s5; While the expected value is 2000000, produced value is different. The problem is that both thread read / modify / write [*C@5 sum] @@ -3068,7 +3434,7 @@ destructor:&] [s0; &] [s17; sum `= 2000000&] [s0; &] -[s3;H4;:Section`_6`_3: 6.3 [C@5 ConditionVariable]&] +[s3;H4;:Section`_7`_3: 7.3 [C@5 ConditionVariable]&] [s5; [*C@5 ConditionVariable] in general is a synchronization primitive used to block/awaken the thread. [*C@5 ConditionVariable] is associated with [*C@5 Mutex] used to protect some data; in the thread that @@ -3126,7 +3492,7 @@ threads.&] if no other called [*C@5 Signal]. This is not a bug, but [^https`:`/`/en`.wikipedia`.org`/wiki`/Spurious`_wakeup^ d esign decision for performance reason]. In practice it only means that situation has to be (re)checked after resume.&] -[s3;H4;:Section`_6`_4: 6.4 [C@5 CoWork]&] +[s3;H4;:Section`_7`_4: 7.4 [C@5 CoWork]&] [s5; [*C@5 CoWork] is intented to be use when thread are used to speedup code by distributing tasks over multiple CPU cores. [*C@5 CoWork] spans a single set of worker threads that exist for the whole @@ -3160,23 +3526,23 @@ testing data&] [s7; &] [s7; DUMP(w);&] [s0; &] -[s17; w `= `[esse, cillum, dolore, eu, fugiat, nulla, pariatur, Excepteur, -sint, occaecat, cupidatat, consequat, Duis, aute, irure, dolor, -in, reprehenderit, voluptate, velit, quis, nostrud, exercitation, -ullamco, laboris, nisi, ut, aliquip, ex, ea, commodo, tempor, -incididunt, labore, et, magna, aliqua, Ut, enim, ad, minim, veniam, -Lorem, ipsum, sit, amet, consectetur, adipiscing, elit, sed, -do, eiusmod, non, proident, sunt, culpa, qui, officia, deserunt, -mollit, anim, id, est, laborum`]&] +[s17; w `= `[Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, +elit, aliqua, Ut, enim, ad, minim, veniam, quis, nostrud, exercitation, +Duis, aute, irure, in, reprehenderit, voluptate, velit, esse, +cillum, dolore, eu, fugiat, nulla, pariatur, Excepteur, officia, +deserunt, mollit, anim, id, est, laborum, ullamco, laboris, nisi, +ut, aliquip, ex, ea, commodo, consequat, sint, occaecat, cupidatat, +non, proident, sunt, culpa, qui, sed, do, eiusmod, tempor, incididunt, +labore, et, magna`]&] [s0; &] [s5; Adding words to [*C@5 w] requires [*C@5 Mutex]. Alternative to this -`'result gathering`' [*C@5 Mutex] is CoWork`::FinLock. The idea -behind this is that CoWork requires an internal [*C@5 Mutex] to -serialize access to common data, so why [*C@5 FinLock] locks this -internal mutex a bit earlier, saving CPU cycles required to lock -and unlock dedicated mutex. From API contract perspective, you -can consider [*C@5 FinLock] to serialize code till the end of worker -job.&] +`'result gathering`' [*C@5 Mutex] is [*C@5 CoWork`::FinLock]. The +idea behind this is that CoWork requires an internal [*C@5 Mutex] +to serialize access to common data, so why [*C@5 FinLock] locks +this internal mutex a bit earlier, saving CPU cycles required +to lock and unlock dedicated mutex. From API contract perspective, +you can consider [*C@5 FinLock] to serialize code till the end +of worker job.&] [s0; &] [s7; in.Seek(0);&] [s7; while(!in.IsEof()) `{&] @@ -3194,20 +3560,131 @@ end of CoWork job&] [s7; &] [s7; DUMP(w);&] [s0; &] -[s17; w `= `[esse, cillum, dolore, eu, fugiat, nulla, pariatur, Excepteur, -sint, occaecat, cupidatat, consequat, Duis, aute, irure, dolor, -in, reprehenderit, voluptate, velit, quis, nostrud, exercitation, -ullamco, laboris, nisi, ut, aliquip, ex, ea, commodo, tempor, -incididunt, labore, et, magna, aliqua, Ut, enim, ad, minim, veniam, -Lorem, ipsum, sit, amet, consectetur, adipiscing, elit, sed, -do, eiusmod, non, proident, sunt, culpa, qui, officia, deserunt, -mollit, anim, id, est, laborum`]&] +[s17; w `= `[Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, +elit, aliqua, Ut, enim, ad, minim, veniam, quis, nostrud, exercitation, +Duis, aute, irure, in, reprehenderit, voluptate, velit, esse, +cillum, dolore, eu, fugiat, nulla, pariatur, Excepteur, officia, +deserunt, mollit, anim, id, est, laborum, ullamco, laboris, nisi, +ut, aliquip, ex, ea, commodo, consequat, sint, occaecat, cupidatat, +non, proident, sunt, culpa, qui, sed, do, eiusmod, tempor, incididunt, +labore, et, magna`]&] [s0; &] -[s5; Of course, the code performed after FinLock should not take -long, otherwise there is negative impact on all CoWork instances. -In fact, from this perspective, above code is probably past this -threshold...&] -[s3;H4;:Section`_6`_5: 6.5 CoPartition&] +[s5; Of course, the code performed after [*C@5 FinLock] should not +take long, otherwise there is negative impact on all [*C@5 CoWork] +instances. In fact, from this perspective, above code is probably +past the threshold...&] +[s5; When exception is thrown in [*C@5 CoWork], it is propagated to +the thread that calls [*C@5 Finish] and [*C@5 CoWork] is canceled. +If more than single job throws, one of exceptions is selected +randomly to be rethrown in Finish.&] +[s5; As [*C@5 CoWork] destructor calls [*C@5 Finish] too, it is possible +that it will be thrown by destructor, which is not exactly recommended +thing to do in C`+`+, but is well defined and really the best +option here:&] +[s0; &] +[s7; in.Seek(0);&] +[s7; try `{&] +[s7; -|while(!in.IsEof()) `{&] +[s7; -|-|String ln `= in.GetLine();&] +[s7; -|-|co `& `[ln, `&w, `&m`] `{&] +[s7; -|-|-|if(ln.GetCount() > 75)&] +[s7; -|-|-|-|throw `"Input line was too long!`";&] +[s7; -|-|-|Vector h `= Split(ln, `[`](int c) `{ return IsAlpha(c) +? 0 : c; `});&] +[s7; -|-|-|CoWork`::FinLock(); // replaces the mutex, locked till the +end of CoWork job&] +[s7; -|-|-|for(const auto`& s : h)&] +[s7; -|-|-|-|w.FindAdd(s);&] +[s7; -|-|`};&] +[s7; -|`}&] +[s7; -|co.Finish();&] +[s7; `}&] +[s7; catch(const char `*exception) `{&] +[s7; -|DUMP(exception);&] +[s7; `}&] +[s0; &] +[s5; Sometimes there is a need for cancellation of the whole [*C@5 CoWork]. +[*C@5 Cancel] method cancels all scheduled jobs that have not been +yet executed and sets [*C@5 CoWork] to canceled state, which can +be checked in job routine using [*C@5 CoWork`::IsCanceled]:&] +[s0; &] +[s7; for(int i `= 0; i < 100; i`+`+)&] +[s7; -|co `& `[`] `{&] +[s7; -|-|for(;;) `{&] +[s7; -|-|-|if(CoWork`::IsCanceled()) `{&] +[s7; -|-|-|-|LOG(`"Job was canceled`");&] +[s7; -|-|-|-|return;&] +[s7; -|-|-|`}&] +[s7; -|-|-|Sleep(1);&] +[s7; -|-|`}&] +[s7; -|`};&] +[s7; Sleep(200); // Give CoWork a chance to start some jobs&] +[s7; co.Cancel();&] +[s0; &] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s17; Job was canceled&] +[s0; &] +[s5; Canceling CoWork is common in GUI applications.&] +[s3;H4;:Section`_7`_5: 7.5 [C@5 AsyncWork]&] +[s5; [*C@5 AsyncWork] is [*C@5 CoWork] based tool that resembles std`::future. +[*C@5 AsyncWork] instances are created using [*C@5 Async] function +and represent a work that can be done in parallel with current +thread. [*C@5 AsyncWork] supports returning values. A call to [*C@5 AsyncWork`::Get] +makes sure that a work routine was finished and returns the return +value (if any):&] +[s0; &] +[s7; auto a `= Async(`[`](int n) `-> double `{&] +[s7; -|double f `= 1;&] +[s7; -|for(int i `= 2; i <`= n; i`+`+)&] +[s7; -|-|f `*`= i;&] +[s7; -|return f;&] +[s7; `}, 100);&] +[s7; &] +[s7; DUMP(a.Get());&] +[s0; &] +[s17; a.Get() `= 9.33262154439441e157&] +[s0; &] +[s5; Exceptions thrown in Async work are propagated upon call to +[*C@5 Get]:&] +[s0; &] +[s7; auto b `= Async(`[`] `{ throw `"error`"; `});&] +[s7; &] +[s7; try `{&] +[s7; -|b.Get();&] +[s7; `}&] +[s7; catch(...) `{&] +[s7; -|LOG(`"Exception has been caught`");&] +[s7; `}&] +[s0; &] +[s17; Exception has been caught&] +[s0; &] +[s5; [*C@5 AsyncWork] instances can be canceled (and are canceled in +destructor if Get is not called on them):&] +[s0; &] +[s7; `{&] +[s7; -|auto c `= Async(`[`] `{&] +[s7; -|-|for(;;)&] +[s7; -|-|-|if(CoWork`::IsCanceled()) `{&] +[s7; -|-|-|-|LOG(`"Work was canceled`");&] +[s7; -|-|-|-|break;&] +[s7; -|-|-|`}&] +[s7; -|`});&] +[s7; -|Sleep(100); // give it chance to start&] +[s7; -|// c destructor cancels the work (can be explicitly canceled +by Cancel method too)&] +[s7; `}&] +[s0; &] +[s17; Work was canceled&] +[s0; &] +[s3;H4;:Section`_7`_6: 7.6 CoPartition&] [s5; There is some overhead associated with CoWork worker threads. That is why e.g. performing a simple operation on the array spawning worker thread for each element is not a good idea performance @@ -3281,7 +3758,7 @@ h) `{&] [s0; &] [s17; sum `= 49995000&] [s0; &] -[s3;H4;:Section`_6`_6: 6.6 Parallel algorithms&] +[s3;H4;:Section`_7`_7: 7.7 Parallel algorithms&] [s5; U`+`+ provides a parallel versions of algorithms where it makes sense. The naming scheme is `'Co`' prefix before the name of algorithm designates the parallel version.&]