topic "U++ Core Tutorial";
[l288;i1120;a17;O9;~~~.1408;2 $$1,0#10431211400427159095818037425705:param]
[a83;*R6 $$2,5#31310162474203024125188417583966:caption]
[b83;*4 $$3,5#07864147445237544204411237157677:title]
[i288;O9;C2 $$4,6#40027414424643823182269349404212:item]
[b42;a42;ph2 $$5,5#45413000475342174754091244180557:text]
[l288;b17;a17;2 $$6,6#27521748481378242620020725143825:desc]
[l321;C@5;1 $$7,7#20902679421464641399138805415013:code]
[b2503;2 $$8,0#65142375456100023862071332075487:separator]
[*@(0.0.255)2 $$9,0#83433469410354161042741608181528:base]
[C2 $$10,0#37138531426314131251341829483380:class]
[l288;a17;*1 $$11,11#70004532496200323422659154056402:requirement]
[i417;b42;a42;O9;~~~.416;2 $$12,12#10566046415157235020018451313112:tparam]
[b167;C2 $$13,13#92430459443460461911108080531343:item1]
[i288;a42;O9;C2 $$14,14#77422149456609303542238260500223:item2]
[*@2$(0.128.128)2 $$15,15#34511555403152284025741354420178:NewsDate]
[l321;*C$7;2 $$16,16#03451589433145915344929335295360:result]
[l321;*C$7;2 $$17,17#07531550463529505371228428965313:result`-line]
[l160;*C+117 $$18,5#88603949442205825958800053222425:package`-title]
[2 $$19,0#53580023442335529039900623488521:gap]
[C2 $$20,20#70211524482531209251820423858195:class`-nested]
[b50;2 $$21,21#03324558446220344731010354752573:Par]
[H8;b73;*+150 $$22,5#07864147445237544204111237153677:subtitle]
[2 $$0,0#00000000000000000000000000000000:Default]
[{_} 
[s2;%% U`+`+ Core Tutorial&]
[s22; Table of contents&]
[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; &]
[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. 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. 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. 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. 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 CoWork loop method]&]
[s0; ___[^`#Section`_7`_8^ 7.8 Parallel algorithms]&]
[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 
and examine results. In this tutorial we will be using logging 
extensively, so let us start tutorial with the explanation of 
logging.&]
[s5; In debug mode and with default settings, macro [*C@5 LOG] puts 
string into output log file. Log file is placed into `'config`-directory`', 
which by default is .exe directory in Win32 and `~/.upp/appname 
in POSIX.&]
[s5; In TheIDE, you can access the log using `'Debug`'/`'View the 
log file Alt`+L`'.&]
[s0; &]
[s7; LOG(`"Hello world`");&]
[s0; &]
[s17; Hello world&]
[s0; &]
[s5; You can log values of various types, as long as they have [*C@5 AsString] 
function defined You can chain values in single [*C@5 LOG] using 
[*C@5 operator<<]:&]
[s0; &]
[s7; int x `= 123;&]
[s7; LOG(`"Value of x is `" << x);&]
[s0; &]
[s17; Value of x is 123&]
[s0; &]
[s5; As it is very common to log a value of single variable, [*C@5 DUMP] 
macro provides a useful shortcut, creating a log line with the 
variable name and value:&]
[s0; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= 123&]
[s0; &]
[s5; To get the value in hexadecimal code, you can use [*C@5 LOGHEX] 
/ [*C@5 DUMPHEX]&]
[s0; &]
[s7; DUMPHEX(x);&]
[s7; String h `= `"foo`";&]
[s7; DUMPHEX(h);&]
[s0; &]
[s17; x `= 0x7b&]
[s17; h `= Memory at 0x000000E30D31FA10, size 0x3 `= 3&]
[s17;     `+0 0x000000E30D31FA10 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]:&]
[s0; &]
[s7; Vector<int> v `= `{ 1, 2, 3 `};&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[1, 2, 3`]&]
[s0; &]
[s5; or you can use DUMPC for multi`-line output:&]
[s0; &]
[s7; DUMPC(v);&]
[s0; &]
[s17; v:&]
[s17; -|`[0`] `= 1&]
[s17; -|`[1`] `= 2&]
[s17; -|`[2`] `= 3&]
[s0; &]
[s5; For maps, use DUMPM:&]
[s0; &]
[s7; VectorMap<int, String> map `= `{ `{ 1, `"one`" `}, `{ 2, `"two`" 
`} `};&]
[s7; &]
[s7; DUMP(map);&]
[s0; &]
[s17; map `= `{1: one, 2: two`}&]
[s0; &]
[s0; &]
[s7; DUMPM(map);&]
[s0; &]
[s17; map:&]
[s17; -|`[0`] `= (1) one&]
[s17; -|`[1`] `= (2) two&]
[s0; &]
[s5; All normal [*C@5 LOG]s are removed in release mode. If you need 
to log things in release mode, you need to use [*C@5 LOG]/``DUMP`` 
variant with `'[*C@5 R]`' prefix ([*C@5 RLOG], [*C@5 RDUMP], [*C@5 RDUMPHEX]...):&]
[s0; &]
[s7; RLOG(`"This will be logged in release mode too!`");&]
[s0; &]
[s17; This will be logged in release mode too!&]
[s0; &]
[s5; Sort of opposite situation is when adding temporary [*C@5 LOG]s 
to the code for debugging. In that case, `'[*C@5 D]`' prefixed 
variants ([*C@5 DLOG], [*C@5 DDUMP], [*C@5 DDUMPHEX]...) are handy 
`- these cause compile error in release mode, so will not get 
forgotten in the code past the release:&]
[s0; &]
[s7; DLOG(`"This would not compile in release mode.`");&]
[s0; &]
[s17; This would not compile in release mode.&]
[s0; &]
[s5; The last flavor of [*C@5 LOG] you can encounter while reading 
U`+`+ sources is the one prefixed with `'[*C@5 L]`'. This one is 
not actually defined in U`+`+ library and is just a convention. 
On the start of file, there is usually something like:&]
[s0; &]
[s7; #define LLOG(x) // DLOG(x)&]
[s0; &]
[s5; and by uncommenting the body part, you can activate the logging 
in that particular file.&]
[s5; While logging to .log file is default, there are various ways 
how to affect logging, for example following line adjusts logging 
to output the log both to the console and .log file:&]
[s0; &]
[s7; StdLogSetup(LOG`_COUT`|LOG`_FILE);&]
[s0; &]
[s3;H4;:Section`_1`_2: 1.2 String&]
[s5; String is a value type useful for storing text or binary data.&]
[s0; &]
[s7; String a `= `"Hello`";&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= Hello&]
[s0; &]
[s5; You can concatenate it with another String or literal:&]
[s0; &]
[s7; a `= a `+ `" world`";&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= Hello world&]
[s0; &]
[s5; Or single character or specified number of characters from another 
[*C@5 String] or literal:&]
[s0; &]
[s7; a.Cat(`'!`');&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= Hello world!&]
[s0; &]
[s0; &]
[s7; a.Cat(`"ABCDEFGHIJKLM`", 3);&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= Hello world!ABC&]
[s0; &]
[s5; [*C@5 Clear] method empties the String:&]
[s0; &]
[s7; a.Clear();&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= &]
[s0; &]
[s5; You can use [*C@5 operator<<] to append to existing [*C@5 String]. 
Non`-string values are converted to appropriate [*C@5 String] representation 
(using standard function [*C@5 AsString], whose default template 
definition calls [*C@5 ToString] method for value):&]
[s0; &]
[s7; for(int i `= 0; i < 10; i`+`+)&]
[s7; -|a << i << `", `";&]
[s7; &]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &]
[s0; &]
[s5; Sometimes is is useful to use [*C@5 operator<<] to produce a temporary 
[*C@5 String] value (e.g. as real argument to function call):&]
[s0; &]
[s7; String b `= String() << `"Number is `" << 123 << `".`";&]
[s7; &]
[s7; DUMP(b);&]
[s0; &]
[s17; b `= Number is 123.&]
[s0; &]
[s5; String provides many various methods for obtaining character 
count, inserting characters into [*C@5 String] or removing them:&]
[s0; &]
[s7; a `= `"0123456789`";&]
[s7; &]
[s7; DUMP(a.GetCount());&]
[s0; &]
[s17; a.GetCount() `= 10&]
[s0; &]
[s0; &]
[s7; DUMP(a.GetLength()); // GetLength is a synonym of GetCount&]
[s0; &]
[s17; a.GetLength() `= 10&]
[s0; &]
[s0; &]
[s7; a.Insert(6, `"<inserted>`");&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= 012345<inserted>6789&]
[s0; &]
[s0; &]
[s7; a.Remove(2, 2);&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= 0145<inserted>6789&]
[s0; &]
[s5; as well as searching and comparing methods:&]
[s0; &]
[s7; DUMP(a.Find(`'e`'));&]
[s7; DUMP(a.ReverseFind(`'e`'));&]
[s0; &]
[s17; a.Find(`'e`') `= 8&]
[s17; a.ReverseFind(`'e`') `= 11&]
[s0; &]
[s0; &]
[s7; DUMP(a.Find(`"ins`"));&]
[s0; &]
[s17; a.Find(`"ins`") `= 5&]
[s0; &]
[s0; &]
[s7; DUMP(a.StartsWith(`"ABC`"));&]
[s7; DUMP(a.StartsWith(`"01`"));&]
[s7; DUMP(a.EndsWith(`"89`"));&]
[s0; &]
[s17; a.StartsWith(`"ABC`") `= false&]
[s17; a.StartsWith(`"01`") `= true&]
[s17; a.EndsWith(`"89`") `= true&]
[s0; &]
[s5; You can get slice of String using Mid method; with single parameter 
it provides slice to the end of String:&]
[s0; &]
[s7; DUMP(a.Mid(3, 3));&]
[s7; DUMP(a.Mid(3));&]
[s0; &]
[s17; a.Mid(3, 3) `= 5<i&]
[s17; a.Mid(3) `= 5<inserted>6789&]
[s0; &]
[s5; You can also trim the length of String using Trim (this is faster 
than using any other method):&]
[s0; &]
[s7; a.Trim(4);&]
[s7; DUMP(a);&]
[s0; &]
[s17; a `= 0145&]
[s0; &]
[s5; You can obtain integer values of individual characters using 
operator`[`]:&]
[s0; &]
[s7; DUMP(a`[0`]);&]
[s0; &]
[s17; a`[0`] `= 48&]
[s0; &]
[s5; or the value of first character using operator`* (note that 
if [*C@5 GetCount() `=`= 0], this will return zero terminator):&]
[s0; &]
[s7;     DUMP(`*a);&]
[s0; &]
[s17; `*a `= 48&]
[s0; &]
[s0; &]
[s7;     a.Clear();&]
[s7;     &]
[s7;     DUMP(`*a);&]
[s0; &]
[s17; `*a `= 0&]
[s0; &]
[s5; [*C@5 String] has implicit cast to zero terminated [*C@5 const char 
`*ptr] (only valid as long as [*C@5 String] does not mutate:&]
[s0; &]
[s7; a `= `"1234`";&]
[s7; const char `*s `= a;&]
[s7; while(`*s)&]
[s7; -|LOG(`*s`+`+);&]
[s0; &]
[s17; 1&]
[s17; 2&]
[s17; 3&]
[s17; 4&]
[s0; &]
[s5; [*C@5 String] also has standard [*C@5 begin] [*C@5 end] methods, which 
e.g. allows for C`+`+11 [*C@5 for]:&]
[s0; &]
[s7; for(char ch : a)&]
[s7; -|LOG(ch);&]
[s0; &]
[s17; 1&]
[s17; 2&]
[s17; 3&]
[s17; 4&]
[s0; &]
[s5; It is absolutely OK and common to use String for storing binary 
data, including zeroes:&]
[s0; &]
[s7; a.Cat(0);&]
[s7; &]
[s7; DUMPHEX(a);&]
[s0; &]
[s17; a `= Memory at 0x000000E30D31F970, size 0x5 `= 5&]
[s17;     `+0 0x000000E30D31F970 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 
character buffer, you can use complementary [*C@5 StringBuffer] 
class. One of reasons to do so is when you have to deal with 
some C`-API functions that expects to write directly to [*C@5 char 
`*] and you would like that result converted to the [*C@5 String]:&]
[s0; &]
[s7; void CApiFunction(char `*c)&]
[s7; `{&]
[s7; -|strcpy(c, `"Hello`");&]
[s7; `}&]
[s7; &]
[s7; StringBuffer b;&]
[s7; b.SetLength(200);&]
[s7; CApiFunction(b);&]
[s7; b.Strlen();&]
[s7; String x `= b;&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= Hello&]
[s0; &]
[s5; In this case, [*C@5 SetLength] creates a C array of 200 characters. 
You can then call C`-API function. Later you set the real length 
using [*C@5 Strlen] `- this function performs strlen of buffer 
and sets the length accordingly. Later you simply assign the 
[*C@5 StringBuffer] to [*C@5 String]. Note that for performance reasons, 
this operation clears the [*C@5 StringBuffer] content (operation 
is fast and does not depend on the number of characters).&]
[s5; Another usage scenario of StringBuffer is altering existing 
String:&]
[s0; &]
[s7; b `= x;&]
[s7; b`[1`] `= `'a`';&]
[s7; x `= b;&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= Hallo&]
[s0; &]
[s5; Similar to assigning StringBuffer to String, assigning String 
to StringBuffer clears the source String.&]
[s5; StringBuffer also provides appending operations:&]
[s0; &]
[s7; b `= x;&]
[s7; b.Cat(`'!`');&]
[s7; x `= b;&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= Hallo!&]
[s0; &]
[s5; Note that sometimes when creating some String from a lot of 
single characters, using StringBuffer for the operation is slightly 
faster then using String directly.&]
[s3;H4;:Section`_1`_4: 1.4 WString&]
[s5; String works with 8 bit characters. For 16`-bit character encoding 
use [*C@5 WString]. Both classes are closely related and share 
most of interface methods. U`+`+ also provides conversions between 
[*C@5 String] and [*C@5 WString] and you can also use 8 bit string 
literals with [*C@5 WString]. Conversion is ruled by current default 
character set. Default value of default character set is [*C@5 CHARSET`_UTF8]. 
This conversion is also used in [*C@5 WString`::ToString], e.g. 
when putting [*C@5 WString] to log:&]
[s0; &]
[s7; WString x `= `"characters 280`-300: `"; // you can assign 8`-bit 
character literal to WString&]
[s7; for(int i `= 280; i < 300; i`+`+)&]
[s7; -|x.Cat(i);&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= characters 280`-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī&]
[s0; &]
[s5; [*C@5 ToString] converts [*C@5 WString] to [*C@5 String]:&]
[s0; &]
[s7; String y `= x.ToString();&]
[s7; DUMP(y);&]
[s0; &]
[s17; y `= characters 280`-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī&]
[s0; &]
[s5; [*C@5 ToWString] converts [*C@5 String] to [*C@5 WString]:&]
[s0; &]
[s7; y.Cat(`" (appended)`"); // you can use 8`-bit character literals 
in most WString operations&]
[s7; x `= y.ToWString();&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= characters 280`-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī 
(appended)&]
[s0; &]
[s3;H4;:Section`_1`_5: 1.5 Date and Time&]
[s5; To represent date and time, U`+`+ provides [*C@5 Date] and [*C@5 Time] 
concrete types.&]
[s0; &]
[s7; Date date `= GetSysDate();&]
[s7; &]
[s7; DUMP(date);&]
[s0; &]
[s17; date `= 02/09/2018&]
[s0; &]
[s5; All data members of [*C@5 Date] structure are public:&]
[s0; &]
[s7; DUMP((int)date.year); // we need to cast to int because some 
date members&]
[s7; DUMP((int)date.month); // are of unsigned character type which 
would log&]
[s7; DUMP((int)date.day); // as characters&]
[s0; &]
[s17; (int)date.year `= 2018&]
[s17; (int)date.month `= 2&]
[s17; (int)date.day `= 9&]
[s0; &]
[s5; Dates can be compared:&]
[s0; &]
[s7; DUMP(date > Date(2000, 1, 1));&]
[s0; &]
[s17; date > Date(2000, 1, 1) `= true&]
[s0; &]
[s5; Adding a number to [*C@5 Date] adds a number of days to it, incrementing/decrement
ing goes to the next/previous day:&]
[s0; &]
[s7; DUMP(date `+ 1);&]
[s7; DUMP(`-`-date);&]
[s7; DUMP(`+`+date);&]
[s0; &]
[s17; date `+ 1 `= 02/10/2018&]
[s17; `-`-date `= 02/08/2018&]
[s17; `+`+date `= 02/09/2018&]
[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) `= 6614&]
[s0; &]
[s5; There are several [*C@5 Date] and calendar related functions:&]
[s0; &]
[s7; DUMP(IsLeapYear(2012));&]
[s7; DUMP(IsLeapYear(2014));&]
[s7; DUMP(IsLeapYear(2015));&]
[s7; DUMP(IsLeapYear(2016));&]
[s7; DUMP(IsLeapYear(2017));&]
[s0; &]
[s17; IsLeapYear(2012) `= true&]
[s17; IsLeapYear(2014) `= false&]
[s17; IsLeapYear(2015) `= false&]
[s17; IsLeapYear(2016) `= true&]
[s17; IsLeapYear(2017) `= false&]
[s0; &]
[s0; &]
[s7; DUMP(GetDaysOfMonth(2, 2015));&]
[s7; DUMP(GetDaysOfMonth(2, 2016));&]
[s0; &]
[s17; GetDaysOfMonth(2, 2015) `= 28&]
[s17; GetDaysOfMonth(2, 2016) `= 29&]
[s0; &]
[s0; &]
[s7; DUMP(DayOfWeek(date)); // 0 is Sunday&]
[s0; &]
[s17; DayOfWeek(date) `= 5&]
[s0; &]
[s0; &]
[s7; DUMP(LastDayOfMonth(date));&]
[s7; DUMP(FirstDayOfMonth(date));&]
[s7; DUMP(LastDayOfYear(date));&]
[s7; DUMP(FirstDayOfYear(date));&]
[s7; DUMP(DayOfYear(date)); // number of days since Jan`-1 `+ 1&]
[s7; DUMP(DayOfYear(Date(2016, 1, 1)));&]
[s0; &]
[s17; LastDayOfMonth(date) `= 02/28/2018&]
[s17; FirstDayOfMonth(date) `= 02/01/2018&]
[s17; LastDayOfYear(date) `= 12/31/2018&]
[s17; FirstDayOfYear(date) `= 01/01/2018&]
[s17; DayOfYear(date) `= 40&]
[s17; DayOfYear(Date(2016, 1, 1)) `= 1&]
[s0; &]
[s0; &]
[s7; DUMP(AddMonths(date, 20));&]
[s7; DUMP(GetMonths(date, date `+ 100)); // number of `'whole months`' 
between two dates&]
[s7; DUMP(GetMonthsP(date, date `+ 100)); // number of `'whole or 
partial months`' between two dates&]
[s7; DUMP(AddYears(date, 2));&]
[s0; &]
[s17; AddMonths(date, 20) `= 10/09/2019&]
[s17; GetMonths(date, date `+ 100) `= 3&]
[s17; GetMonthsP(date, date `+ 100) `= 4&]
[s17; AddYears(date, 2) `= 02/09/2020&]
[s0; &]
[s0; &]
[s7; DUMP(GetWeekDate(2015, 1));&]
[s7; int year;&]
[s7; DUMP(GetWeek(Date(2016, 1, 1), year)); // first day of year 
can belong to previous year&]
[s7; DUMP(year);&]
[s0; &]
[s17; GetWeekDate(2015, 1) `= 12/29/2014&]
[s17; GetWeek(Date(2016, 1, 1), year) `= 53&]
[s17; year `= 2015&]
[s0; &]
[s0; &]
[s7; DUMP(EasterDay(2015));&]
[s7; DUMP(EasterDay(2016));&]
[s0; &]
[s17; EasterDay(2015) `= 04/05/2015&]
[s17; EasterDay(2016) `= 03/27/2016&]
[s0; &]
[s5; U`+`+ defines the beginning and the end of era, most algorithms 
can safely assume that as minimal and maximal values [*C@5 Date] 
can represent:&]
[s0; &]
[s7; DUMP(Date`::Low());&]
[s7; DUMP(Date`::High());&]
[s0; &]
[s17; Date`::Low() `= 01/01/`-4000&]
[s17; Date`::High() `= 01/01/4000&]
[s0; &]
[s5; Time is derived from [*C@5 Date], adding members to represent 
time:&]
[s0; &]
[s7; Time time `= GetSysTime();&]
[s7; DUMP(time);&]
[s7; DUMP((Date)time);&]
[s7; DUMP((int)time.hour);&]
[s7; DUMP((int)time.minute);&]
[s7; DUMP((int)time.second);&]
[s0; &]
[s17; time `= 02/09/2018 12:56:36&]
[s17; (Date)time `= 02/09/2018&]
[s17; (int)time.hour `= 12&]
[s17; (int)time.minute `= 56&]
[s17; (int)time.second `= 36&]
[s0; &]
[s5; Times can be compared:&]
[s0; &]
[s7; DUMP(time > Time(1970, 0, 0));&]
[s0; &]
[s17; time > Time(1970, 0, 0) `= true&]
[s0; &]
[s5; Warning: As [*C@5 Time] is derived from the [*C@5 Date], most operations 
automatically convert [*C@5 Time] back to [*C@5 Date]. You have to 
use [*C@5 ToTime] conversion function to convert [*C@5 Date] to [*C@5 Time]:&]
[s0; &]
[s7; DUMP(time > date); // time gets converted to Date...&]
[s7; DUMP(time > ToTime(date));&]
[s0; &]
[s17; time > date `= false&]
[s17; time > ToTime(date) `= true&]
[s0; &]
[s5; Like [*C@5 Date], [*C@5 Time] supports add and subtract operations, 
but numbers represent seconds (using [*C@5 int64] datatype):&]
[s0; &]
[s7; DUMP(time `+ 1);&]
[s7; DUMP(time `+ 24 `* 3600);&]
[s7; DUMP(time `- date); // time converts to Date, so the result 
is in days&]
[s7; DUMP(time `- ToTime(date)); // Time `- Time is in seconds&]
[s0; &]
[s17; time `+ 1 `= 02/09/2018 12:56:37&]
[s17; time `+ 24 `* 3600 `= 02/10/2018 12:56:36&]
[s17; time `- date `= 0&]
[s17; time `- ToTime(date) `= 46596&]
[s0; &]
[s5; [*C@5 Time] defines era limits too:&]
[s0; &]
[s7; DUMP(Time`::Low());&]
[s7; DUMP(Time`::High());&]
[s0; &]
[s17; Time`::Low() `= 01/01/`-4000 00:00:00&]
[s17; Time`::High() `= 01/01/4000 00:00:00&]
[s0; &]
[s3;H4;:Section`_1`_6: 1.6 [C@5 AsString], [C@5 ToString] and [C@5 operator<<]&]
[s5; U`+`+ Core provides simple yet effective standard schema for 
converting values to default textual form. System is based on 
the combination of template functions (following code is part 
of U`+`+ library):&]
[s0; &]
[s7; namespace Upp `{&]
[s7; -|template <class T>&]
[s7; -|inline String AsString(const T`& x)&]
[s7; -|`{&]
[s7; -|-|return x.ToString();&]
[s7; -|`}&]
[s7; -|&]
[s7; -|template <class T>&]
[s7; -|inline Stream`& operator<<(Stream`& s, const T`& x)&]
[s7; -|`{&]
[s7; -|-|s << AsString(x);&]
[s7; -|-|return s;&]
[s7; -|`}&]
[s7; -|&]
[s7; -|template <class T>&]
[s7; -|inline String`& operator<<(String`& s, const T`& x)&]
[s7; -|`{&]
[s7; -|-|s.Cat(AsString(x));&]
[s7; -|-|return s;&]
[s7; -|`}&]
[s7; `};&]
[s0; &]
[s5; Client types have to either define [*C@5 String ToString] method 
or specialize [*C@5 AsString] template in [*C@5 Upp] namespace. Such 
types can be appended to Streams or Strings using [*C@5 operator<<]. 
Of course, U`+`+ value types and primitive types have required 
items predefined by U`+`+:&]
[s0; &]
[s7; FileOut fout(ConfigFile(`"test.txt`"));&]
[s7; String  sout;&]
[s7; &]
[s7; fout << 1.23 << `' `' << GetSysDate() << `' `' << GetSysTime();&]
[s7; sout << 1.23 << `' `' << GetSysDate() << `' `' << GetSysTime();&]
[s7; &]
[s7; fout.Close();&]
[s7; &]
[s7; DUMP(LoadFile(ConfigFile(`"test.txt`")));&]
[s7; DUMP(sout);&]
[s0; &]
[s17; LoadFile(ConfigFile(`"test.txt`")) `= 1.23 02/09/2018 02/09/2018 
12:56:36&]
[s17; sout `= 1.23 02/09/2018 02/09/2018 12:56:36&]
[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:&]
[s0; &]
[s7; struct BinFoo `{&]
[s7; -|int x;&]
[s7; -|&]
[s7; -|String ToString() const   `{ return FormatIntBase(x, 2); `}&]
[s7; `};&]
[s7; &]
[s7; BinFoo bf;&]
[s7; bf.x `= 30;&]
[s7; &]
[s7; sout.Clear();&]
[s7; sout << bf;&]
[s7; DUMP(sout);&]
[s0; &]
[s17; sout `= 11110&]
[s0; &]
[s5; If you cannot add [*C@5 ToString], you can still specialize template 
in Upp namespace:&]
[s0; &]
[s7; struct RomanFoo `{&]
[s7; -|int x;&]
[s7; -|&]
[s7; -|RomanFoo(int x) : x(x) `{`}&]
[s7; `};&]
[s7; &]
[s7; namespace Upp `{&]
[s7; template <> String Upp`::AsString(const RomanFoo`& a) `{ return 
FormatIntRoman(a.x); `}&]
[s7; `};&]
[s0; &]
[s3;H4;:Section`_1`_7: 1.7 CombineHash&]
[s5; To simplify providing of high quality hash codes for composite 
types, U`+`+ provides [*C@5 CombineHash] utility class. This class 
uses [*C@5 GetHashValue] function to gather hash codes of all values 
and combines them to provide final hash value for composite type:&]
[s0; &]
[s7; struct Foo `{&]
[s7; -|String a;&]
[s7; -|int    b;&]
[s7; -|&]
[s7; -|unsigned GetHashValue() const `{ return CombineHash(a, b); 
`}&]
[s7; `};&]
[s0; &]
[s5; Note that [*C@5 GetHashValue] is defined as function template 
that calls [*C@5 GetHashValue] method of its argument, therefore 
defining [*C@5 GetHashValue] method defines [*C@5 GetHashValue] function 
too:&]
[s0; &]
[s7; Foo x;&]
[s7; x.a `= `"world`";&]
[s7; x.b `= 22;&]
[s7; &]
[s7; DUMP(GetHashValue(x));&]
[s0; &]
[s17; GetHashValue(x) `= 4272824901&]
[s0; &]
[s0; &]
[s7; x.a << `'!`';&]
[s7; &]
[s7; DUMP(GetHashValue(x));&]
[s0; &]
[s17; GetHashValue(x) `= 3378606405&]
[s0; &]
[s3;H4;:Section`_1`_8: 1.8 SgnCompare and CombineCompare&]
[s5; Traditional approach of C language of representing comparison 
results was 3`-state: comparing a and b results in negative value 
(if a < b), zero (if a `=`= b) or positive value (a > b). In 
C`+`+ standard library, comparisons are usually represented with 
[*C@5 bool] predicates.&]
[s5; However, with [*C@5 bool] predicate it becomes somewhat more difficult 
to provide comparisons for composite types:&]
[s0; &]
[s7; struct Foo `{&]
[s7; -|String a;&]
[s7; -|int    b;&]
[s7; -|int    c;&]
[s7; -|&]
[s7; -|// we want to order Foo instances by a first, then b, then 
c&]
[s7; -|&]
[s7; -|bool operator<(const Foo`& x) const `{&]
[s7; -|-|return a < x.a ? true&]
[s7; -|-|               : a `=`= x.a ? b < x.b ? true&]
[s7; -|-|                          : b `=`= x.b ? false&]
[s7; -|-|                                     : c < x.c&]
[s7; -|-|               : false;&]
[s7; -|`}&]
[s7; `};&]
[s0; &]
[s5; U`+`+ provides standard function [*C@5 SgnCompare], which returns 
negative value/zero/positive in `"C style`":&]
[s0; &]
[s7; int a `= 1;&]
[s7; int b `= 2;&]
[s7; &]
[s7; DUMP(SgnCompare(a, b));&]
[s7; DUMP(SgnCompare(b, a));&]
[s7; DUMP(SgnCompare(a, a));&]
[s0; &]
[s17; SgnCompare(a, b) `= `-1&]
[s17; SgnCompare(b, a) `= 1&]
[s17; SgnCompare(a, a) `= 0&]
[s0; &]
[s5; Default implementation of [*C@5 SgnCompare] calls [*C@5 Compare] 
method of value:&]
[s0; &]
[s7; struct MyClass `{&]
[s7; -|int val;&]
[s7; -|&]
[s7; -|int Compare(const MyClass`& x) const `{ return SgnCompare(val, 
x.val); `}&]
[s7; `};&]
[s0; &]
[s5; [*C@5 SgnCompare] is now defined for [*C@5 MyClass]:&]
[s0; &]
[s7; MyClass u, v;&]
[s7; u.val `= 1;&]
[s7; v.val `= 2;&]
[s7; &]
[s7; DUMP(SgnCompare(u, v));&]
[s7; DUMP(SgnCompare(v, u));&]
[s7; DUMP(SgnCompare(v, v));&]
[s0; &]
[s17; SgnCompare(u, v) `= `-1&]
[s17; SgnCompare(v, u) `= 1&]
[s17; SgnCompare(v, v) `= 0&]
[s0; &]
[s5; Now getting back to [*C@5 Foo], with [*C@5 SgnCompare] [*C@5 operator<] 
becomes much less difficult:&]
[s0; &]
[s7; struct Foo2 `{&]
[s7; -|String a;&]
[s7; -|int    b;&]
[s7; -|int    c;&]
[s7; -|&]
[s7; -|bool operator<(const Foo2`& x) const `{&]
[s7; -|-|int q `= SgnCompare(a, x.a);&]
[s7; -|-|if(q) return q < 0;&]
[s7; -|-|q `= SgnCompare(b, x.b);&]
[s7; -|-|if(q) return q < 0;&]
[s7; -|-|q `= SgnCompare(c, x.c);&]
[s7; -|-|return q < 0;&]
[s7; -|`}&]
[s7; `};&]
[s0; &]
[s5; Alternatively, it is possible to define just [*C@5 Compare] method 
and use [*C@5 Comparable] [^https`:`/`/en`.wikipedia`.org`/wiki`/Curiously`_recurring`_template`_pattern^ C
RTP idiom] to define all relation operators:&]
[s0; &]
[s7; struct Foo3 : Comparable<Foo3> `{&]
[s7; -|String a;&]
[s7; -|int    b;&]
[s7; -|int    c;&]
[s7; -|&]
[s7; -|int Compare(const Foo3`& x) const `{&]
[s7; -|-|int q `= SgnCompare(a, x.a);&]
[s7; -|-|if(q) return q;&]
[s7; -|-|q `= SgnCompare(b, x.b);&]
[s7; -|-|if(q) return q;&]
[s7; -|-|return SgnCompare(c, x.c);&]
[s7; -|`}&]
[s7; `};&]
[s7; &]
[s7; Foo3 m, n;&]
[s7; m.a `= `"A`";&]
[s7; m.b `= 1;&]
[s7; m.c `= 2;&]
[s7; n.a `= `"A`";&]
[s7; n.b `= 1;&]
[s7; n.c `= 3;&]
[s7; &]
[s7; DUMP(m < n);&]
[s7; DUMP(m `=`= n);&]
[s7; DUMP(m !`= n);&]
[s7; DUMP(SgnCompare(m, n));&]
[s0; &]
[s17; m < n `= true&]
[s17; m `=`= n `= false&]
[s17; m !`= n `= true&]
[s17; SgnCompare(m, n) `= `-1&]
[s0; &]
[s5; While the content of [*C@5 Compare] method is trivial, it can 
be further simplified using [*C@5 CombineCompare] helper class:&]
[s0; &]
[s7; struct Foo4 : Comparable<Foo4> `{&]
[s7; -|String a;&]
[s7; -|int    b;&]
[s7; -|int    c;&]
[s7; -|&]
[s7; -|int Compare(const Foo4`& x) const `{&]
[s7; -|-|return CombineCompare(a, x.a)(b, x.b)(c, x.c);&]
[s7; -|`}&]
[s7; `};&]
[s7; &]
[s7; Foo4 o, p;&]
[s7; o.a `= `"A`";&]
[s7; o.b `= 1;&]
[s7; o.c `= 2;&]
[s7; p.a `= `"A`";&]
[s7; p.b `= 1;&]
[s7; p.c `= 3;&]
[s7; &]
[s7; DUMP(o < p);&]
[s7; DUMP(o `=`= p);&]
[s7; DUMP(o !`= p);&]
[s7; DUMP(SgnCompare(o, p));&]
[s0; &]
[s17; o < p `= true&]
[s17; o `=`= p `= false&]
[s17; o !`= p `= true&]
[s17; SgnCompare(o, p) `= `-1&]
[s0; &]
[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 0x000000E30D31F770, size 0x8 `= 
8&]
[s17;     `+0 0x000000E30D31F770 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 0x0000025E6A3C0360, 
size 0x14 `= 20&]
[s17;     `+0 0x0000025E6A3C0360 54 68 69 73 20 69 73 20 61 20 74 
65 73 74 20 31     This is a test 1&]
[s17;    `+16 0x0000025E6A3C0370 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 0x000000E30D31F3D8, size 0x9 `= 9&]
[s17;     `+0 0x000000E30D31F3D8 53 6F 6D 65 20 6C 69 6E 65       
                   Some line       &]
[s17; ms.GetLine() `= Memory at 0x000000E30D31F3D8, size 0xC `= 12&]
[s17;     `+0 0x000000E30D31F3D8 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 `= 1782092368&]
[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 
[*C@5 Vector] that allow its implementation to move elements in 
memory using plain [*C@5 memcpy]/``memmove`` (`"Moveable`" concept).&]
[s5; Anyway, for now let us start with simple [*C@5 Vector] of [*C@5 int]s:&]
[s0; &]
[s7; -|Vector<int> v;&]
[s0; &]
[s5; You can add elements to the Vector as parameters of the Add 
method&]
[s0; &]
[s7; -|v.Add(1);&]
[s7; -|v.Add(2);&]
[s7; -|&]
[s7; -|DUMP(v);&]
[s0; &]
[s17; v `= `[1, 2`]&]
[s0; &]
[s5; Alternative and very important possibility for U`+`+ containers 
is `'in`-place creation`'. In this case, parameter`-less Add 
returns a reference to a new element in [*C@5 Vector]:&]
[s0; &]
[s7; -|v.Add() `= 3;&]
[s7; -|&]
[s7; -|DUMP(v);&]
[s0; &]
[s17; v `= `[1, 2, 3`]&]
[s0; &]
[s5; You can also use [*C@5 operator<<]&]
[s0; &]
[s7; -|v << 4 << 5;&]
[s7; &]
[s7; -|DUMP(v);&]
[s0; &]
[s17; v `= `[1, 2, 3, 4, 5`]&]
[s0; &]
[s5; [*C@5 Vector] also supports initializer lists:&]
[s0; &]
[s7; -|v.Append(`{ 6, 7 `});&]
[s7; &]
[s7; -|DUMP(v);&]
[s0; &]
[s17; v `= `[1, 2, 3, 4, 5, 6, 7`]&]
[s0; &]
[s5; To iterate [*C@5 Vector] you can use indices:&]
[s0; &]
[s7; -|for(int i `= 0; i < v.GetCount(); i`+`+)&]
[s7; -|-|LOG(v`[i`]);&]
[s0; &]
[s17; 1&]
[s17; 2&]
[s17; 3&]
[s17; 4&]
[s17; 5&]
[s17; 6&]
[s17; 7&]
[s0; &]
[s5; begin/end interface:&]
[s0; &]
[s7; -|for(auto q `= v.begin(), e `= v.end(); q !`= e; q`+`+)&]
[s7; -|-|LOG(`*q);&]
[s0; &]
[s17; 1&]
[s17; 2&]
[s17; 3&]
[s17; 4&]
[s17; 5&]
[s17; 6&]
[s17; 7&]
[s0; &]
[s5; C`+`+11 range`-for syntax:&]
[s0; &]
[s7; -|for(const auto`& q : v)&]
[s7; -|-|LOG(q);&]
[s0; &]
[s17; 1&]
[s17; 2&]
[s17; 3&]
[s17; 4&]
[s17; 5&]
[s17; 6&]
[s17; 7&]
[s0; &]
[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; &]
[s7; Vector<int> v;&]
[s7; v.Add(1);&]
[s7; v.Add(2);&]
[s7; &]
[s7; v.Insert(1, 10);&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[1, 10, 2`]&]
[s0; &]
[s0; &]
[s7; v.Insert(0, `{ 7, 6, 5 `});&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[7, 6, 5, 1, 10, 2`]&]
[s0; &]
[s0; &]
[s7; v.Remove(0);&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[6, 5, 1, 10, 2`]&]
[s0; &]
[s5; [*C@5 At] method returns element at specified position ensuring 
that such a position exists. If there is not enough elements 
in [*C@5 Vector], required number of elements is added. If second 
parameter of [*C@5 At] is present, newly added elements are initialized 
to this value.&]
[s0; &]
[s7; v.Clear();&]
[s7; for(int i `= 0; i < 10000; i`+`+)&]
[s7; -|v.At(Random(10), 0)`+`+;&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[1027, 975, 1030, 1035, 977, 964, 972, 1023, 996, 1001`]&]
[s0; &]
[s5; Referencing invalid index is undefined operation. Sometimes 
however it is useful to return the element value if index is 
valid and some default value if it is not. This can be achieved 
with two parameter Get method:&]
[s0; &]
[s7; DUMP(v.Get(4, 0));&]
[s7; DUMP(v.Get(`-10, 0));&]
[s7; DUMP(v.Get(13, `-1));&]
[s0; &]
[s17; v.Get(4, 0) `= 977&]
[s17; v.Get(`-10, 0) `= 0&]
[s17; v.Get(13, `-1) `= `-1&]
[s0; &]
[s5; You can apply algorithms on containers, e.g. Sort&]
[s0; &]
[s7; Sort(v);&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[964, 972, 975, 977, 996, 1001, 1023, 1027, 1030, 1035`]&]
[s0; &]
[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 
stored, also might support [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ clone 
semantics]. When transferring the value, you have to explicitly 
specify which one to use:&]
[s0; &]
[s7; Vector<int> v`{ 1, 2 `};&]
[s7; &]
[s7; DUMP(v);&]
[s7; &]
[s7; Vector<int> v1 `= pick(v);&]
[s7; &]
[s7; DUMP(v);&]
[s7; DUMP(v1);&]
[s0; &]
[s17; v `= `[1, 2`]&]
[s17; v `= `[`]&]
[s17; v1 `= `[1, 2`]&]
[s0; &]
[s5; now source [*C@5 Vector] [*C@5 v] is empty, as elements were `'picked`' 
to [*C@5 v1].&]
[s5; If you really need to preserve value of source (and elements 
support deep copy operation), you can use [*C@5 clone]:&]
[s0; &]
[s7; v `= clone(v1);&]
[s7; &]
[s7; DUMP(v);&]
[s7; DUMP(v1);&]
[s0; &]
[s17; v `= `[1, 2`]&]
[s17; v1 `= `[1, 2`]&]
[s0; &]
[s5; The requirement of explicit [*C@5 clone] has the advantage of 
avoiding unexpected deep copies. For example:&]
[s0; &]
[s7; Vector<Vector<int>> x;&]
[s7; x.Add() << 1 << 2 << 3;&]
[s7; &]
[s7; for(auto i : x) `{ LOG(i); `}&]
[s0; &]
[s5; results in run`-time error, whereas the equivalent code with 
[*C@5 std`::vector] compiles but silently performs deep copy for 
each iteration:&]
[s0; &]
[s7; std`::vector<std`::vector<int>> sv;&]
[s7; sv.push`_back(`{1, 2, 3`});&]
[s7; for(auto i : sv) // invokes std`::vector<int> copy constructor&]
[s7; -|for(auto j : i)&]
[s7; -|-|DUMP(j);&]
[s0; &]
[s5; That said, in certain cases it is simpler to have default copy 
instead of explicit [*C@5 clone]. You can easily achieve that using 
[*C@5 WithDeepCopy] template:&]
[s0; &]
[s7; WithDeepCopy<Vector<int>> v2;&]
[s7; &]
[s7; v2 `= v;&]
[s7; &]
[s7; DUMP(v);&]
[s7; DUMP(v2);&]
[s0; &]
[s17; v `= `[1, 2`]&]
[s17; v2 `= `[1, 2`]&]
[s0; &]
[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] 
requirement `- in short, it must not contain back`-pointers nor 
virtual methods. Type must be marked as [/ moveable] in order to 
define interface contract using [*C@5 Moveable] [^https`:`/`/en`.wikipedia`.org`/wiki`/Curiously`_recurring`_template`_pattern^ C
RTP idiom]:&]
[s0; &]
[s7; struct Distribution : Moveable<Distribution> `{&]
[s7; -|String      text;&]
[s7; -|Vector<int> data;&]
[s7; -|&]
[s7; -|String ToString() const `{ return text `+ `": `" `+ AsString(data); 
`}&]
[s7; `};&]
[s0; &]
[s5; Now to add [*C@5 Distribution] elements you cannot use [*C@5 Vector`::Add(const 
T`&)], because it requires elements to have default deep`-copy 
constructor `- and [*C@5 Distribution does not have one, as ]Vector<int>`` 
has default pick`-constructor, so Distribution itself has pick`-constructor. 
It would no be a good idea either, because deep`-copy would involve 
expensive copying of inner Vector.&]
[s5; Instead, Add without parameters has to be used `- it default 
constructs (that is cheap) element in Vector and returns reference 
to it:&]
[s0; &]
[s7; Vector<Distribution> dist;&]
[s7; for(int n `= 5; n <`= 10; n`+`+) `{&]
[s7; -|Distribution`& d `= dist.Add();&]
[s7; -|d.text << `"Test `" << n;&]
[s7; -|for(int i `= 0; i < 10000; i`+`+)&]
[s7; -|-|d.data.At(Random(n), 0)`+`+;&]
[s7; `}&]
[s7; &]
[s7; DUMPC(dist);&]
[s0; &]
[s17; dist:&]
[s17; -|`[0`] `= Test 5: `[1995, 1951, 1997, 2092, 1965`]&]
[s17; -|`[1`] `= Test 6: `[1664, 1675, 1657, 1592, 1640, 1772`]&]
[s17; -|`[2`] `= Test 7: `[1377, 1491, 1405, 1446, 1354, 1492, 1435`]&]
[s17; -|`[3`] `= Test 8: `[1260, 1261, 1266, 1248, 1243, 1219, 1259, 
1244`]&]
[s17; -|`[4`] `= Test 9: `[1126, 1117, 1079, 1123, 1099, 1125, 1087, 
1081, 1163`]&]
[s17; -|`[5`] `= Test 10: `[1012, 1040, 933, 972, 993, 1036, 949, 1022, 
1031, 1012`]&]
[s0; &]
[s5; Another possibility is to use [*C@5 Vector`::Add(T`&`&)] method, 
which uses pick`-constructor instead of deep`-copy constructor. 
E.g. [*C@5 Distribution] elements might be generated by some function:&]
[s0; &]
[s7; Distribution CreateDist(int n);&]
[s0; &]
[s5; and code for adding such elements to Vector then looks like:&]
[s0; &]
[s7; for(n `= 5; n <`= 10; n`+`+)&]
[s7; -|dist.Add(CreateDist(n));&]
[s0; &]
[s5; alternatively, you can use default`-constructed variant too&]
[s0; &]
[s7; -|dist.Add() `= CreateDist();&]
[s0; &]
[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 
elements `- Array flavor never invalidates references or pointers 
to them. Finally, if sizeof(T) is large (say more than 100`-200 
bytes), using Array might be better from performance perspective.&]
[s5; Example of elements that cannot be stored in Vector flavor are 
standard library objects like [*C@5 std`::string] (because obviously, 
standard library knows nothing about U`+`+ Moveable concept):&]
[s0; &]
[s7; Array<std`::string> as;&]
[s7; for(int i `= 0; i < 4; i`+`+)&]
[s7; -|as.Add(`"Test`");&]
[s7; &]
[s7; for(auto s : as)&]
[s7; -|DUMP(s.c`_str());&]
[s0; &]
[s17; s.c`_str() `= Test&]
[s17; s.c`_str() `= Test&]
[s17; s.c`_str() `= Test&]
[s17; s.c`_str() `= Test&]
[s0; &]
[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 `{&]
[s7; -|virtual double Get() const `= 0;&]
[s7; -|String ToString() const `{ return AsString(Get()); `}&]
[s7; -|virtual `~Number() `{`}&]
[s7; `};&]
[s7; &]
[s7; struct Integer : public Number `{&]
[s7; -|int n;&]
[s7; -|virtual double Get() const `{ return n; `}&]
[s7; `};&]
[s7; &]
[s7; struct Double : public Number `{&]
[s7; -|double n;&]
[s7; -|virtual double Get() const `{ return n; `}&]
[s7; `};&]
[s0; &]
[s5; To add such derived types to [*C@5 Array], you can best use in`-place 
creation with [*C@5 Create] method:&]
[s0; &]
[s7; Array<Number> num;&]
[s7; num.Create<Double>().n `= 15.5;&]
[s7; num.Create<Integer>().n `= 3;&]
[s7; &]
[s7; DUMP(num);&]
[s0; &]
[s17; num `= `[15.5, 3`]&]
[s0; &]
[s5; Alternatively, you can use [*C@5 Add(T `*)] method and provide 
a pointer to the newly created instance on the heap ([*C@5 Add] 
returns a reference to the instance):&]
[s0; &]
[s7; Double `*nd `= new Double;&]
[s7; nd`->n `= 1.1;&]
[s7; num.Add(nd);&]
[s7; &]
[s7; DUMP(num);&]
[s0; &]
[s17; num `= `[15.5, 3, 1.1`]&]
[s0; &]
[s5; Array takes ownership of heap object and deletes it as appropriate. 
We recommend to use this variant only if in`-place creation with 
[*C@5 Create] is not possible.&]
[s5; It is OK do directly apply U`+`+ algorithms on [*C@5 Array] (the 
most stringent requirement of any of basic algorithms is that 
there is [*C@5 IterSwap] provided for container iterators and that 
is specialized for [*C@5 Array] iterators):&]
[s0; &]
[s7; Sort(num, `[`](const Number`& a, const Number`& b) `{ return 
a.Get() < b.Get(); `});&]
[s7; &]
[s7; DUMP(num);&]
[s0; &]
[s17; num `= `[1.1, 3, 15.5`]&]
[s0; &]
[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). 
[*C@5 BiVector] and [*C@5 BiArray] are optimal for this scenario:&]
[s0; &]
[s7; BiVector<int> n;&]
[s7; n.AddHead(1);&]
[s7; n.AddTail(2);&]
[s7; n.AddHead(3);&]
[s7; n.AddTail(4);&]
[s7; DUMP(n);&]
[s0; &]
[s17; n `= `[3, 1, 2, 4`]&]
[s0; &]
[s0; &]
[s7; n.DropHead();&]
[s7; DUMP(n);&]
[s0; &]
[s17; n `= `[1, 2, 4`]&]
[s0; &]
[s0; &]
[s7; n.DropTail();&]
[s7; DUMP(n);&]
[s0; &]
[s17; n `= `[1, 2`]&]
[s0; &]
[s0; &]
[s7; struct Val `{&]
[s7; -|virtual String ToString() const `= 0;&]
[s7; -|virtual `~Val() `{`}&]
[s7; `};&]
[s7; &]
[s7; struct Number : Val `{&]
[s7; -|int n;&]
[s7; -|virtual String ToString() const `{ return AsString(n); `}&]
[s7; `};&]
[s7; &]
[s7; struct Text : Val `{&]
[s7; -|String s;&]
[s7; -|virtual String ToString() const `{ return s; `}&]
[s7; `};&]
[s7; &]
[s7; BiArray<Val> num;&]
[s7; num.CreateHead<Number>().n `= 3;&]
[s7; num.CreateTail<Text>().s `= `"Hello`";&]
[s7; num.CreateHead<Text>().s `= `"World`";&]
[s7; num.CreateTail<Number>().n `= 2;&]
[s7; &]
[s7; DUMP(num);&]
[s0; &]
[s17; num `= `[World, 3, Hello, 2`]&]
[s0; &]
[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] 
(it is random access array of elements with fast addition at 
the end) with one additional feature `- it is able to fast retrieve 
position of element with required value using [*C@5 Find] method:&]
[s0; &]
[s7; Index<String> ndx;&]
[s7; ndx.Add(`"alfa`");&]
[s7; ndx.Add(`"beta`");&]
[s7; ndx.Add(`"gamma`");&]
[s7; ndx.Add(`"delta`");&]
[s7; ndx.Add(`"kappa`");&]
[s7; &]
[s7; DUMP(ndx);&]
[s7; DUMP(ndx.Find(`"beta`"));&]
[s0; &]
[s17; ndx `= `[alfa, beta, gamma, delta, kappa`]&]
[s17; ndx.Find(`"beta`") `= 1&]
[s0; &]
[s5; If element is not present in [*C@5 Index], [*C@5 Find] returns a 
negative value:&]
[s0; &]
[s7; DUMP(ndx.Find(`"something`"));&]
[s0; &]
[s17; ndx.Find(`"something`") `= `-1&]
[s0; &]
[s5; Any element can be replaced using [*C@5 Set] method:&]
[s0; &]
[s7; ndx.Set(1, `"alfa`");&]
[s7; &]
[s7; DUMP(ndx);&]
[s0; &]
[s17; ndx `= `[alfa, alfa, gamma, delta, kappa`]&]
[s0; &]
[s5; If there are more elements with the same value, they can be 
iterated using [*C@5 FindNext] method:&]
[s0; &]
[s7; int fi `= ndx.Find(`"alfa`");&]
[s7; while(fi >`= 0) `{&]
[s7; -|DUMP(fi);&]
[s7; -|fi `= ndx.FindNext(fi);&]
[s7; `}&]
[s0; &]
[s17; fi `= 0&]
[s17; fi `= 1&]
[s0; &]
[s5; [*C@5 FindAdd] method retrieves position of element like [*C@5 Find], 
but if element is not present in [*C@5 Index], it is added:&]
[s0; &]
[s7; DUMP(ndx.FindAdd(`"one`"));&]
[s7; DUMP(ndx.FindAdd(`"two`"));&]
[s7; DUMP(ndx.FindAdd(`"three`"));&]
[s7; DUMP(ndx.FindAdd(`"two`"));&]
[s7; DUMP(ndx.FindAdd(`"three`"));&]
[s7; DUMP(ndx.FindAdd(`"one`"));&]
[s0; &]
[s17; ndx.FindAdd(`"one`") `= 5&]
[s17; ndx.FindAdd(`"two`") `= 6&]
[s17; ndx.FindAdd(`"three`") `= 7&]
[s17; ndx.FindAdd(`"two`") `= 6&]
[s17; ndx.FindAdd(`"three`") `= 7&]
[s17; ndx.FindAdd(`"one`") `= 5&]
[s0; &]
[s5; Removing elements from random access sequence tends to be expensive, 
that is why rather than remove, [*C@5 Index] supports [*C@5 Unlink] 
and [*C@5 UnlinkKey] operations, which retain the element in [*C@5 Index] 
but make it invisible for [*C@5 Find] operation:&]
[s0; &]
[s7; ndx.Unlink(2);&]
[s7; ndx.UnlinkKey(`"kappa`");&]
[s7; &]
[s7; DUMP(ndx.Find(ndx`[2`]));&]
[s7; DUMP(ndx.Find(`"kappa`"));&]
[s0; &]
[s17; ndx.Find(ndx`[2`]) `= `-1&]
[s17; ndx.Find(`"kappa`") `= `-1&]
[s0; &]
[s5; You can test whether element at given position is unlinked using 
[*C@5 IsUnlinked] method&]
[s0; &]
[s7; DUMP(ndx.IsUnlinked(1));&]
[s7; DUMP(ndx.IsUnlinked(2));&]
[s0; &]
[s17; ndx.IsUnlinked(1) `= false&]
[s17; ndx.IsUnlinked(2) `= true&]
[s0; &]
[s5; Unlinked positions can be reused by [*C@5 Put] method:&]
[s0; &]
[s7; ndx.Put(`"foo`");&]
[s7; &]
[s7; DUMP(ndx);&]
[s7; DUMP(ndx.Find(`"foo`"));&]
[s0; &]
[s17; ndx `= `[alfa, alfa, foo, delta, kappa, one, two, three`]&]
[s17; ndx.Find(`"foo`") `= 2&]
[s0; &]
[s5; You can also remove all unlinked elements from [*C@5 Index] using 
[*C@5 Sweep] method:&]
[s0; &]
[s7; ndx.Sweep();&]
[s7; &]
[s7; DUMP(ndx);&]
[s0; &]
[s17; ndx `= `[alfa, alfa, foo, delta, one, two, three`]&]
[s0; &]
[s5; Operations directly removing or inserting elements of Index 
are expensive, but available too:&]
[s0; &]
[s7; ndx.Remove(1);&]
[s7; &]
[s7; DUMP(ndx);&]
[s0; &]
[s17; ndx `= `[alfa, foo, delta, one, two, three`]&]
[s0; &]
[s0; &]
[s7; ndx.RemoveKey(`"two`");&]
[s7; &]
[s7; DUMP(ndx);&]
[s0; &]
[s17; ndx `= `[alfa, foo, delta, one, three`]&]
[s0; &]
[s0; &]
[s7; ndx.Insert(0, `"insert`");&]
[s7; &]
[s7; DUMP(ndx);&]
[s0; &]
[s17; ndx `= `[insert, alfa, foo, delta, one, three`]&]
[s0; &]
[s5; PickKeys operation allows you to obtain Vector of elements of 
Index in low constant time operation (while destroying source 
Index)&]
[s0; &]
[s7; Vector<String> d `= ndx.PickKeys();&]
[s7; &]
[s7; DUMP(d);&]
[s0; &]
[s17; d `= `[insert, alfa, foo, delta, one, three`]&]
[s0; &]
[s5; Pick`-assigning [*C@5 Vector] to [*C@5 Index] is supported as well:&]
[s0; &]
[s7; d`[0`] `= `"test`";&]
[s7; &]
[s7; ndx `= pick(d);&]
[s7; &]
[s7; DUMP(ndx);&]
[s0; &]
[s17; ndx `= `[test, alfa, foo, delta, one, three`]&]
[s0; &]
[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 
code. It is recommended to use [*C@5 CombineHash] to combine hash 
values of types that already provide [*C@5 GetHashValue]:&]
[s0; &]
[s7; struct Person : Moveable<Person> `{&]
[s7; -|String name;&]
[s7; -|String surname;&]
[s7; &]
[s7; -|unsigned GetHashValue() const          `{ return CombineHash(name, 
surname); `}&]
[s7; -|bool operator`=`=(const Person`& b) const `{ return name `=`= 
b.name `&`& surname `=`= b.surname; `}&]
[s7; &]
[s7; -|Person(String name, String surname) : name(name), surname(surname) 
`{`}&]
[s7; -|Person() `{`}&]
[s7; `};&]
[s7; &]
[s7; Index<Person> p;&]
[s7; p.Add(Person(`"John`", `"Smith`"));&]
[s7; p.Add(Person(`"Paul`", `"Carpenter`"));&]
[s7; p.Add(Person(`"Carl`", `"Engles`"));&]
[s7; &]
[s7; DUMP(p.Find(Person(`"Paul`", `"Carpenter`")));&]
[s0; &]
[s17; p.Find(Person(`"Paul`", `"Carpenter`")) `= 1&]
[s0; &]
[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]:&]
[s0; &]
[s7; struct Person : Moveable<Person> `{&]
[s7; -|String name;&]
[s7; -|String surname;&]
[s7; -|&]
[s7; -|String ToString() const `{ return String() << name << `' `' 
<< surname; `}&]
[s7; &]
[s7; -|Person(String name, String surname) : name(name), surname(surname) 
`{`}&]
[s7; -|Person() `{`}&]
[s7; `};&]
[s7; &]
[s7; VectorMap<String, Person> m;&]
[s7; &]
[s7; m.Add(`"1`", Person(`"John`", `"Smith`"));&]
[s7; m.Add(`"2`", Person(`"Carl`", `"Engles`"));&]
[s7; &]
[s7; Person`& p `= m.Add(`"3`");&]
[s7; p.name `= `"Paul`";&]
[s7; p.surname `= `"Carpenter`";&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{1: John Smith, 2: Carl Engles, 3: Paul Carpenter`}&]
[s0; &]
[s5; [*C@5 VectorMap] provides read`-only access to its [*C@5 Index] 
of keys and read`-write access to its [*C@5 Vector] of values:&]
[s0; &]
[s7; DUMP(m.GetKeys());&]
[s7; DUMP(m.GetValues());&]
[s0; &]
[s17; m.GetKeys() `= `[1, 2, 3`]&]
[s17; m.GetValues() `= `[John Smith, Carl Engles, Paul Carpenter`]&]
[s0; &]
[s0; &]
[s7; m.GetValues()`[2`].name `= `"Peter`";&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{1: John Smith, 2: Carl Engles, 3: Peter Carpenter`}&]
[s0; &]
[s5; You can use indices to iterate [*C@5 VectorMap] contents:&]
[s0; &]
[s7; for(int i `= 0; i < m.GetCount(); i`+`+)&]
[s7; -|LOG(m.GetKey(i) << `": `" << m`[i`]);&]
[s0; &]
[s17; 1: John Smith&]
[s17; 2: Carl Engles&]
[s17; 3: Peter Carpenter&]
[s0; &]
[s5; Standard [*C@5 begin] / [*C@5 end] pair for [*C@5 VectorMap] is the 
range of just values (internal Vector) `- it corresponds with 
[*C@5 operator`[`]] returning values:&]
[s0; &]
[s7; for(const auto`& p : m)&]
[s7; -|DUMP(p);&]
[s0; &]
[s17; p `= John Smith&]
[s17; p `= Carl Engles&]
[s17; p `= Peter Carpenter&]
[s0; &]
[s5; To iterate through keys, you can use [*C@5 begin]/``end`` of internal 
[*C@5 Index]:&]
[s0; &]
[s7; for(const auto`& p : m.GetKeys())&]
[s7; -|DUMP(p);&]
[s0; &]
[s17; p `= 1&]
[s17; p `= 2&]
[s17; p `= 3&]
[s0; &]
[s5; Alternatively, it is possible to create `'projection range`' 
of VectorMap that provides convenient key/value iteration, using 
[*C@5 operator`~] (note that is also removes `'unlinked`' items, 
see later):&]
[s0; &]
[s7; for(const auto`& e : `~m) `{&]
[s7; -|DUMP(e.key);&]
[s7; -|DUMP(e.value);&]
[s7; `}&]
[s0; &]
[s17; e.key `= 1&]
[s17; e.value `= John Smith&]
[s17; e.key `= 2&]
[s17; e.value `= Carl Engles&]
[s17; e.key `= 3&]
[s17; e.value `= Peter Carpenter&]
[s0; &]
[s5; Note that the `'projection range`' obtained by [*C@5 operator`~] 
is temporary value, which means that if mutating operation is 
required for values, r`-value reference has to be used instead 
of plain reference:&]
[s0; &]
[s7; for(const auto`& e : `~m)&]
[s7; -|if(e.key `=`= `"2`")&]
[s7; -|-|e.value.surname `= `"May`";&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{1: John Smith, 2: Carl May, 3: Peter Carpenter`}&]
[s0; &]
[s5; You can use Find method to retrieve position of element with 
required key:&]
[s0; &]
[s7; DUMP(m.Find(`"2`"));&]
[s0; &]
[s17; m.Find(`"2`") `= 1&]
[s0; &]
[s5; or Get method to retrieve corresponding value:&]
[s0; &]
[s7; DUMP(m.Get(`"2`"));&]
[s0; &]
[s17; m.Get(`"2`") `= Carl May&]
[s0; &]
[s5; Passing key not present in [*C@5 VectorMap] as [*C@5 Get] parameter 
is undefined behavior (ASSERT fails in debug mode), but there 
exists two parameter version of [*C@5 Get] that returns second 
parameter if the key is not found in VectorMap:&]
[s0; &]
[s7; DUMP(m.Get(`"33`", Person(`"unknown`", `"person`")));&]
[s0; &]
[s17; m.Get(`"33`", Person(`"unknown`", `"person`")) `= unknown person&]
[s0; &]
[s5; As with [*C@5 Index], you can use [*C@5 Unlink] to make elements 
invisible for Find operations:&]
[s0; &]
[s7; m.Unlink(1);&]
[s7; DUMP(m.Find(`"2`"));&]
[s0; &]
[s17; m.Find(`"2`") `= `-1&]
[s0; &]
[s5; [*C@5 SetKey] changes the key of the element:&]
[s0; &]
[s7; m.SetKey(1, `"33`");&]
[s7; DUMP(m.Get(`"33`", Person(`"unknown`", `"person`")));&]
[s0; &]
[s17; m.Get(`"33`", Person(`"unknown`", `"person`")) `= Carl May&]
[s0; &]
[s5; If there are more elements with the same key in [*C@5 VectorMap], 
you can iterate them using [*C@5 FindNext] method:&]
[s0; &]
[s7; m.Add(`"33`", Person(`"Peter`", `"Pan`"));&]
[s7; &]
[s7; int q `= m.Find(`"33`");&]
[s7; while(q >`= 0) `{&]
[s7; -|DUMP(m`[q`]);&]
[s7; -|q `= m.FindNext(q);&]
[s7; `}&]
[s0; &]
[s17; m`[q`] `= Carl May&]
[s17; m`[q`] `= Peter Pan&]
[s0; &]
[s5; Unlinked positions can be `'reused`' using Put method:&]
[s0; &]
[s7; m.UnlinkKey(`"33`");&]
[s7; m.Put(`"22`", Person(`"Ali`", `"Baba`"));&]
[s7; m.Put(`"44`", Person(`"Ivan`", `"Wilks`"));&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{1: John Smith, 22: Ali Baba, 3: Peter Carpenter, 44: Ivan 
Wilks`}&]
[s0; &]
[s5; [*C@5 PickValues] / [*C@5 PickIndex] / [*C@5 PickKeys] / pick internal 
[*C@5 Vector] / [*C@5 Index] / [*C@5 Vector] of [*C@5 Index]:&]
[s0; &]
[s7; Vector<Person> ps `= m.PickValues();&]
[s7; Vector<String> ks `= m.PickKeys();&]
[s7; &]
[s7; DUMP(ps);&]
[s7; DUMP(ks);&]
[s7; DUMP(m);&]
[s0; &]
[s17; ps `= `[John Smith, Ali Baba, Peter Carpenter, Ivan Wilks`]&]
[s17; ks `= `[1, 22, 3, 44`]&]
[s17; m `= `{`}&]
[s0; &]
[s5; [*C@5 VectorMap] pick constructor to create map by picking:&]
[s0; &]
[s7; ks`[0`] `= `"Changed key`";&]
[s7; &]
[s7; m `= VectorMap<String, Person>(pick(ks), pick(ps));&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{Changed key: John Smith, 22: Ali Baba, 3: Peter Carpenter, 
44: Ivan Wilks`}&]
[s0; &]
[s5; [*C@5 ArrayMap] is composition of Index and Array, for cases where 
Array is better fit for value type (e.g. they are polymorphic):&]
[s0; &]
[s7; ArrayMap<String, Person> am;&]
[s7; am.Create<Person>(`"key`", `"new`", `"person`");&]
[s7; &]
[s7; DUMP(am);&]
[s0; &]
[s17; am `= `{key: new person`}&]
[s0; &]
[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.&]
[s0; &]
[s7; struct Base `{&]
[s7; -|virtual String Get() `= 0;&]
[s7; -|virtual `~Base() `{`}&]
[s7; `};&]
[s7; &]
[s7; struct Derived1 : Base `{&]
[s7; -|virtual String Get() `{ return `"Derived1`"; `}&]
[s7; `};&]
[s7; &]
[s7; struct Derived2 : Base `{&]
[s7; -|virtual String Get() `{ return `"Derived2`"; `}&]
[s7; `};&]
[s7; &]
[s7; One<Base> s;&]
[s0; &]
[s5; [*C@5 operator bool] of one returns true if it contains an element:&]
[s0; &]
[s7; DUMP((bool)s);&]
[s0; &]
[s17; (bool)s `= false&]
[s0; &]
[s0; &]
[s7; s.Create<Derived1>();&]
[s7; DUMP((bool)s);&]
[s7; DUMP(s`->Get());&]
[s0; &]
[s17; (bool)s `= true&]
[s17; s`->Get() `= Derived1&]
[s0; &]
[s5; You can use [*C@5 Is] to check if certain type is currently stored 
in [*C@5 One]:&]
[s0; &]
[s7; DUMP(s.Is<Derived1>());&]
[s7; DUMP(s.Is<Base>());&]
[s7; DUMP(s.Is<Derived2>());&]
[s0; &]
[s17; s.Is<Derived1>() `= true&]
[s17; s.Is<Base>() `= true&]
[s17; s.Is<Derived2>() `= false&]
[s0; &]
[s5; To get a pointer to the contained instance, use [*C@5 operator`~]:&]
[s0; &]
[s7; Base `*b `= `~s;&]
[s7; DUMP(b`->Get());&]
[s0; &]
[s17; b`->Get() `= Derived1&]
[s0; &]
[s5; Clear method removes the element from One:&]
[s0; &]
[s7; s.Clear();&]
[s7; DUMP((bool)s);&]
[s0; &]
[s17; (bool)s `= false&]
[s0; &]
[s5; Helper class MakeOne derived from One can be used to create 
contained element:&]
[s0; &]
[s7; s `= MakeOne<Derived1>();&]
[s7; DUMP(s`->Get());&]
[s0; &]
[s17; s`->Get() `= Derived1&]
[s0; &]
[s0; &]
[s7; MakeOne<Derived2> d2;&]
[s7; DUMP(d2`->Get());&]
[s0; &]
[s17; d2`->Get() `= Derived2&]
[s0; &]
[s0; &]
[s7; s `= pick(d2);&]
[s7; DUMP(s`->Get());&]
[s0; &]
[s17; s`->Get() `= Derived2&]
[s0; &]
[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] 
to retrieve a reference to the instance stored:&]
[s0; &]
[s7; for(int pass `= 0; pass < 2; pass`+`+) `{&]
[s7; -|Any x;&]
[s7; -|if(pass)&]
[s7; -|-|x.Create<String>() `= `"Hello!`";&]
[s7; -|else&]
[s7; -|-|x.Create<Color>() `= Blue();&]
[s7; -|&]
[s7; -|if(x.Is<String>())&]
[s7; -|-|LOG(`"Any is now String: `" << x.Get<String>());&]
[s7; -|&]
[s7; -|if(x.Is<Color>())&]
[s7; -|-|LOG(`"Any is now Color: `" << x.Get<Color>());&]
[s7; `}&]
[s0; &]
[s17; Any is now Color: Color(0, 0, 128)&]
[s17; Any is now String: Hello!&]
[s0; &]
[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 
quickly. You can expect [*C@5 operator`[`]] to be about 10 times 
slower than in Vector (but that is still quite fast), while [*C@5 Insert] 
at any position scales well up to hundreds of megabytes of data 
(e.g. [*C@5 InVector] containing 100M of String elements is handled 
without problems).&]
[s0; &]
[s7; InVector<int> v;&]
[s7; for(int i `= 0; i < 1000000; i`+`+)&]
[s7; -|v.Add(i);&]
[s7; v.Insert(0, `-1); // This is fast&]
[s0; &]
[s5; While the interface of [*C@5 InVector]/``InArray`` is almost identical 
to [*C@5 Vector]/``Array``, [*C@5 InVector]/``InArray`` in addition 
implements [*C@5 FindLowerBound]/``FindUpperBound`` methods `- 
while normal generic range algorithms work, it is possible to 
provide [*C@5 InVector]/``InArray`` specific optimizations that 
basically match the performace of [*C@5 Find`*Bound] on simple 
[*C@5 Vector].&]
[s0; &]
[s7; DUMP(v.FindLowerBound(55));&]
[s0; &]
[s17; v.FindLowerBound(55) `= 56&]
[s0; &]
[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 
parameter, defaults to [*C@5 StdLess]). Implementation is using 
[*C@5 InVector], so it works fine even with very large number of 
elements (performance is similar to tree based [*C@5 std`::set]). 
Unlike [*C@5 Index], [*C@5 SortedIndex] provides lower/upper bounds 
searches, so it allows range search.&]
[s0; &]
[s7; SortedIndex<int> x;&]
[s7; x.Add(5);&]
[s7; x.Add(3);&]
[s7; x.Add(7);&]
[s7; x.Add(1);&]
[s7; &]
[s7; DUMPC(x);&]
[s7; DUMP(x.Find(3));&]
[s7; DUMP(x.Find(3));&]
[s7; DUMP(x.FindLowerBound(3));&]
[s7; DUMP(x.FindUpperBound(6));&]
[s0; &]
[s17; x:&]
[s17; -|`[0`] `= 1&]
[s17; -|`[1`] `= 3&]
[s17; -|`[2`] `= 5&]
[s17; -|`[3`] `= 7&]
[s17; x.Find(3) `= 1&]
[s17; x.Find(3) `= 1&]
[s17; x.FindLowerBound(3) `= 1&]
[s17; x.FindUpperBound(6) `= 3&]
[s0; &]
[s5; [*C@5 SortedVectorMap] and [*C@5 SortedArrayMap] are then [*C@5 SortedIndex] 
based equivalents to [*C@5 VectorMap]/``ArrayMap``:&]
[s0; &]
[s7; SortedVectorMap<String, int> m;&]
[s7; m.Add(`"zulu`", 11);&]
[s7; m.Add(`"frank`", 12);&]
[s7; m.Add(`"alfa`", 13);&]
[s7; &]
[s7; DUMPM(m);&]
[s7; DUMP(m.Get(`"zulu`"));&]
[s0; &]
[s17; m:&]
[s17; -|`[0`] `= (alfa) 13&]
[s17; -|`[1`] `= (frank) 12&]
[s17; -|`[2`] `= (zulu) 11&]
[s17; m.Get(`"zulu`") `= 11&]
[s0; &]
[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 
are directly accessible as member variables [*C@5 a]..``d``, [*C@5 Tuple] 
supports persistent storage patterns ([*C@5 Serialize], [*C@5 Jsonize], 
[*C@5 Xmlize]), hash code ([*C@5 GetHashValue]), conversion to [*C@5 String] 
and Value conversions.&]
[s5; To create a [*C@5 Tuple] value, you can use the [*C@5 MakeTuple] 
function.&]
[s0; &]
[s7; Tuple<int, String, String> x `= MakeTuple(12, `"hello`", `"world`");&]
[s0; &]
[s5; Individual values are accessible as members [*C@5 a] .. [*C@5 d]:&]
[s0; &]
[s7; DUMP(x.a);&]
[s7; DUMP(x.b);&]
[s7; DUMP(x.c);&]
[s0; &]
[s17; x.a `= 12&]
[s17; x.b `= hello&]
[s17; x.c `= world&]
[s0; &]
[s5; Or using [*C@5 Get]:&]
[s0; &]
[s7; DUMP(x.Get<1>());&]
[s7; DUMP(x.Get<int>());&]
[s0; &]
[s17; x.Get<1>() `= hello&]
[s17; x.Get<int>() `= 12&]
[s0; &]
[s5; As long as all individual types have conversion to [*C@5 String] 
([*C@5 AsString]), the tuple also has such conversion and thus 
can e.g. be easily logged:&]
[s0; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= (12, hello, world)&]
[s0; &]
[s5; As long as individual types have defined [*C@5 GetHashValue], 
so does [*C@5 Tuple]:&]
[s0; &]
[s7; DUMP(GetHashValue(x));&]
[s0; &]
[s17; GetHashValue(x) `= 834842890&]
[s0; &]
[s5; As long as individual types have defined [*C@5 operator`=`=], 
[*C@5 Tuple] has defined [*C@5 operator`=`=] and [*C@5 operator!`=]:&]
[s0; &]
[s7; Tuple<int, String, String> y `= x;&]
[s7; DUMP(x `=`= y);&]
[s7; DUMP(x !`= y);&]
[s7; y.a`+`+;&]
[s7; DUMP(x `=`= y);&]
[s7; DUMP(x !`= y);&]
[s0; &]
[s17; x `=`= y `= true&]
[s17; x !`= y `= false&]
[s17; x `=`= y `= false&]
[s17; x !`= y `= true&]
[s0; &]
[s5; As long as all individual types have defined [*C@5 SgnCompare], 
Tuple has SgnCompare, Compare method and operators <, <`=, >, 
>`=:&]
[s0; &]
[s7; DUMP(x.Compare(y));&]
[s7; DUMP(SgnCompare(x, y));&]
[s7; DUMP(x < y);&]
[s0; &]
[s17; x.Compare(y) `= `-1&]
[s17; SgnCompare(x, y) `= `-1&]
[s17; x < y `= true&]
[s0; &]
[s5; GetCount returns the width of [*C@5 Tuple]:&]
[s0; &]
[s7; DUMP(x.GetCount());&]
[s0; &]
[s17; x.GetCount() `= 3&]
[s0; &]
[s5; Elements that are directly convertible with [*C@5 Value] can be 
`'Get`'/`'Set`':&]
[s0; &]
[s7; for(int i `= 0; i < x.GetCount(); i`+`+)&]
[s7; -|DUMP(x.Get(i));&]
[s0; &]
[s17; x.Get(i) `= 12&]
[s17; x.Get(i) `= hello&]
[s17; x.Get(i) `= world&]
[s0; &]
[s0; &]
[s7; x.Set(1, `"Hi`");&]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= (12, Hi, world)&]
[s0; &]
[s5; As long as all individual types are convertible with [*C@5 Value], 
you can convert Tuple to [*C@5 ValueArray] and back:&]
[s0; &]
[s7; ValueArray va `= x.GetArray();&]
[s7; DUMP(va);&]
[s7; &]
[s7; va.Set(2, `"Joe`");&]
[s7; x.SetArray(va);&]
[s0; &]
[s17; va `= `[12, Hi, world`]&]
[s0; &]
[s5; It is OK to assign [*C@5 Tuple] to [*C@5 Tuple] with different individual 
types, as long as types are directly convertible:&]
[s0; &]
[s7; Tuple<double, String, String> d `= x;&]
[s7; DUMP(d);&]
[s0; &]
[s17; d `= (12, Hi, Joe)&]
[s0; &]
[s5; Tie can be used to assign tuple to l`-values:&]
[s0; &]
[s7; int i;&]
[s7; String s1, s2;&]
[s7; &]
[s7; Tie(i, s1, s2) `= x;&]
[s7; &]
[s7; DUMP(i);&]
[s7; DUMP(s1);&]
[s7; DUMP(s2);&]
[s0; &]
[s17; i `= 12&]
[s17; s1 `= Hi&]
[s17; s2 `= Joe&]
[s0; &]
[s5; U`+`+ Tuples are carefully designed as POD type, which allows 
POD arrays to be intialized with classic C style:&]
[s0; &]
[s7; static Tuple2<int, const char `*> map`[`] `= `{&]
[s7; -|`{ 1, `"one`" `},&]
[s7; -|`{ 2, `"one`" `},&]
[s7; -|`{ 3, `"one`" `},&]
[s7; `};&]
[s0; &]
[s5; Simple FindTuple template function is provided to search for 
tuple based on the first value ([*C@5 a]) (linear O(n) search):&]
[s0; &]
[s7; DUMP(FindTuple(map, `_`_countof(map), 3)`->b);&]
[s0; &]
[s17; FindTuple(map, `_`_countof(map), 3)`->b `= one&]
[s0; &]
[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 
random access to elements (all U`+`+ containers are random access), 
[*C@5 operator`[`]] and [*C@5 GetCount] method.&]
[s5; Obviously, U`+`+ containers are ranges:&]
[s0; &]
[s7; Vector<int> x `= `{ 1, 2, 3, 4, 5, 1, 2, 3, 4 `};&]
[s7; &]
[s7; DUMP(FindIndex(x, 2)); // FindIndex is a trivial algorithm that 
does linear search&]
[s0; &]
[s17; FindIndex(x, 2) `= 1&]
[s0; &]
[s5; If you want the algorithm to run on part of container only, 
you can use [*C@5 SubRange] instance:&]
[s0; &]
[s7; DUMP(SubRange(x, 3, 6));&]
[s7; DUMP(FindIndex(SubRange(x, 3, 6), 4));&]
[s0; &]
[s17; SubRange(x, 3, 6) `= `[4, 5, 1, 2, 3, 4`]&]
[s17; FindIndex(SubRange(x, 3, 6), 4) `= 0&]
[s0; &]
[s5; As a side`-job, SubRange can also be created from `'begin`' 
/ `'end`' pair, thus e.g. allowing algorithms to work on C arrays:&]
[s0; &]
[s7; int a`[`] `= `{ 1, 22, 4, 2, 8 `};&]
[s7; &]
[s7; auto ar `= SubRange(std`::begin(a), std`::end(a));&]
[s7; &]
[s7; DUMP(ar);&]
[s0; &]
[s17; ar `= `[1, 22, 4, 2, 8`]&]
[s0; &]
[s0; &]
[s7; Sort(ar);&]
[s7; DUMP(ar);&]
[s0; &]
[s17; ar `= `[1, 2, 4, 8, 22`]&]
[s0; &]
[s5; There are some macro aliases that make type management of ranges 
easier:&]
[s0; &]
[s7; DUMP(typeid(ValueTypeOf<decltype(x)>).name());&]
[s7; DUMP(typeid(ValueTypeOf<decltype(SubRange(x, 1, 1))>).name());&]
[s7; DUMP(typeid(IteratorOf<decltype(x)>).name());&]
[s7; DUMP(typeid(ConstIteratorOf<decltype(SubRange(x, 1, 1))>).name());&]
[s7; DUMP(typeid(SubRangeOf<Vector<int>>).name());&]
[s0; &]
[s17; typeid(ValueTypeOf<decltype(x)>).name() `= int&]
[s17; typeid(ValueTypeOf<decltype(SubRange(x, 1, 1))>).name() `= int&]
[s17; typeid(IteratorOf<decltype(x)>).name() `= int `* `_`_ptr64&]
[s17; typeid(ConstIteratorOf<decltype(SubRange(x, 1, 1))>).name() 
`= int `* `_`_ptr64&]
[s17; typeid(SubRangeOf<Vector<int>>).name() `= class Upp`::SubRangeClass<int 
`* `_`_ptr64>&]
[s0; &]
[s5; While containers themselves and SubRange are the two most common 
range types, U`+`+ has two special ranges. [*C@5 ConstRange] simply 
provides the range of single value:&]
[s0; &]
[s7; DUMP(ConstRange(1, 10));&]
[s0; &]
[s17; ConstRange(1, 10) `= `[1, 1, 1, 1, 1, 1, 1, 1, 1, 1`]&]
[s0; &]
[s5; [*C@5 ReverseRange] reverses the order of elements in the source 
range:&]
[s0; &]
[s7; Vector<int> v`{ 1, 2, 3, 4 `};&]
[s7; &]
[s7; DUMP(ReverseRange(v));&]
[s0; &]
[s17; ReverseRange(v) `= `[4, 3, 2, 1`]&]
[s0; &]
[s5; [*C@5 ViewRange] picks a source range and [*C@5 Vector] of integer 
indices a provides a view of source range through this [*C@5 Vector]:&]
[s0; &]
[s7; Vector<int> h`{ 2, 4, 0 `};&]
[s7; &]
[s7; DUMP(ViewRange(x, clone(h)));&]
[s0; &]
[s17; ViewRange(x, clone(h)) `= `[3, 5, 1`]&]
[s0; &]
[s0; &]
[s7; Sort(ViewRange(x, clone(h)));&]
[s7; DUMP(ViewRange(x, clone(h)));&]
[s7; DUMP(x);&]
[s0; &]
[s17; ViewRange(x, clone(h)) `= `[1, 3, 5`]&]
[s17; x `= `[5, 2, 1, 4, 3, 1, 2, 3, 4`]&]
[s0; &]
[s5; [*C@5 SortedRange] returns range sorted by predicate (default 
is std`::less):&]
[s0; &]
[s7; DUMP(SortedRange(x));&]
[s0; &]
[s17; SortedRange(x) `= `[1, 1, 2, 2, 3, 3, 4, 4, 5`]&]
[s0; &]
[s5; Finally [*C@5 FilterRange] creates a subrange of elements satisfying 
certain condition:&]
[s0; &]
[s7; DUMP(FilterRange(x, `[`](int x) `{ return x > 3; `}));&]
[s0; &]
[s17; FilterRange(x, `[`](int x) `{ return x > 3; `}) `= `[5, 4, 4`]&]
[s0; &]
[s5; Various Range functions can be combined to produce complex results:&]
[s0; &]
[s7; DUMP(ReverseRange(FilterRange(x, `[`](int x) `{ return x < 4; 
`})));&]
[s0; &]
[s17; ReverseRange(FilterRange(x, `[`](int x) `{ return x < 4; `})) 
`= `[3, 2, 1, 3, 1, 2`]&]
[s0; &]
[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 
ranges and they prefer indices. Sometimes, U`+`+ algorithm will 
perform faster with U`+`+ types than standard library algorithm.&]
[s5; [*C@5 FindIndex] performs linear search to find element with given 
value and returns its index or `-1 if not found:&]
[s0; &]
[s7; Vector<int> data `{ 5, 3, 7, 9, 3, 4, 2 `};&]
[s7; &]
[s7; &]
[s7; DUMP(FindIndex(data, 3));&]
[s7; DUMP(FindIndex(data, 6));&]
[s0; &]
[s17; FindIndex(data, 3) `= 1&]
[s17; FindIndex(data, 6) `= `-1&]
[s0; &]
[s5; [*C@5 SubRange] can be used to apply algorithm on subrange of 
container:&]
[s0; &]
[s7; DUMP(FindIndex(SubRange(data, 2, data.GetCount() `- 2), 3));&]
[s0; &]
[s17; FindIndex(SubRange(data, 2, data.GetCount() `- 2), 3) `= 2&]
[s0; &]
[s5; [*C@5 FindMin] and [*C@5 FindMax] return the index of minimal / 
maximal element:&]
[s0; &]
[s7; DUMP(FindMin(data));&]
[s7; DUMP(FindMax(data));&]
[s0; &]
[s17; FindMin(data) `= 6&]
[s17; FindMax(data) `= 3&]
[s0; &]
[s5; [*C@5 Min] and [*C@5 Max] return the [/ value] of minimal / maximal 
element:&]
[s0; &]
[s7; DUMP(Min(data));&]
[s7; DUMP(Max(data));&]
[s0; &]
[s17; Min(data) `= 2&]
[s17; Max(data) `= 9&]
[s0; &]
[s5; If the range is empty, [*C@5 Min] and [*C@5 Max] are undefined (ASSERT 
fails in debug mode), unless the value is specified as second 
parameter to be used in this case:&]
[s0; &]
[s7; -|Vector<int> empty;&]
[s7; //-|DUMP(Min(empty)); // This is undefined (fails in ASSERT)&]
[s7; -|DUMP(Min(empty, `-99999));&]
[s0; &]
[s17; Min(empty, `-99999) `= `-99999&]
[s0; &]
[s5; [*C@5 Count] returns the number of elements with specified value, 
[*C@5 CountIf] the number of elements that satisfy predicate:&]
[s0; &]
[s7; DUMP(Count(data, 11));&]
[s7; DUMP(CountIf(data, `[`=`](int c) `{ return c >`= 5; `}));&]
[s0; &]
[s17; Count(data, 11) `= 0&]
[s17; CountIf(data, `[`=`](int c) `{ return c >`= 5; `}) `= 3&]
[s0; &]
[s5; [*C@5 Sum] return the sum of all elements in range:&]
[s0; &]
[s7; DUMP(Sum(data));&]
[s0; &]
[s17; Sum(data) `= 33&]
[s0; &]
[s5; Sorted containers can be searched with bisection. U`+`+ provides 
usual upper / lower bound algorithms. [*C@5 FindBinary] returns 
the index of element with given value or `-1 if not found:&]
[s0; &]
[s7; data `= `{ 5, 7, 9,  9, 14, 20, 23, 50 `};&]
[s7;       // 0  1  2   3   4   5   6   7&]
[s7; DUMP(FindLowerBound(data, 9));&]
[s7; DUMP(FindUpperBound(data, 9));&]
[s7; DUMP(FindBinary(data, 9));&]
[s7; DUMP(FindLowerBound(data, 10));&]
[s7; DUMP(FindUpperBound(data, 10));&]
[s7; DUMP(FindBinary(data, 10));&]
[s0; &]
[s17; FindLowerBound(data, 9) `= 2&]
[s17; FindUpperBound(data, 9) `= 4&]
[s17; FindBinary(data, 9) `= 2&]
[s17; FindLowerBound(data, 10) `= 4&]
[s17; FindUpperBound(data, 10) `= 4&]
[s17; FindBinary(data, 10) `= `-1&]
[s0; &]
[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; &]
[s7; Vector<String> x `{ `"1`", `"2`", `"10`" `};&]
[s7; &]
[s7; Sort(x);&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= `[1, 10, 2`]&]
[s0; &]
[s0; &]
[s7; Sort(x, `[`](const String`& a, const String`& b) `{ return atoi(a) 
< atoi(b); `});&]
[s7; &]
[s7; DUMP(x);&]
[s0; &]
[s17; x `= `[1, 2, 10`]&]
[s0; &]
[s5; [*C@5 IndexSort] is sort variant that is able to sort two ranges 
(like [*C@5 Vector] or [*C@5 Array]) of the same size, based on values 
in the first range:&]
[s0; &]
[s7; Vector<int> a `{ 5, 10, 2, 9, 7, 3 `};&]
[s7; Vector<String> b `{ `"five`", `"ten`", `"two`", `"nine`", `"seven`", 
`"three`" `};&]
[s7; &]
[s7; IndexSort(a, b);&]
[s7; &]
[s7; DUMP(a);&]
[s7; DUMP(b);&]
[s0; &]
[s17; a `= `[2, 3, 5, 7, 9, 10`]&]
[s17; b `= `[two, three, five, seven, nine, ten`]&]
[s0; &]
[s0; &]
[s7; IndexSort(b, a);&]
[s7; &]
[s7; DUMP(a);&]
[s7; DUMP(b);&]
[s0; &]
[s17; a `= `[5, 9, 7, 10, 3, 2`]&]
[s17; b `= `[five, nine, seven, ten, three, two`]&]
[s0; &]
[s5; There are also [*C@5 IndexSort2] and [*C@5 IndexSort3] variants 
that sort 2 or 3 dependent ranges.&]
[s5; Sometimes, instead of sorting items in the range, it is useful 
to know the order of items as sorted, using [*C@5 GetSortOrder]:&]
[s0; &]
[s7; Vector<int> o `= GetSortOrder(a);&]
[s7; &]
[s7; DUMP(o);&]
[s0; &]
[s17; o `= `[5, 4, 0, 2, 1, 3`]&]
[s0; &]
[s5; Normal [*C@5 Sort] is not stable `- equal items can appear in 
sorted range in random order. If maintaining original order of 
equal items is important, use [*C@5 StableSort] variant (with performance 
penalty):&]
[s0; &]
[s7; Vector<Point> t `{ Point(10, 10), Point(7, 1), Point(7, 2), 
Point(7, 3), Point(1, 0) `};&]
[s7; StableSort(t, `[`](const Point`& a, const Point`& b) `{ return 
a.x < b.x; `});&]
[s7; &]
[s7; DUMP(t);&]
[s0; &]
[s17; t `= `[`[1, 0`], `[7, 1`], `[7, 2`], `[7, 3`], `[10, 10`]`]&]
[s0; &]
[s5; All sorting algorithms have they `'Stable`' variant, so there 
is [*C@5 StableIndexSort], [*C@5 GetStableSortOrder] etc...&]
[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 
with [*C@5 Value] and it is then possible to e.g. compare [*C@5 Value]s 
containing such types against each other or serialize them for 
persistent storage.&]
[s5; Usually, Value compatible types define typecast operator to 
[*C@5 Value] and constructor from [*C@5 Value], so that interaction 
is for the most part seamless:&]
[s0; &]
[s7; Value a `= 1;&]
[s7; Value b `= 2.34;&]
[s7; Value c `= GetSysDate();&]
[s7; Value d `= `"hello`";&]
[s7; &]
[s7; DUMP(a);&]
[s7; DUMP(b);&]
[s7; DUMP(c);&]
[s7; DUMP(d);&]
[s7; &]
[s7; int x `= a;&]
[s7; double y `= b;&]
[s7; Date z `= c;&]
[s7; String s `= d;&]
[s7; &]
[s7; DUMP(x);&]
[s7; DUMP(y);&]
[s7; DUMP(z);&]
[s7; DUMP(s);&]
[s0; &]
[s17; a `= 1&]
[s17; b `= 2.34&]
[s17; c `= 02/09/2018&]
[s17; d `= hello&]
[s17; x `= 1&]
[s17; y `= 2.34&]
[s17; z `= 02/09/2018&]
[s17; s `= hello&]
[s0; &]
[s5; As for primitive types, Value seamlessly works with [*C@5 int], 
[*C@5 int64], [*C@5 bool] and [*C@5 double]. Casting [*C@5 Value] to 
a type that it does not contain throws an exception:&]
[s0; &]
[s7; try `{&]
[s7; -|s `= a;&]
[s7; -|DUMP(s); // we never get here....&]
[s7; `}&]
[s7; catch(ValueTypeError) `{&]
[s7; -|LOG(`"Failed Value conversion`");&]
[s7; `}&]
[s0; &]
[s17; Failed Value conversion&]
[s0; &]
[s5; However, conversion between related types is possible (as long 
as it is supported by these types):&]
[s0; &]
[s7; double i `= a;&]
[s7; int j `= b;&]
[s7; Time k `= c;&]
[s7; WString t `= d;&]
[s7; &]
[s7; DUMP(i);&]
[s7; DUMP(j);&]
[s7; DUMP(k);&]
[s7; DUMP(t);&]
[s0; &]
[s17; i `= 1&]
[s17; j `= 2&]
[s17; k `= 02/09/2018 00:00:00&]
[s17; t `= hello&]
[s0; &]
[s5; To determine type of value stored in [*C@5 Value], you can use 
[*C@5 Is] method:&]
[s0; &]
[s7; DUMP(a.Is<int>());&]
[s7; DUMP(a.Is<double>());&]
[s7; DUMP(b.Is<double>());&]
[s7; DUMP(c.Is<int>());&]
[s7; DUMP(c.Is<Date>());&]
[s7; DUMP(d.Is<String>());&]
[s0; &]
[s17; a.Is<int>() `= true&]
[s17; a.Is<double>() `= false&]
[s17; b.Is<double>() `= true&]
[s17; c.Is<int>() `= false&]
[s17; c.Is<Date>() `= true&]
[s17; d.Is<String>() `= true&]
[s0; &]
[s5; Note that Is tests for absolute type match, not for compatible 
types. For that reason, for widely used compatible types helper 
functions are defined:&]
[s0; &]
[s7; DUMP(IsNumber(a));&]
[s7; DUMP(IsNumber(b));&]
[s7; DUMP(IsDateTime(c));&]
[s7; DUMP(IsString(d));&]
[s0; &]
[s17; IsNumber(a) `= true&]
[s17; IsNumber(b) `= true&]
[s17; IsDateTime(c) `= true&]
[s17; IsString(d) `= true&]
[s0; &]
[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 
as lowest number the type can represent). If type supports ordering 
(<, >), all values of the type are greater than Null value. To 
test whether a value is empty, use [*C@5 IsNull] function.&]
[s0; &]
[s7; int x `= Null;&]
[s7; int y `= 120;&]
[s7; Date d `= Null;&]
[s7; Date e `= GetSysDate();&]
[s7; &]
[s7; DUMP(x);&]
[s7; DUMP(y);&]
[s7; DUMP(d);&]
[s7; DUMP(e > d);&]
[s0; &]
[s17; x `= &]
[s17; y `= 120&]
[s17; d `= &]
[s17; e > d `= true&]
[s0; &]
[s5; [*C@5 Null] is the only instance of [*C@5 Nuller] type. Assigning 
[*C@5 Null] to primitive types is achieved by cast operators of 
[*C@5 Nuller], other types can do it using constructor from [*C@5 Nuller].&]
[s5; As a special case, if [*C@5 Value] contains [*C@5 Null], it is convertible 
to any value type that can contain [*C@5 Null]:&]
[s0; &]
[s7; Value v `= x; // x is int&]
[s7; e `= v; // e is Date, but v is Null, so Null is assigned to 
e&]
[s7; &]
[s7; DUMP(IsNull(e));&]
[s0; &]
[s17; IsNull(e) `= true&]
[s0; &]
[s5; Function [*C@5 Nvl] is U`+`+ analog of well known SQL function 
coalesce (ifnull, Nvl), which returns the first non`-null argument 
(or [*C@5 Null] if all are [*C@5 Null]).&]
[s0; &]
[s7; int a `= Null;&]
[s7; int b `= 123;&]
[s7; int c `= 1;&]
[s7; &]
[s7; DUMP(Nvl(a, b, c));&]
[s0; &]
[s17; Nvl(a, b, c) `= 123&]
[s0; &]
[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 
and assignment operator are required (and there are even forms 
of [*C@5 RawValue] that work for types missing these):&]
[s0; &]
[s7; struct RawFoo `{&]
[s7; -|String x;&]
[s7; -|// default copy constructor and assignment operator are provided 
by compiler&]
[s7; `};&]
[s0; &]
[s5; To convert such type to [*C@5 Value], use [*C@5 RawToValue]:&]
[s0; &]
[s7; RawFoo h;&]
[s7; h.x `= `"hello`";&]
[s7; Value q `= RawToValue(h);&]
[s7; &]
[s7; DUMP(q.Is<RawFoo>());&]
[s0; &]
[s17; q.Is<RawFoo>() `= true&]
[s0; &]
[s5; To convert it back, us `'To`' templated member function of [*C@5 Value], 
it returns a constant reference to the value:&]
[s0; &]
[s7; DUMP(q.To<RawFoo>().x);&]
[s0; &]
[s17; q.To<RawFoo>().x `= hello&]
[s0; &]
[s5; [*C@5 RichValue] level [*C@5 Value]s provide more operations for 
[*C@5 Value] `- equality test, [*C@5 IsNull] test, hashing, conversion 
to text, serialization (possibly to XML and Json), comparison. 
In order to make serialization work, type must also have assigned 
an integer id (client types should use ids in range 10000..20000). 
Type can provide the support for these operations via template 
function specializations or (perhaps more convenient) using defined 
methods and inheriting from [*C@5 ValueType] base class template:&]
[s0; &]
[s7; struct Foo : ValueType<Foo, 10010> `{&]
[s7; -|int x;&]
[s7; -|&]
[s7; -|Foo(const Nuller`&)                  `{ x `= Null; `}&]
[s7; -|Foo(int x) : x(x) `{`}&]
[s7; -|Foo() `{`}&]
[s7; &]
[s7; -|// We provide these methods to allow automatic conversion of 
Foo to/from Value&]
[s7; -|operator Value() const              `{ return RichToValue(`*this); 
`}&]
[s7; -|Foo(const Value`& v)                 `{ `*this `= v.Get<Foo>(); 
`}&]
[s7; &]
[s7; -|String ToString() const             `{ return AsString(x); 
`}&]
[s7; -|unsigned GetHashValue() const       `{ return x; `}&]
[s7; -|void Serialize(Stream`& s)           `{ s % x; `}&]
[s7; -|bool operator`=`=(const Foo`& b) const `{ return x `=`= b.x; 
`}&]
[s7; -|bool IsNullInstance() const         `{ return IsNull(x); `}&]
[s7; -|int  Compare(const Foo`& b) const    `{ return SgnCompare(x, 
b.x); `}&]
[s7; -|// This type does not define XML nor Json serialization&]
[s7; `};&]
[s7; &]
[s7; INITBLOCK `{ // This has to be at file level scope&]
[s7; -|Value`::Register<Foo>(); // need to register value type integer 
id to allow serialization&]
[s7; `}&]
[s7; &]
[s7; Value a `= Foo(54321); // uses Foo`::operator Value&]
[s7; Value b `= Foo(54321);&]
[s7; Value c `= Foo(600);&]
[s7; &]
[s7; DUMP(a); // uses Foo`::ToString&]
[s7; DUMP(a `=`= b); // uses Foo`::operator`=`=&]
[s7; DUMP(a `=`= c);&]
[s7; DUMP(c < a); // uses Foo`::Compare&]
[s7; &]
[s7; DUMP(IsNull(a)); // uses Foo`::IsNullInstance&]
[s7; &]
[s7; Foo foo `= c; // Uses Foo`::Foo(const Value`&)&]
[s7; DUMP(foo);&]
[s0; &]
[s17; a `= 54321&]
[s17; a `=`= b `= true&]
[s17; a `=`= c `= false&]
[s17; c < a `= true&]
[s17; IsNull(a) `= false&]
[s17; foo `= 600&]
[s0; &]
[s0; &]
[s7; String s `= StoreAsString(a); // Uses Foo`::Serialize&]
[s7; &]
[s7; Value loaded;&]
[s7; // Using registered (Value`::Registered) integer id creates the 
correct type, then uses&]
[s7; // Foo`::Serialize to load the data from the stream&]
[s7; LoadFromString(loaded, s);&]
[s7; &]
[s7; DUMP(loaded);&]
[s0; &]
[s17; loaded `= 54321&]
[s0; &]
[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`};&]
[s7; &]
[s7; DUMP(va);&]
[s0; &]
[s17; va `= `[1, 2, 3`]&]
[s0; &]
[s5; ValueArray can be assigned to Value (and back):&]
[s0; &]
[s7; Value v `= va;&]
[s7; &]
[s7; DUMP(v);&]
[s7; DUMP(v.Is<ValueArray>()); // must be exactly ValueArray&]
[s7; DUMP(IsValueArray(v)); // is ValueArray or ValueMap (which is 
convertible to ValueArray)&]
[s7; &]
[s7; ValueArray va2 `= v;&]
[s7; &]
[s7; DUMP(va2);&]
[s0; &]
[s17; v `= `[1, 2, 3`]&]
[s17; v.Is<ValueArray>() `= true&]
[s17; IsValueArray(v) `= true&]
[s17; va2 `= `[1, 2, 3`]&]
[s0; &]
[s5; Elements can be appended using [*C@5 Add] method or [*C@5 operator<<], 
element at index can be changed with [*C@5 Set]:&]
[s0; &]
[s7; va.Add(10);&]
[s7; va << 20 << 21;&]
[s7; va.Set(0, 999);&]
[s7; &]
[s7; DUMP(va);&]
[s0; &]
[s17; va `= `[999, 2, 3, 10, 20, 21`]&]
[s0; &]
[s5; Elements can be removed:&]
[s0; &]
[s7; va.Remove(0, 2);&]
[s7; &]
[s7; DUMP(va);&]
[s0; &]
[s17; va `= `[3, 10, 20, 21`]&]
[s0; &]
[s5; and inserted:&]
[s0; &]
[s7; va.Insert(1, v);&]
[s7; &]
[s7; DUMP(va);&]
[s0; &]
[s17; va `= `[3, 1, 2, 3, 10, 20, 21`]&]
[s0; &]
[s5; It is possible to get a reference to element at index, however 
note that some [^topic`:`/`/Core`/srcdoc`/ValueReference`$en`-us^ special 
rules] apply here:&]
[s0; &]
[s7; va.At(0) `= 222;&]
[s7; &]
[s7; DUMP(va);&]
[s0; &]
[s17; va `= `[222, 1, 2, 3, 10, 20, 21`]&]
[s0; &]
[s5; If [*C@5 Value] contains [*C@5 ValueArray], [*C@5 Value`::GetCount] 
method returns the number of elements in the array (if there 
is no [*C@5 ValueArray] in [*C@5 Value], it returns zero). You can 
use [*C@5 Value`::operator`[`](int)] to get constant reference to 
[*C@5 ValueArray] elements:&]
[s0; &]
[s7; for(int i `= 0; i < v.GetCount(); i`+`+)&]
[s7; -|LOG(v`[i`]);&]
[s0; &]
[s17; 1&]
[s17; 2&]
[s17; 3&]
[s0; &]
[s5; It is even possible to directly add element to [*C@5 Value] if 
it contains [*C@5 ValueArray]:&]
[s0; &]
[s7; v.Add(4);&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[1, 2, 3, 4`]&]
[s0; &]
[s5; Or even get a reference to element at some index (with [^topic`:`/`/Core`/srcdoc`/ValueReference`$en`-us^ s
pecial rules]):&]
[s0; &]
[s7; v.At(0) `= 111;&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `[111, 2, 3, 4`]&]
[s0; &]
[s5; [*C@5 ValueMap] can store key `- value pairs and retrieve value 
for key quickly. Note that keys are not limited to [*C@5 String], 
but can be any [*C@5 Value] with [*C@5 operator`=`=] and hash code 
defined.&]
[s5; [*C@5 Add] method or [*C@5 operator()] add data to [*C@5 ValueMap]:&]
[s0; &]
[s7; ValueMap m;&]
[s7; &]
[s7; m.Add(`"one`", 1);&]
[s7; m(`"two`", 2)(`"three`", 3);&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{ one: 1, two: 2, three: 3 `}&]
[s0; &]
[s5; [*C@5 operator`[`]] retrieves the value at the key:&]
[s0; &]
[s7; DUMP(m`[`"two`"`]);&]
[s0; &]
[s17; m`[`"two`"`] `= 2&]
[s0; &]
[s5; When key is not present in the map, [*C@5 operator`[`]] returns 
void Value (which is also Null):&]
[s0; &]
[s7; DUMP(m`[`"key`"`]);&]
[s7; DUMP(m`[`"key`"`].IsVoid());&]
[s7; DUMP(IsNull(m`[`"key`"`]));&]
[s0; &]
[s17; m`[`"key`"`] `= &]
[s17; m`[`"key`"`].IsVoid() `= true&]
[s17; IsNull(m`[`"key`"`]) `= true&]
[s0; &]
[s5; Just like [*C@5 VectorMap], [*C@5 ValueMap] is ordered, so the order 
of adding pairs to it matters:&]
[s0; &]
[s7; ValueMap m2;&]
[s7; &]
[s7; m2.Add(`"two`", 2);&]
[s7; m2(`"one`", 1)(`"three`", 3);&]
[s7; &]
[s7; DUMP(m2);&]
[s7; DUMP(m `=`= m2); // different order of adding means they are 
not equal&]
[s0; &]
[s17; m2 `= `{ two: 2, one: 1, three: 3 `}&]
[s17; m `=`= m2 `= false&]
[s0; &]
[s5; `'Unordered`' equality test can be done using [*C@5 IsSame]:&]
[s0; &]
[s7; DUMP(m.IsSame(m2));&]
[s0; &]
[s17; m.IsSame(m2) `= true&]
[s0; &]
[s5; Iterating ValueMap can be achieved with [*C@5 GetCount], [*C@5 GetKey] 
and [*C@5 GetValue]:&]
[s0; &]
[s7; for(int i `= 0; i < m.GetCount(); i`+`+)&]
[s7; -|LOG(m.GetKey(i) << `" `= `" << m.GetValue(i));&]
[s0; &]
[s17; one `= 1&]
[s17; two `= 2&]
[s17; three `= 3&]
[s0; &]
[s5; It is possible to get [*C@5 ValueArray] of values:&]
[s0; &]
[s7; LOG(m.GetValues());&]
[s0; &]
[s17; `[1, 2, 3`]&]
[s0; &]
[s5; [*C@5 GetKeys] gets constant reference to [*C@5 Index<Value>] of 
keys:&]
[s0; &]
[s7; LOG(m.GetKeys());&]
[s0; &]
[s17; `[one, two, three`]&]
[s0; &]
[s5; It is possible to change the value with [*C@5 Set]:&]
[s0; &]
[s7; m.Set(`"two`", 4);&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{ one: 1, two: 4, three: 3 `}&]
[s0; &]
[s5; Or to change the value of key with [*C@5 SetKey]:&]
[s0; &]
[s7; m.SetKey(1, `"four`");&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{ one: 1, four: 4, three: 3 `}&]
[s0; &]
[s5; It is possible get a reference of value at given key, (with 
[^topic`:`/`/Core`/srcdoc`/ValueReference`$en`-us^ special rules]) 
with [*C@5 GetAdd] or [*C@5 operator()]:&]
[s0; &]
[s7; Value`& h `= m(`"five`");&]
[s7; &]
[s7; h `= 5;&]
[s7; &]
[s7; DUMP(m);&]
[s0; &]
[s17; m `= `{ one: 1, four: 4, three: 3, five: 5 `}&]
[s0; &]
[s5; When ValueMap is stored into Value, [*C@5 operator`[`](String)] 
provides access to value at key. Note that this narrows keys 
to text values:&]
[s0; &]
[s7; v `= m;&]
[s7; DUMP(v);&]
[s7; DUMP(v`[`"five`"`]);&]
[s0; &]
[s17; v `= `{ one: 1, four: 4, three: 3, five: 5 `}&]
[s17; v`[`"five`"`] `= 5&]
[s0; &]
[s5; [*C@5 Value`::GetAdd] and [*C@5 Value`::operator()] provide a reference 
to value at key, with [^topic`:`/`/Core`/srcdoc`/ValueReference`$en`-us^ special 
rules]:&]
[s0; &]
[s7; v.GetAdd(`"newkey`") `= `"foo`";&]
[s7; v(`"five`") `= `"FIVE`";&]
[s7; &]
[s7; DUMP(v);&]
[s0; &]
[s17; v `= `{ one: 1, four: 4, three: 3, five: FIVE, newkey: foo `}&]
[s0; &]
[s5; [*C@5 ValueMap] and [*C@5 ValueArray] are convertible with each 
other. When assigning [*C@5 ValueMap] to [*C@5 ValueArray], values 
are simply used:&]
[s0; &]
[s7; ValueArray v2 `= m;&]
[s7; &]
[s7; DUMP(v2);&]
[s0; &]
[s17; v2 `= `[1, 4, 3, 5`]&]
[s0; &]
[s5; When assigning [*C@5 ValueArray] to [*C@5 ValueMap], keys are set 
as indices of elements:&]
[s0; &]
[s7; ValueMap m3 `= v2;&]
[s7; &]
[s7; DUMP(m3);&]
[s0; &]
[s17; m3 `= `{ 0: 1, 1: 4, 2: 3, 3: 5 `}&]
[s0; &]
[s5; With basic [*C@5 Value] types [*C@5 int], [*C@5 String], [*C@5 ValueArray] 
and [*C@5 ValueMap], [*C@5 Value] can represent JSON:&]
[s0; &]
[s7; Value j `= ParseJSON(`"`{ `\`"array`\`" : `[ 1, 2, 3 `] `}`");&]
[s7; &]
[s7; DUMP(j);&]
[s0; &]
[s17; j `= `{ array: `[1, 2, 3`] `}&]
[s0; &]
[s0; &]
[s7; j(`"value`") `= m;&]
[s7; &]
[s7; DUMP(AsJSON(j));&]
[s0; &]
[s17; AsJSON(j) `= `{`"array`":`[1,2,3`],`"value`":`{`"one`":1,`"four`":4,`"three`":3,`"fiv
e`":5`}`}&]
[s0; &]
[s0; &]
[s7; j(`"array`").At(1) `= ValueMap()(`"key`", 1);&]
[s7; &]
[s7; DUMP(AsJSON(j));&]
[s0; &]
[s17; AsJSON(j) `= `{`"array`":`[1,`{`"key`":1`},3`],`"value`":`{`"one`":1,`"four`":4,`"thr
ee`":3,`"five`":5`}`}&]
[s0; &]
[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 
empty [*C@5 Function] is NOP, if [*C@5 Function] has return type 
[*C@5 T], it returns [*C@5 T()]. Second, [*C@5 Function] allows effective 
chaining of callable targets using [*C@5 operator<<], if [*C@5 Function] 
has return type, the return type of last callable appended is 
used.&]
[s5; Usually, the callable target is C`+`+11 lambda:&]
[s0; &]
[s7; Function<int (int)> fn `= `[`](int n) `{ LOG(`"Called A`"); 
return 3 `* n; `};&]
[s7; &]
[s7; LOG(`"About to call function`");&]
[s7; int n `= fn(7);&]
[s7; DUMP(n);&]
[s0; &]
[s17; About to call function&]
[s17; Called A&]
[s17; n `= 21&]
[s0; &]
[s5; If you chain another lambda into [*C@5 Function], all are called, 
but the last one`'s return value is used:&]
[s0; &]
[s7; fn << `[`](int n) `{ LOG(`"Called B`"); return n `* n; `};&]
[s7; LOG(`"About to call combined function`");&]
[s7; n `= fn(7);&]
[s7; DUMP(n);&]
[s0; &]
[s17; About to call combined function&]
[s17; Called A&]
[s17; Called B&]
[s17; n `= 49&]
[s0; &]
[s5; Invoking empty lambda does nothing and returns default constructed 
return value. This is quite useful for GUI classes, which have 
a lot of output events represented by [*C@5 Function] which are 
often unassigned to any action.&]
[s0; &]
[s7; fn.Clear();&]
[s7; LOG(`"About to call empty function`");&]
[s7; n `= fn(7);&]
[s7; DUMP(n);&]
[s0; &]
[s17; About to call empty function&]
[s17; n `= 0&]
[s0; &]
[s5; While using [*C@5 Function] with lambda expression is the most 
common, you can use any target that has corresponding [*C@5 operator()] 
defined:&]
[s0; &]
[s7; struct Functor `{&]
[s7; -|int operator()(int x) `{ LOG(`"Called Foo`"); return x % 2; 
`}&]
[s7; `};&]
[s7; &]
[s7; fn `= Functor();&]
[s7; LOG(`"About to call Functor`");&]
[s7; n `= fn(7);&]
[s7; DUMP(n);&]
[s0; &]
[s17; About to call Functor&]
[s17; Called Foo&]
[s17; n `= 1&]
[s0; &]
[s5; As [*C@5 Function] with [*C@5 void] and [*C@5 bool] return types are 
the most frequently used, U`+`+ defines template aliases [*C@5 Event]:&]
[s0; &]
[s7; Event<> ev `= `[`] `{ LOG(`"Event invoked`"); `};&]
[s7; &]
[s7; ev();&]
[s0; &]
[s17; Event invoked&]
[s0; &]
[s5; and [*C@5 Gate]:&]
[s0; &]
[s7; Gate<int> gt `= `[`](int x) `{ LOG(`"Gate invoked with `" << 
x); return x < 10; `};&]
[s7; &]
[s7; bool b `= gt(9);&]
[s7; DUMP(b);&]
[s7; b `= gt(10);&]
[s7; DUMP(b);&]
[s0; &]
[s17; Gate invoked with 9&]
[s17; b `= true&]
[s17; Gate invoked with 10&]
[s17; b `= false&]
[s0; &]
[s5; Using lambda to define calls to methods with more parameters 
can be verbose and error`-prone. The issue can be simplified 
by using [*C@5 THISFN] macro:&]
[s0; &]
[s7; struct Foo `{&]
[s7; -|void Test(int a, const String`& b) `{ LOG(`"Foo`::Test `" << 
a << `", `" << b); `}&]
[s7; -|&]
[s7; -|typedef Foo CLASSNAME; // required for THISFN&]
[s7; -|&]
[s7; -|void Do() `{&]
[s7; -|-|Event<int, const String`&> fn;&]
[s7; -|-|&]
[s7; -|-|fn `= `[`=`](int a, const String`& b) `{ Test(a, b); `};&]
[s7; -|-|fn(1, `"using lambda`");&]
[s7; -|-|&]
[s7; -|-|fn `= THISFN(Test); // this is functionally equivalent, but 
less verbose&]
[s7; -|-|fn(2, `"using THISFN`");&]
[s7; -|`}&]
[s7; `};&]
[s7; &]
[s7; Foo f;&]
[s7; f.Do();&]
[s0; &]
[s17; Foo`::Test 1, using lambda&]
[s17; Foo`::Test 2, using THISFN&]
[s0; &]
[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; &]
[s7; Vector<int> x`{ 1, 2 `};&]
[s7; Array<String> y`{ `"one`", `"two`" `};&]
[s7; Event<> ev `= `[x `= pick(x), y `= clone(y)`] `{ DUMP(x); DUMP(y); 
`};&]
[s7; &]
[s7; DUMP(x); // x is picked, so empty&]
[s7; DUMP(y); // y was cloned, so it retains original value&]
[s7; &]
[s7; LOG(`"About to invoke event`");&]
[s7; &]
[s7; ev();&]
[s0; &]
[s17; x `= `[`]&]
[s17; y `= `[one, two`]&]
[s17; About to invoke event&]
[s17; x `= `[1, 2`]&]
[s17; y `= `[one, two`]&]
[s0; &]
[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 
needs a cleanup call at the the thread exit, which is naturally 
implemented into [*C@5 Upp`::Thread]. Second `'hard`' reason is 
that Microsoft compiler is using Win32 API function for condition 
variable that are not available for Windows XP, while U`+`+ has 
alternative implementation for Windows XP, thus making executable 
compatible with it.&]
[s5; Then of course we believe U`+`+ multithreading / parallel programming 
support is easier to use and leads to higher performance...&]
[s5; [*C@5 Thread] class can start the thread and allows launching 
thread to [*C@5 Wait] for its completion:&]
[s0; &]
[s7; Thread t;&]
[s7; t.Run(`[`] `{&]
[s7; -|for(int i `= 0; i < 10; i`+`+) `{&]
[s7; -|-|LOG(`"In the thread `" << i);&]
[s7; -|-|Sleep(100);&]
[s7; -|`}&]
[s7; -|LOG(`"Thread is ending...`");&]
[s7; `});&]
[s7; for(int i `= 0; i < 5; i`+`+) `{&]
[s7; -|LOG(`"In the main thread `" << i);&]
[s7; -|Sleep(100);&]
[s7; `}&]
[s7; LOG(`"About to wait for thread to finish`");&]
[s7; t.Wait();&]
[s7; LOG(`"Wait for thread done`");&]
[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 main thread 2&]
[s17; In the thread 2&]
[s17; In the main thread 3&]
[s17; In the thread 3&]
[s17; In the thread 4&]
[s17; In the main thread 4&]
[s17; About to wait for thread to finish&]
[s17; In the thread 5&]
[s17; In the thread 6&]
[s17; In the thread 7&]
[s17; In the thread 8&]
[s17; In the thread 9&]
[s17; Thread is ending...&]
[s17; Wait for thread done&]
[s0; &]
[s5; [*C@5 Thread] destructor calls [*C@5 Detach] method with `'disconnects`' 
[*C@5 Thread] from the thread. Thread continues running.&]
[s5; [*C@5 Thread`::Start] static method launches a thread without possibility 
to wait for its completion; if you need to wait, you have to 
use some other method:&]
[s0; &]
[s7; bool x `= false;&]
[s7; &]
[s7; Thread`::Start(`[`&x`] `{ LOG(`"In the Started thread`"); x `= 
true; `});&]
[s7; &]
[s7; LOG(`"About to wait for thread to finish`");&]
[s7; while(!x) `{ Sleep(1); `} // Do not do this in real code!&]
[s7; LOG(`"Wait for thread done`");&]
[s0; &]
[s17; About to wait for thread to finish&]
[s17; In the Started thread&]
[s17; Wait for thread done&]
[s0; &]
[s5; (method used here is horrible, but should demonstrate the point).&]
[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 
code demonstrates why:&]
[s0; &]
[s7; Thread t;&]
[s7; &]
[s7; int sum `= 0;&]
[s7; t.Run(`[`&sum`] `{&]
[s7; -|for(int i `= 0; i < 1000000; i`+`+)&]
[s7; -|-|sum`+`+;&]
[s7; `});&]
[s7; &]
[s7; for(int i `= 0; i < 1000000; i`+`+)&]
[s7; -|sum`+`+;&]
[s7; &]
[s7; t.Wait();&]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 1052016&]
[s0; &]
[s5; While the expected value is 2000000, produced value is different. 
The problem is that both thread read / modify / write [*C@5 sum] 
value without any locking. Using [*C@5 Mutex] locks the [*C@5 sum] 
and thus serializes access to it `- read / modify / write sequence 
 is now exclusive for the thread that has [*C@5 Mutex] locked, 
this fixing the issue. [*C@5 Mutex] can be locked / unlocked with 
[*C@5 Enter] / [*C@5 Leave] methods. Alternatively, [*C@5 Mutex`::Lock] 
helper class locks [*C@5 Mutex] in constructor and unlocks it in 
destructor:&]
[s0; &]
[s7; Mutex m;&]
[s7; sum `= 0;&]
[s7; t.Run(`[`&sum, `&m`] `{&]
[s7; -|for(int i `= 0; i < 1000000; i`+`+) `{&]
[s7; -|-|m.Enter();&]
[s7; -|-|sum`+`+;&]
[s7; -|-|m.Leave();&]
[s7; -|`}&]
[s7; `});&]
[s7; &]
[s7; for(int i `= 0; i < 1000000; i`+`+) `{&]
[s7; -|Mutex`::Lock `_`_(m); // Lock m till the end of scope&]
[s7; -|sum`+`+;&]
[s7; `}&]
[s7; &]
[s7; t.Wait();&]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 2000000&]
[s0; &]
[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 
is to be blocked, [*C@5 Mutex] has to locked; call to [*C@5 Wait] 
atomically unlocks the [*C@5 Mutex] and puts the thread to waiting. 
Another thread then can resume the thread by calling [*C@5 Signal], 
which also causes [*C@5 Mutex] to lock again. Multiple threads 
can be waiting on single [*C@5 ConditionVariable]; [*C@5 Signal] 
resumes single waiting thread, [*C@5 Brodcast] resumes all waitng 
threads.&]
[s0; &]
[s7; bool  stop `= false;&]
[s7; BiVector<int> data;&]
[s7; Mutex m;&]
[s7; ConditionVariable cv;&]
[s7; &]
[s7; Thread t;&]
[s7; t.Run(`[`&stop, `&data, `&m, `&cv`] `{&]
[s7; -|Mutex`::Lock `_`_(m);&]
[s7; -|for(;;) `{&]
[s7; -|-|while(data.GetCount()) `{&]
[s7; -|-|-|int q `= data.PopTail();&]
[s7; -|-|-|LOG(`"Data received: `" << q);&]
[s7; -|-|`}&]
[s7; -|-|if(stop)&]
[s7; -|-|-|break;&]
[s7; -|-|cv.Wait(m);&]
[s7; -|`}&]
[s7; `});&]
[s7; &]
[s7; for(int i `= 0; i < 10; i`+`+) `{&]
[s7; -|`{&]
[s7; -|-|Mutex`::Lock `_`_(m);&]
[s7; -|-|data.AddHead(i);&]
[s7; -|`}&]
[s7; -|cv.Signal();&]
[s7; -|Sleep(1);&]
[s7; `}&]
[s7; stop `= true;&]
[s7; cv.Signal();&]
[s7; t.Wait();&]
[s0; &]
[s17; Data received: 0&]
[s17; Data received: 1&]
[s17; Data received: 2&]
[s17; Data received: 3&]
[s17; Data received: 4&]
[s17; Data received: 5&]
[s17; Data received: 6&]
[s17; Data received: 7&]
[s17; Data received: 8&]
[s17; Data received: 9&]
[s0; &]
[s5; Important note: rarely thread can be resumed from [*C@5 Wait] even 
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`_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 
duration of program run. [*C@5 CoWork] instances then manage assigning 
jobs to these worker threads and waiting for the all work to 
finish.&]
[s5; Job units to [*C@5 CoWork] are represented by [*C@5 Function<void 
()>] and thus can be written inline as lambdas.&]
[s5; As an example, following code reads input file by lines, splits 
lines into words (this is the parallelized work) and then adds 
resulting words to [*C@5 Index]:&]
[s0; &]
[s7; FileIn in(GetDataFile(`"test.txt`")); // let us open some tutorial 
testing data&]
[s7; &]
[s7; Index<String> w;&]
[s7; Mutex m; // need mutex to serialize access to w&]
[s7; &]
[s7; CoWork co;&]
[s7; while(!in.IsEof()) `{&]
[s7; -|String ln `= in.GetLine();&]
[s7; -|co `& `[ln, `&w, `&m`] `{&]
[s7; -|-|Vector<String> h `= Split(ln, `[`](int c) `{ return IsAlpha(c) 
? 0 : c; `});&]
[s7; -|-|Mutex`::Lock `_`_(m);&]
[s7; -|-|for(const auto`& s : h)&]
[s7; -|-|-|w.FindAdd(s);&]
[s7; -|`};&]
[s7; `}&]
[s7; co.Finish();&]
[s7; &]
[s7; DUMP(w);&]
[s0; &]
[s17; w `= `[Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, 
elit, ullamco, laboris, nisi, ut, aliquip, ex, ea, commodo, consequat, 
aliqua, Ut, enim, ad, minim, veniam, quis, nostrud, exercitation, 
esse, cillum, dolore, eu, fugiat, nulla, pariatur, Excepteur, 
sint, occaecat, cupidatat, non, proident, sunt, in, culpa, qui, 
sed, do, eiusmod, tempor, incididunt, labore, et, magna, Duis, 
aute, irure, reprehenderit, voluptate, velit, officia, deserunt, 
mollit, anim, id, est, laborum`]&]
[s0; &]
[s5; Adding words to [*C@5 w] requires [*C@5 Mutex]. Alternative to this 
`'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()) `{&]
[s7; -|String ln `= in.GetLine();&]
[s7; -|co `& `[ln, `&w, `&m`] `{&]
[s7; -|-|Vector<String> 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; DUMP(w);&]
[s0; &]
[s17; w `= `[Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, 
elit, ullamco, laboris, nisi, ut, aliquip, ex, ea, commodo, consequat, 
aliqua, Ut, enim, ad, minim, veniam, quis, nostrud, exercitation, 
esse, cillum, dolore, eu, fugiat, nulla, pariatur, Excepteur, 
sint, occaecat, cupidatat, non, proident, sunt, in, culpa, qui, 
sed, do, eiusmod, tempor, incididunt, labore, et, magna, Duis, 
aute, irure, reprehenderit, voluptate, velit, officia, deserunt, 
mollit, anim, id, est, laborum`]&]
[s0; &]
[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<String> 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 
wise:&]
[s0; &]
[s7; Vector<int> data;&]
[s7; for(int i `= 0; i < 10000; i`+`+)&]
[s7; -|data.Add(i);&]
[s7; &]
[s7; int sum `= 0;&]
[s7; &]
[s7; CoWork co;&]
[s7; for(int i `= 0; i < data.GetCount(); i`+`+)&]
[s7; -|co `& `[i, `&sum, `&data`] `{ CoWork`::FinLock(); sum `+`= data`[i`]; 
`};&]
[s7; co.Finish();&]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 49995000&]
[s0; &]
[s5; Above code computes the sum of all elements in the [*C@5 Vector], 
using CoWorker job for each element. While producing the correct 
result, it is likely to run much slower than single`-threaded 
version.&]
[s5; The solution to the problem is to split the array into small 
number of larger subranges that are processed in parallel. This 
is what [*C@5 CoPartition] template algorithm does:&]
[s0; &]
[s7; sum `= 0;&]
[s7; CoPartition(data, `[`&sum`](const auto`& subrange) `{&]
[s7; -|int partial`_sum `= 0;&]
[s7; -|for(const auto`& x : subrange)&]
[s7; -|-|partial`_sum `+`= x;&]
[s7; -|CoWork`::FinLock(); // available as CoPartition uses CoWork&]
[s7; -|sum `+`= partial`_sum;&]
[s7; `});&]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 49995000&]
[s0; &]
[s5; Note that CoWork is still internally used, so [*C@5 CoWork`::FinLock] 
is available. Instead of working on subranges, it is also possible 
to use iterators:&]
[s0; &]
[s7; sum `= 0;&]
[s7; CoPartition(data.begin(), data.end(), `[`&sum`] (auto l, auto 
h) `{&]
[s7; -|int partial`_sum `= 0;&]
[s7; -|while(l !`= h)&]
[s7; -|-|partial`_sum `+`= `*l`+`+;&]
[s7; -|CoWork`::FinLock(); // available as CoPartition uses CoWork&]
[s7; -|sum `+`= partial`_sum;&]
[s7; `});&]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 49995000&]
[s0; &]
[s5; There is no requirement on the type of iterators, so it is even 
possible to use just indices:&]
[s0; &]
[s7; sum `= 0;&]
[s7; CoPartition(0, data.GetCount(), `[`&sum, `&data`] (int l, int 
h) `{&]
[s7; -|int partial`_sum `= 0;&]
[s7; -|while(l !`= h)&]
[s7; -|-|partial`_sum `+`= data`[l`+`+`];&]
[s7; -|CoWork`::FinLock(); // available as CoPartition uses CoWork&]
[s7; -|sum `+`= partial`_sum;&]
[s7; `});&]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 49995000&]
[s0; &]
[s3;H4;:Section`_7`_7: 7.7 CoWork loop method&]
[s5; An alternative to [*C@5 CoPartition] is `'loop`' method of [*C@5 CoWork] 
([*C@5 Loop] method with synonym [*C@5 operator`*]). In this pattern, 
the job is simply started in all threads and the code is responsible 
for scheduling the work. [*C@5 Loop] / [*C@5 operator`*] waits for 
all started threads to finish. For scheduling, it is possible 
to use internal [*C@5 CoWork] index counter, which is set to 0 
at the start of [*C@5 Loop] / [*C@5 operator`*]. This way, the overhead 
associated with creating lambdas and scheduling them is kept 
to the minimum.&]
[s0; &]
[s7; Vector<String> data;&]
[s7; for(int i `= 0; i < 100; i`+`+)&]
[s7; -|data.Add(AsString(1.0 / i));&]
[s7; &]
[s7; double sum `= 0;&]
[s7; &]
[s7; CoWork co;&]
[s7; co `* `[`&`] `{&]
[s7; -|int i;&]
[s7; -|double m `= 0;&]
[s7; -|while((i `= co.Next()) < data.GetCount())&]
[s7; -|-|m `+`= atof(data`[i`]);&]
[s7; -|CoWork`::FinLock();&]
[s7; -|sum `+`= m;&]
[s7; `};&]
[s7; &]
[s7; DUMP(sum);&]
[s0; &]
[s17; sum `= 1e299&]
[s0; &]
[s3;H4;:Section`_7`_8: 7.8 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.&]
[s5; So the parallel version of e.g. [*C@5 FindIndex] is [*C@5 CoFindIndex], 
for [*C@5 Sort] it is [*C@5 CoSort]:&]
[s0; &]
[s7; Vector<String> x`{ `"zero`", `"one`", `"two`", `"three`", `"four`", 
`"five`" `};&]
[s7; &]
[s7; DUMP(FindIndex(x, `"two`"));&]
[s7; DUMP(CoFindIndex(x, `"two`"));&]
[s7; &]
[s7; CoSort(x);&]
[s7; DUMP(x);&]
[s0; &]
[s17; FindIndex(x, `"two`") `= 2&]
[s17; CoFindIndex(x, `"two`") `= 2&]
[s17; x `= `[five, four, one, three, two, zero`]&]
[s0; &]
[s5; Caution should be exercised when using these algorithms `- for 
small datasets, they are almost certainly slower than single`-threaded 
versions.&]
[s5; ]]