From dd88feaf2eb3bf6c673dda868dfbfa390db10b69 Mon Sep 17 00:00:00 2001 From: cxl Date: Mon, 12 Dec 2016 10:06:03 +0000 Subject: [PATCH] New Core Tutorial git-svn-id: svn://ultimatepp.org/upp/trunk@10537 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/srcdoc.tpp/Caveats$en-us.tpp | 22 +- uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp | 2 +- uppsrc/Core/srcdoc.tpp/CoreTutorial$en-us.tpp | 580 ---- uppsrc/Core/srcdoc.tpp/Tutorial$en-us.tpp | 2982 +++++++++++++---- 4 files changed, 2358 insertions(+), 1228 deletions(-) delete mode 100644 uppsrc/Core/srcdoc.tpp/CoreTutorial$en-us.tpp diff --git a/uppsrc/Core/srcdoc.tpp/Caveats$en-us.tpp b/uppsrc/Core/srcdoc.tpp/Caveats$en-us.tpp index 7debb3cde..35780b3c0 100644 --- a/uppsrc/Core/srcdoc.tpp/Caveats$en-us.tpp +++ b/uppsrc/Core/srcdoc.tpp/Caveats$en-us.tpp @@ -87,8 +87,7 @@ the GUI init sequence.&] [s0; [C@5 `{]&] [s0; [C@5 ...]&] [s0; [C@5 `}]&] -[s0;C@5 &] -[s0; [C@5 `*.icpp]] +[s0;C@5 ] ::^l/25r/25t/15b/15 [s0; Upp provides a nice solution to do init / deinit work of your package`'s static or global stuff (i.e. if it`'s not Plain Ol`' Data and needs some function calls).&] @@ -103,11 +102,18 @@ won`'t be invoked.&] itself in an init facility from upper Upp code. So no code ref downwards is added.&] [s0; &] +[s0; [/ Deprecated Solution:]&] +[s0; [/ If nothing else references some code in the file, make it a +.icpp file.]&] +[s0;/ &] +[s0; [/ .icpp files are forced to be linked no matter what. See ][/^topic`:`/`/ide`/app`/Files`$en`-us^ f +iles ][/ description section in manual.]&] +[s0;/ &] [s0; Solution:&] -[s0; If nothing else references some code in the file, make it a -.icpp file.&] +[s0; Use INITIALIZE([/ MyName]); in package header and INITIALIZER(PNGRaster) +`{ .. `} in .cpp where INITBLOCK / EXITBLOCK are. Possibly, you +can replace INITBLOCK by INITIALIZER body. Including header file +into main project ensures that file with INITIALIZER will be +linked into binary.]}}&] [s0; &] -[s0; .icpp files are forced to be linked no matter what. See [^topic`:`/`/ide`/app`/Files`$en`-us^ f -iles ]description section in manual]}}&] -[s0; &] -[s0; ] \ No newline at end of file +[s0; ]] \ No newline at end of file diff --git a/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp b/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp index 42f49ac7c..263db749e 100644 --- a/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp +++ b/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp @@ -130,7 +130,7 @@ String operator`+: String a; ... String x `= a`+ `" foo `" `+ [s3; Smaller issues&] [s5; Stream`::SerializeRaw count is now int64 (instead of 32`-bit int)&] -[s5; 2G items limit is better implemented in StringBuffer, WStringBuffer, +[s5; 2GB items limit is better implemented in StringBuffer, WStringBuffer, StringStream.&] [s5; StringStream now has SizeLimit option, if breached (when storing data to StringStream), exception is thrown.&] diff --git a/uppsrc/Core/srcdoc.tpp/CoreTutorial$en-us.tpp b/uppsrc/Core/srcdoc.tpp/CoreTutorial$en-us.tpp deleted file mode 100644 index 574421eb2..000000000 --- a/uppsrc/Core/srcdoc.tpp/CoreTutorial$en-us.tpp +++ /dev/null @@ -1,580 +0,0 @@ -topic "U++ Core value types tutorial"; -[2 $$0,0#00000000000000000000000000000000:Default] -[l288;i1120;a17;O9;~~~.1408;2 $$1,0#10431211400427159095818037425705:param] -[a83;*R6 $$2,5#31310162474203024125188417583966:caption] -[H4;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;t246;C@5;1 $$7,7#20902679421464641399138805415013:code] -[b2503;2 $$8,0#65142375456100023862071332075487:separator] -[*@(0.0.255)2 $$9,0#83433469410354161042741608181528:base] -[t4167;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,0#03451589433145915344929335295360:result] -[l321;b83;a83;*C$7;2 $$17,17#07531550463529505371228428965313:result`-line] -[l160;t4167;*C+117 $$18,5#88603949442205825958800053222425:package`-title] -[2 $$19,0#53580023442335529039900623488521:gap] -[t4167;C2 $$20,20#70211524482531209251820423858195:class`-nested] -[b50;2 $$21,21#03324558446220344731010354752573:Par] -[{_}%EN-US -[s2; U`+`+ Core value types tutorial&] -[s3; 1. String&] -[s5; String is a value type useful for storing text or binary data. -Content of String can be also obtained in form zero terminated -const char `*ptr (valid till the next mutating operation only.&] -[s5; You can assign a C string literal to String&] -[s5; &] -[s7; String a;&] -[s7; a `= `"Hello`";&] -[s7; &] -[s16; a `= Hello&] -[s7; &] -[s5; &] -[s5; concatenate with another String or literal&] -[s5; &] -[s7; a `= a `+ `" world`";&] -[s7; &] -[s16; a `= Hello world&] -[s7; &] -[s5; &] -[s5; or single character or specified number of characters from another -String or literal&] -[s5; &] -[s7; a.Cat(`'!`');&] -[s7; &] -[s16; a `= Hello world!&] -[s7; &] -[s7; a.Cat(`"ABCDEFGHIJKLM`", 3);&] -[s7; &] -[s16; a `= Hello world!ABC&] -[s7; &] -[s5; &] -[s5; Clear method empties the String&] -[s5; &] -[s7; a.Clear();&] -[s5; &] -[s5; You can use operator<< to append to existing String. Note that -this is more efficient form than operator`+ as String is not -required to be assigned a temporary. Also, U`+`+ provides extensible -framework for converting values to String&] -[s5; &] -[s7; for(int i `= 0; i < 10; i`+`+)&] -[s7; -|a << i << `", `";&] -[s7; &] -[s16; a `= 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &] -[s5; &] -[s5; Sometimes is is useful to use operator<< to produce a temporary -String value (e.g. as real argument to function call).&] -[s5; &] -[s7; String().Cat() << `"Number is `" << 123 << `".`"&] -[s5; &] -[s5; Note: This strange special Cat method is needed because C`+`+ -does not allow non`-cont references to temporary objects.&] -[s5; &] -[s16; Number is 123.&] -[s5; &] -[s5; String provides methods for obtaining character count, inserting -characters into String or removing them&] -[s5; &] -[s7; a `= `"0123456789`";&] -[s7; &] -[s16; a.GetLength() `= 10&] -[s7; &] -[s7; a.Insert(6, `"`");&] -[s7; &] -[s16; a `= 0123456789&] -[s7; &] -[s7; a.Remove(2, 2);&] -[s7; &] -[s16; a `= 01456789&] -[s7; &] -[s5; &] -[s5; as well as a couple of searching and comparing methods&] -[s5; &] -[s7; a.Find(`'e`')&] -[s7; &] -[s16; a.Find(`'e`') `= 8&] -[s7; &] -[s7; a.ReverseFind(`'e`')&] -[s7; &] -[s16; a.ReverseFind(`'e`') `= 11&] -[s16; &] -[s7; a.StartsWith(`"ABC`")&] -[s7; &] -[s16; a.StartsWith(`"ABC`") `= false&] -[s7; &] -[s7; a.EndsWith(`"KLM`")&] -[s7; &] -[s16; a.EndsWith(`"KLM`") `= false&] -[s7; &] -[s7; a.Find(`"ted`")&] -[s7; &] -[s16; a.Find(`"ted`") `= 10&] -[s5; &] -[s5; You can get slice of String using Mid method; with single parameter -it provides slice to the end of String&] -[s5; &] -[s7; a.Mid(3, 3)&] -[s7; &] -[s16; a.Mid(3, 3) `= 56789&] -[s5; &] -[s5; You can also decrease the length of String using Trim&] -[s5; &] -[s7; a.Trim(4);&] -[s7; &] -[s16; a `= 0145&] -[s5; &] -[s5; You can obtain int values of individual characters using operator`[`]&] -[s5; &] -[s7; a`[0`]&] -[s7; &] -[s16; a`[0`] `= 48&] -[s5; &] -[s5; or the value of first character using operator`* (note that -if GetLengt() `=`= 0, this will return zero terminator)&] -[s5; &] -[s7; -|DUMP(`*a);&] -[s7; &] -[s16; `*a `= 48&] -[s5; &] -[s3; 2. StringBuffer&] -[s5; If you need a direct write access to String`'s C`-string character -buffer, you can use complementary StringBuffer class. One of -reasons to do so is when you have to deal with some C`-API functions&] -[s5; &] -[s7; void CApiFunction(char `*c)&] -[s7; `{&] -[s7; -|strcpy(c, `"Hello`");&] -[s7; `}&] -[s7; &] -[s7; ..........&] -[s7; &] -[s7; [* -|StringBuffer] b;&] -[s7; -|b.[* SetLength](200);&] -[s7; -|CApiFunction(b);&] -[s7; -|b.[* Strlen]();&] -[s7; -|String x [* `=] b;&] -[s7; &] -[s16; x `= Hello&] -[s5; &] -[s5; In this case, SetLength creates a C array of 200 characters. -You can then call C`-API function. Later you set the real length -using Strlen `- this function performs strlen of buffer and sets -the length accordingly. Later you simply assign the StringBuffer -to String. Note that for performance reasons, this operation -clears the StringBuffer content (operation is fast and does not -depend on the number of characters).&] -[s5; Another usage scenario of StringBuffer is about altering existing -String&] -[s5; &] -[s7; -|b `= x;&] -[s7; -|b`[1`] `= `'a`';&] -[s7; -|x `= b;&] -[s7; &] -[s16; x `= Hallo&] -[s5; &] -[s5; Similar to assigning StringBuffer to String, assigning String -to StringBuffer clears the source String.&] -[s5; StringBuffer also provides appending operations:&] -[s7; -|b `= x;&] -[s7; -|b.Cat(`'!`');&] -[s7; -|x `= b;&] -[s7; &] -[s16; x `= Hallo!&] -[s16; &] -[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.&] -[s5; &] -[s3; 3. WString&] -[s5; String works with 8 bit characters. For 16`-bit character encoding -use WString. Both classes are closely related and share most -of interface methods. U`+`+ also provides conversions between -String and WString and you can also use 8 bit string literals -with WString. Conversion is ruled by current [*/ default character -set][/ . ]Default value of default character set is CHARSET`_UTF8.&] -[s5; &] -[s7; -|WString x `= `"characters 280`-300: `";&] -[s7; -|for(int i `= 280; i < 300; i`+`+)&] -[s7; -|-|x.Cat(i);&] -[s7; &] -[s16; x `= characters 280`-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī&] -[s7; &] -[s7; -|String y `= x.ToString();&] -[s7; -|DUMP(y);&] -[s7; &] -[s16; y `= characters 280`-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī&] -[s7; &] -[s7; -|y.Cat(`" (appended)`");&] -[s7; -|x `= y.ToWString();&] -[s7; &] -[s16; x `= characters 280`-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī -(appended)-|&] -[s5; &] -[s5; [/ (Note: y content is displayed as result of conversion from utf`-8 -encoded data)]&] -[s5; &] -[s3; 4. Date and Time&] -[s5; To represent date and time, U`+`+ provides Date and Time concrete -types.&] -[s5; &] -[s7; -|Date date `= GetSysDate();&] -[s0; &] -[s16; date `= 01/21/2007&] -[s5; &] -[s5; All data members of Date structure are public:&] -[s5; &] -[s16; date.year `= 2007&] -[s16; date.month `= 1&] -[s16; date.day `= 21&] -[s5; &] -[s5; Dates can be compared:&] -[s5; &] -[s16; date > Date(1970, 1, 1) `= true&] -[s5; &] -[s5; Adding a number to Date adds a number of days to it:&] -[s5; &] -[s16; date `+ 1 `= 01/22/2007&] -[s5; &] -[s5; Subtraction of dates yields a number of days between them:&] -[s5; &] -[s16; date `- Date(1970, 1, 1) `= 13534&] -[s5; &] -[s5; U`+`+ defines the beginning and the end of era, most algorithms -can safely assume that as minimal and maximal values Date can -represent:&] -[s0; &] -[s16; Date`::Low() `= 01/01/`-4000&] -[s16; Date`::High() `= 01/01/4000&] -[s0; -|&] -[s5; &] -[s5; Time is [/ derived] from Date, adding members to represent time:&] -[s5; &] -[s7; -|Time time `= GetSysTime();&] -[s5; &] -[s16; time `= 01/21/2007 23:28:59&] -[s16; (Date`&)time `= 01/21/2007&] -[s16; (int)time.hour `= 23&] -[s16; (int)time.minute `= 28&] -[s16; (int)time.second `= 59&] -[s5; &] -[s5; Times can be compared:&] -[s5; &] -[s16; time > Time(1970, 0, 0) `= true&] -[s5; &] -[s5; Warning: As Time is derived from the Date, most operations automatically -convert Time back to Date. You have to use ToTime conversion -function to convert Date to Time:&] -[s5; &] -[s16; time > date `= false&] -[s16; time > ToTime(date) `= true&] -[s16; &] -[s5; &] -[s5; Like Date, Time supports add and subtract operations, but numbers -represent seconds (using int64 datatype):&] -[s5; &] -[s16; time `+ 1 `= 01/21/2007 23:29:00&] -[s16; time `+ 24 `* 3600 `= 01/22/2007 23:28:59&] -[s16; time `- date `= 0&] -[s16; time `- ToTime(date) `= 84539&] -[s5; &] -[s5; Time also defines era limits:&] -[s5; &] -[s16; Time`::Low() `= 01/01/`-4000 00:00:00&] -[s16; Time`::High() `= 01/01/4000 00:00:00&] -[s5;/ &] -[s3; 5. AsString, ToString and operator<<&] -[s5; U`+`+ Core provides simple yet effective standard schema for -converting values to default textual form.&] -[s5; System is based on the combination of template functions [/ (following -code is part of U`+`+ headers)]:&] -[s5; &] -[s7; template &] -[s7; inline String AsString(const T`& x)&] -[s7; `{&] -[s7; -|return x.ToString();&] -[s7; `}&] -[s7; &] -[s7; template &] -[s7; inline Stream`& operator<<(Stream`& s, const T`& x)&] -[s7; `{&] -[s7; -|s << AsString(x);&] -[s7; -|return s;&] -[s7; `}&] -[s7; &] -[s7; template &] -[s7; inline String`& operator<<(String`& s, const T`& x)&] -[s7; `{&] -[s7; -|s.Cat(AsString(x));&] -[s7; -|return s;&] -[s7; `}&] -[s5; &] -[s5; Client types have to either define String [*/ ToString] method -(usually more convenient) or specialize [*/ AsString] template. -Such types can be appended to Streams or Strings using [*/ operator<<]. -Of course, U`+`+ value types and primitive types have required -items predefined by U`+`+:&] -[s5; &] -[s7; -|FileOut fout(ConfigFile(`"test.txt`"));&] -[s7; -|String sout;&] -[s7; -|&] -[s7; -|fout << 1.23 << `' `' << GetSysDate() << `' `' << GetSysTime();&] -[s7; -|sout << 1.23 << `' `' << GetSysDate() << `' `' << GetSysTime();&] -[s7; &] -[s16; sout `= 1.23 01/22/2007 01/22/2007 17:58:58&] -[s5; &] -[s5; Getting client types involved into this schema is not too difficult -(example shows both methods):&] -[s7; struct BinFoo `{&] -[s7; -|int x;&] -[s7; -|&] -[s7; -|String [* ToString]() const `{ return FormatIntBase(x, 2); -`}&] -[s7; -|&] -[s7; -|BinFoo(int x) : x(x) `{`}&] -[s7; `};&] -[s7; &] -[s7; struct RomanFoo `{&] -[s7; -|int x;&] -[s7; -|&] -[s7; -|RomanFoo(int x) : x(x) `{`}&] -[s7; `};&] -[s7; &] -[s7; namespace Upp `{&] -[s7; template <>&] -[s7; String Upp`::[* AsString](const RomanFoo`& a) `{ return FormatIntRoman(a.x); -`}&] -[s7; `};&] -[s7; .......&] -[s7; &] -[s7; -|sout << BinFoo(30) << `' `' << RomanFoo(30);&] -[s7; &] -[s7; &] -[s16; sout `= 11110 xxx&] -[s5; &] -[s3; 6. Value&] -[s5; U`+`+ provides one special value type, Value, that can be used -to store and retrieve other values.&] -[s5; &] -[s7; -|Value a `= 1;&] -[s7; -|Value b `= 2.34;&] -[s7; -|Value c `= GetSysDate();&] -[s7; -|Value d `= `"hello`";&] -[s5; &] -[s5; Usually, value types define typecast operator to Value and constructor -from Value, so that interaction is for the most part seamless:&] -[s5; &] -[s7; -|int x `= a;&] -[s7; -|double y `= b;&] -[s7; -|Date z `= c;&] -[s7; -|String s `= d;&] -[s7; &] -[s16; x `= 1&] -[s16; y `= 2.34&] -[s16; z `= 01/24/2007&] -[s16; s `= hello&] -[s5; &] -[s5; As for primitive types, Value seamlessly works with [* int], [* int64], -[* bool] and [* double].&] -[s5; Casting Value to a type that it does not contain causes runtime -error. On the other hand, conversion between related types is -possible:&] -[s5; &] -[s7; &] -[s7; -|double i `= a;&] -[s7; -|int j `= b;&] -[s7; -|Time k `= c;&] -[s7; -|WString t `= d;&] -[s7; &] -[s16; i `= 1&] -[s16; j `= 2&] -[s16; k `= 01/24/2007&] -[s16; t `= hello&] -[s5; &] -[s5; To determine type of value stored in Value, you can use [* Is] -method:&] -[s5; &] -[s16; a.Is() `= true&] -[s16; a.Is() `= false&] -[s16; b.Is() `= true&] -[s16; c.Is() `= false&] -[s16; c.Is() `= true&] -[s16; d.Is() `= true&] -[s5; &] -[s5; Note that Is tests for absolute type match, not for compatible -types. For that reason, for widely used compatible types utility -functions are defined:&] -[s5; &] -[s16; &] -[s16; IsNumber(a) `= true&] -[s16; IsNumber(b) `= true&] -[s16; IsDateTime(c) `= true&] -[s16; IsString(d) `= true&] -[s5; &] -[s3; 7. Null&] -[s5; U`+`+ defines a special Null constant to represent an empty -value. This constant is convertible to many value types including -primitive types double, int and 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 IsNull function.&] -[s5; &] -[s7; -|int x `= Null;&] -[s7; -|int y `= 120;&] -[s7; -|Date d `= Null;&] -[s7; -|Date e `= GetSysDate();&] -[s7; &] -[s16; IsNull(x) `= true&] -[s16; IsNull(y) `= false&] -[s16; IsNull(d) `= true&] -[s16; e > d `= true&] -[s0; &] -[s5; C`+`+ language note: Null is the only instance of Nuller type. -Assigning Null to primitive types is achieved by cast operators -of Nuller, other types can do it using constructor from Nuller.&] -[s5; As a special case, if Value contains Null, it is convertible -to any value type that can contain Null:&] -[s5; &] -[s7; &] -[s7; -|Value v `= x;&] -[s7; -|e `= v;&] -[s7; &] -[s16; IsNull(e) `= true&] -[s5; &] -[s5; Function Nvl is U`+`+ analog of well known SQL function coalesce -(ifnull, Nvl), which returns the first non`-null argument (or -Null if all are Null).&] -[s7; &] -[s7; -|int a `= Null;&] -[s7; -|int b `= 123;&] -[s7; -|int c `= 1;&] -[s7; &] -[s16; Nvl(a, b, c) `= 123&] -[s5; &] -[s3; 8. Client types and Value, RawValue, RichValue&] -[s5; There are two Value compatibility levels. The simple one, [*/ RawValue], -has little requirements for the type used `- only copy constructor -and assignment operator are required:&] -[s5; &] -[s7; struct RawFoo `{&] -[s7; -|String x;&] -[s7; `};&] -[s7; &] -[s5; &] -[s5; (int this case, default copy constructor and assignment operator -are provided by compiler).&] -[s5; &] -[s7; &] -[s7; -|RawFoo h;&] -[s7; -|h.x `= `"hello`";&] -[s7; -|Value q `= RawToValue(h);&] -[s7; &] -[s16; q.Is() `= true&] -[s16; q.To().x `= `"hello`"&] -[s7; &] -[s5; [* RichValue] level Values provide more operations for Value `- -equality test, IsNull test, hashing, conversion to text and serialization. -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 ValueType base class template:&] -[s7; &] -[s7; struct Foo : ValueType `{&] -[s7; -|int x;&] -[s7; -|&] -[s7; -|Foo(const Nuller`&) `{ x `= Null; `}&] -[s7; -|Foo(int x) : x(x) `{`}&] -[s7; -|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; `};&] -[s7; &] -[s7; INITBLOCK `{&] -[s7; -|Value`::Register();&] -[s7; `}&] -[s7; .......&] -[s7; &] -[s7; -|Value a `= [* RichToValue](Foo(54321));&] -[s7; -|Value b `= RichToValue(Foo(54321));&] -[s7; &] -[s16; (a `=`= b) `= true&] -[s16; IsNull(a) `= false&] -[s16; v.Get() `= 54321&] -[s7; &] -[s7; -|String s `= StoreAsString(a);&] -[s7; -|LoadFromString(v, s);&] -[s7; &] -[s16; v.Get() `= 54321&] -[s5; &] -[s5; To avoid RichToValue and ValueTo calls, the client type can -also provide constructor from Value and cast operator to Value:&] -[s5; &] -[s7; struct Foo : ValueType `{&] -[s7; -|int x;&] -[s7; -|&] -[s7; ......&] -[s7; -|&] -[s7; -|operator Value() `{ return RichToValue(`*this); `}&] -[s7; -|Foo(const Value`& v) `{ `*this `= v.Get(); `}&] -[s7; `};&] -[s7; &] -[s7; ......&] -[s7; &] -[s7; -|Value c `= Foo(321);&] -[s7; -|Foo x `= c;&] -[s7; &] -[s16; x `= 123&] -[s5; &] -[s3; 9. CombineHash&] -[s5; To simplify providing of high quality hash codes for composite -types, U`+`+ provides CombineHash utility class. This class uses -GetHashValue function to gather hash codes of all values and -combines them to provide final hash value for composite type:&] -[s7; &] -[s7; struct Foo `{&] -[s7; -|String a;&] -[s7; -|int b;&] -[s7; -|&] -[s7; -|unsigned [* GetHashValue]() const `{ return [* CombineHash](a, -b); `}&] -[s7; `};&] -[s7; &] -[s5; Note that GetHashValue is defined as function template that -calls GetHashValue method of its argument, therefore defining -GetHashValue method defines GetHashValue function too.&] -[s7; &] -[s7; -|Foo x;&] -[s7; -|x.a `= `"world`";&] -[s7; -|x.b `= 22;&] -[s7; &] -[s16; GetHashValue(x) `= 4272824901&] -[s16; &] -[s7; -|x.a << `'!`';&] -[s7; &] -[s16; GetHashValue(x) `= 3378606405&] -[s5; &] -[s3; Recommended tutorials:&] -[s5; If you want to learn more, we have several tutorials that you -can find useful:&] -[s5;l160;i150;O0;~~~32; [^topic`:`/`/Core`/srcdoc`/Tutorial`$en`-us^ Containers -Tutorial] `- everything you should know about NTL (Not standard -Template Library) containers. This is the natural continuation -of issues raised in this article.&] -[s5;l160;i150;O0;~~~32; [^topic`:`/`/CtrlLib`/srcdoc`/Tutorial`$en`-us^ GUI -tutorial] `- learn about creating GUI application with U`+`+.]] \ No newline at end of file diff --git a/uppsrc/Core/srcdoc.tpp/Tutorial$en-us.tpp b/uppsrc/Core/srcdoc.tpp/Tutorial$en-us.tpp index c006534e0..499006a30 100644 --- a/uppsrc/Core/srcdoc.tpp/Tutorial$en-us.tpp +++ b/uppsrc/Core/srcdoc.tpp/Tutorial$en-us.tpp @@ -16,468 +16,1868 @@ topic "U++ Containers Tutorial"; [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;b83;a83;*C$7;2 $$17,17#07531550463529505371228428965313:result`-line] +[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] -[{_}%EN-US -[s2; U`+`+ Containers Tutorial&] -[s3; 1. Vector basics&] -[s5; Let us start with a simple [* Vector] of ints&] -[s7; -|[* Vector] v;&] -[s5; You can add elements to the Vector as parameters of the [* Add] +[b73;*2 $$22,5#07864147445237544204111237153677:subtitle] +[{_} +[s2;%% U`+`+ Core Tutorial&] +[s22; 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 0x01BEF974, size 0x3 `= 3&] +[s17; `+0 0x01BEF974 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 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 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; &] +[s22; 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 camn concatenate 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, `"`");&] +[s7; DUMP(a);&] +[s0; &] +[s17; a `= 0123456789&] +[s0; &] +[s0; &] +[s7; a.Remove(2, 2);&] +[s7; DUMP(a);&] +[s0; &] +[s17; a `= 01456789&] +[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) `= 56789&] +[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 0x01BEF9D4, size 0x5 `= 5&] +[s17; `+0 0x01BEF9D4 31 32 33 34 00 + 1234. &] +[s0; &] +[s22; 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.&] +[s22; 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; &] +[s22; 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 `= 12/12/2016&] +[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 `= 2016&] +[s17; (int)date.month `= 12&] +[s17; (int)date.day `= 12&] +[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 `= 12/13/2016&] +[s17; `-`-date `= 12/11/2016&] +[s17; `+`+date `= 12/12/2016&] +[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) `= 6190&] +[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) `= 1&] +[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) `= 12/31/2016&] +[s17; FirstDayOfMonth(date) `= 12/01/2016&] +[s17; LastDayOfYear(date) `= 12/31/2016&] +[s17; FirstDayOfYear(date) `= 01/01/2016&] +[s17; DayOfYear(date) `= 347&] +[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) `= 08/12/2018&] +[s17; GetMonths(date, date `+ 100) `= 3&] +[s17; GetMonthsP(date, date `+ 100) `= 4&] +[s17; AddYears(date, 2) `= 12/12/2018&] +[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 `= 12/12/2016 10:51:48&] +[s17; (Date)time `= 12/12/2016&] +[s17; (int)time.hour `= 10&] +[s17; (int)time.minute `= 51&] +[s17; (int)time.second `= 48&] +[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 `= 12/12/2016 10:51:49&] +[s17; time `+ 24 `* 3600 `= 12/13/2016 10:51:48&] +[s17; time `- date `= 0&] +[s17; time `- ToTime(date) `= 39108&] +[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; &] +[s22; 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 &] +[s7; -|inline String AsString(const T`& x)&] +[s7; -|`{&] +[s7; -|-|return x.ToString();&] +[s7; -|`}&] +[s7; -|&] +[s7; -|template &] +[s7; -|inline Stream`& operator<<(Stream`& s, const T`& x)&] +[s7; -|`{&] +[s7; -|-|s << AsString(x);&] +[s7; -|-|return s;&] +[s7; -|`}&] +[s7; -|&] +[s7; -|template &] +[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 12/12/2016 12/12/2016 +10:51:48&] +[s17; sout `= 1.23 12/12/2016 12/12/2016 10:51:48&] +[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; &] +[s22; 7. 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 `= 12/12/2016&] +[s17; d `= hello&] +[s17; x `= 1&] +[s17; y `= 2.34&] +[s17; z `= 12/12/2016&] +[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 `= 12/12/2016 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());&] +[s7; DUMP(a.Is());&] +[s7; DUMP(b.Is());&] +[s7; DUMP(c.Is());&] +[s7; DUMP(c.Is());&] +[s7; DUMP(d.Is());&] +[s0; &] +[s17; a.Is() `= true&] +[s17; a.Is() `= false&] +[s17; b.Is() `= true&] +[s17; c.Is() `= false&] +[s17; c.Is() `= true&] +[s17; d.Is() `= 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; &] +[s22; 8. [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; 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 Value contains Null, it is convertible +to any value type that can contain 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; &] +[s22; 9. 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());&] +[s0; &] +[s17; q.Is() `= 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().x);&] +[s0; &] +[s17; q.To().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 `{&] +[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(); +`}&] +[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(); // 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; &] +[s22; 10. 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; &] +[s22; 11. 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 `{&] +[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 `{&] +[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; 12. [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 v;&] +[s0; &] +[s5; You can add elements to the Vector as parameters of the Add method&] -[s7; -|v.[* Add](1);&] +[s0; &] +[s7; -|v.Add(1);&] [s7; -|v.Add(2);&] -[s7; -|v.Add(3);&] -[s5; To iterate Vector you can use indices&] -[s7; -|for(int i `= 0; i < v.[* GetCount](); i`+`+)&] -[s7; -|-|LOG(v[* `[]i[* `]]);&] -[s5; Alternative, U`+`+ also provides standard C`+`+ begin/end interface&] -[s7; -|for(auto q `= v.[* begin](), e `= v.[* end](); q !`= e; q`+`+)&] -[s7; -|-|LOG([* `*]q);&] -[s5; or with C`+`+11 range`-for syntax:&] +[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);&] -[s5;/ &] -[s5; [/ Note: LOG is diagnostics macro that outputs its argument to -the .log file in debug mode. Another similar macros we will use -in this tutorial are DUMP (similar to the LOG, but dumps the -source expression too) and DUMPC (dumps the content of the container).]&] -[s5;/ &] -[s3; 2. Vector operations&] -[s5; You can [* Insert] or [* Remove] elements at random positions of -Vector&] -[s7; -|Vector v;&] -[s7; -|v.Add(1);&] -[s7; -|v.Add(2);&] -[s7; -|&] -[s7; -|v.[* Insert](1, 10);&] -[s17; v `= `{ 1, 10, 2 `}&] -[s7; -|v.[* Remove](0);&] -[s17; v `= `{ 10, 2 `}&] -[s5; [* At] method returns element at specified position ensuring that -such a position exists. If there is not enough elements in Vector, +[s0; &] +[s17; 1&] +[s17; 2&] +[s17; 3&] +[s17; 4&] +[s17; 5&] +[s17; 6&] +[s17; 7&] +[s0; &] +[s22; 13. [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 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; 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 -At is present, newly added elements are initialized to this value. -As an example, we will create distribution of RandomValue with -unknown maximal value&] +[*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; -|v.Clear();&] -[s7; -|for(int i `= 0; i < 10000; i`+`+)&] -[s7; -|-|v.[* At](RandomValue(), 0)`+`+;&] -[s17; v `= `{ 958, 998, 983, 1012, 1013, 1050, 989, 998, 1007, 992 -`}&] -[s5; You can apply algorithms on containers, e.g. [* Sort]&] -[s7; -|[* Sort](v);&] -[s17; v `= `{ 958, 983, 989, 992, 998, 998, 1007, 1012, 1013, 1050 -`}&] -[s5; &] -[s3; 3. Transfer issues&] +[s7; DUMP(v);&] +[s0; &] +[s17; v `= `[1012, 1025, 1052, 925, 1025, 993, 1058, 1007, 945, 958`]&] +[s0; &] +[s5; You can apply algorithms on containers, e.g. Sort&] +[s0; &] +[s7; Sort(v);&] +[s7; &] +[s7; DUMP(v);&] +[s0; &] +[s17; v `= `[925, 945, 958, 993, 1007, 1012, 1025, 1025, 1052, 1058`]&] +[s0; &] +[s22; 14. Transfer issues&] [s5; Often you need to pass content of one container to another of -the same type. NTL containers always support [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ p -ick semantics] (equivalent of std`::move), and, depending on type +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 -sematics]. When transfering the value, you have to explicitly +semantics]. When transferring the value, you have to explicitly specify which one to use:&] -[s7; -|Vector v;&] -[s7; -|v.Add(1);&] -[s7; -|v.Add(2);&] +[s0; &] +[s7; Vector v`{ 1, 2 `};&] [s7; &] -[s7; -|Vector v1 [* `=] pick(v);&] -[s5; now source Vector [C v] is destroyed `- picked `- and you can -no longer access its content. This behaviour has many advantages. -First, it is consistently fast and in most cases, transfer of -value instead of full copy is exactly what you need. Second, -NTL containers can store elements that lack copy operation `- -in that case, pick transfer is the only option anyway.&] +[s7; DUMP(v);&] +[s7; &] +[s7; Vector 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 [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ c -lone]&] -[s7; -|v [* `=] clone(v1);&] -[s5; &] -[s3; 4. Client types&] +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> 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> sv;&] +[s7; sv.push`_back(`{1, 2, 3`});&] +[s7; for(auto i : sv) // invokes std`::vector 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> v2;&] +[s7; &] +[s7; v2 `= v;&] +[s7; &] +[s7; DUMP(v);&] +[s7; DUMP(v2);&] +[s0; &] +[s17; v `= `[1, 2`]&] +[s17; v2 `= `[1, 2`]&] +[s0; &] +[s22; 15. 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 Vector (and the Vector [^topic`:`/`/Core`/src`/Overview`$en`-us^ f +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&] -[s7; -|struct Distribution : [* Moveable] `{&] -[s7; -|-|String text;&] -[s7; -|-|Vector data;&] -[s7; &] -[s7; -|-|rval`_default(Distribution);&] -[s7; -|-|Distribution() `{`}&] -[s7; -|`};&] -[s5; Note that rval`_default macro is helper to restore default deleted -r`-value constructor in C`+`+11 and default constructor is also -default deleted in C`+`+11.&] -[s5; Now to add Distribution elements you cannot use Add with parameter, -because it requires elements to have default deep`-copy constructor -`- and Distribution does not have one, as Vector 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&] -[s7; -|Vector 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(rand() % n, 0)`+`+;&] -[s7; -|`}&] -[s7; &] -[s16; Test 5: `{ 2018, 1992, 2025, 1988, 1977 `}&] -[s16; Test 6: `{ 1670, 1682, 1668, 1658, 1646, 1676 `}&] -[s16; Test 7: `{ 1444, 1406, 1419, 1493, 1370, 1418, 1450 `}&] -[s16; Test 8: `{ 1236, 1199, 1245, 1273, 1279, 1302, 1250, 1216 `}&] -[s16; Test 9: `{ 1115, 1111, 1100, 1122, 1192, 1102, 1089, 1064, 1105 +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 `{&] +[s7; -|String text;&] +[s7; -|Vector data;&] +[s7; -|&] +[s7; -|String ToString() const `{ return text `+ `": `" `+ AsString(data); `}&] -[s16; Test 10: `{ 969, 956, 1002, 1023, 1006, 994, 1066, 1022, 929, -1033 `}&] -[s5; Another possibility is to use AddPick method, which uses pick`-constructor -instead of deep`-copy constructor. E.g. Distribution elements -might be generated by some function &] -[s7; -|Distribution CreateDist(int n);&] -[s5; and code for adding such elements to Vector then looks like&] -[s7; -|for(n `= 5; n <`= 10; n`+`+)&] -[s7; -|-|dist.[* AddPick](CreateDist(n));&] +[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`` +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 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: `[1981, 2070, 2012, 1994, 1943`]&] +[s17; -|`[1`] `= Test 6: `[1669, 1670, 1686, 1612, 1699, 1664`]&] +[s17; -|`[2`] `= Test 7: `[1391, 1419, 1446, 1454, 1401, 1453, 1436`]&] +[s17; -|`[3`] `= Test 8: `[1220, 1294, 1216, 1268, 1298, 1190, 1231, +1283`]&] +[s17; -|`[4`] `= Test 9: `[1133, 1105, 1116, 1096, 1071, 1121, 1122, +1129, 1107`]&] +[s17; -|`[5`] `= Test 10: `[1013, 984, 968, 1048, 965, 942, 1005, 1046, +1069, 960`]&] +[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&] -[s7; -|-|dist.Add() `= CreateDist(); // alternative&] -[s5; &] -[s3; 5. Array flavor&] -[s5; If elements do not satisfy requirements for Vector flavor, they -can still be stored in Array flavor. Another reason for using -Array is need for referencing elements `- Array flavor never -invalidates references or pointers to them.&] +[s0; &] +[s7; -|dist.Add() `= CreateDist();&] +[s0; &] +[s22; 16. 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 -STL containers (STL does not specify the NTL flavor for obvious -reasons):&] -[s7; -|[* Array]< std`::list > al;&] -[s7; -|for(int i `= 0; i < 4; i`+`+) `{&] -[s7; -|-|std`::list`& l `= al.Add();&] -[s7; -|-|for(int q `= 0; q < i; q`+`+)&] -[s7; -|-|-|l.push`_back(q);&] -[s7; -|`}&] -[s5; &] -[s3; 6. Polymorphic Array&] -[s5; Array can even be used for storing polymorphic elements &] +standard library objects like [*C@5 std`::string] (because obviously, +standard library knows nothing about U`+`+ Moveable concept):&] +[s0; &] +[s7; Array 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; &] +[s22; 17. 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; &] [s7; -|String ToString() const `{ return AsString(Get()); `}&] -[s7; -|&] [s7; -|virtual `~Number() `{`}&] [s7; `};&] [s7; &] [s7; struct Integer : public Number `{&] [s7; -|int n;&] [s7; -|virtual double Get() const `{ return n; `}&] -[s7; &] -[s7; -|Integer() `{`}&] [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 num;&] +[s7; num.Create().n `= 15.5;&] +[s7; num.Create().n `= 3;&] [s7; &] -[s7; -|Double() `{`}&] +[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; &] +[s22; 18. 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 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; bool operator<(const Number`& a, const Number`& b)&] -[s7; `{&] -[s7; -|return a.Get() < b.Get();&] -[s7; `}&] +[s7; struct Number : Val `{&] +[s7; -|int n;&] +[s7; -|virtual String ToString() const `{ return AsString(n); `}&] +[s7; `};&] [s7; &] -[s5; In this case, elements are added using Add with pointer to base -element type parameter. Do not be confused by new and pointer, -Array takes ownership of passed object and behaves like container -of base type elements&] -[s7; -|Array num;&] -[s7; -|num.Create().n `= 15.5;&] -[s7; -|num.Create().n `= 3;&] -[s17; num `= `{ 15.5, 3 `}&] -[s5; Thanks to well defined algorithm requirements, you can e.g. -directly apply Sort on such Array&] -[s7; -|bool operator<(const Number`& a, const Number`& b)&] -[s7; -|`{&] -[s7; -|-|return a.Get() < b.Get();&] -[s7; -|`}&] +[s7; struct Text : Val `{&] +[s7; -|String s;&] +[s7; -|virtual String ToString() const `{ return s; `}&] +[s7; `};&] [s7; &] -[s7; .......&] +[s7; BiArray num;&] +[s7; num.CreateHead().n `= 3;&] +[s7; num.CreateTail().s `= `"Hello`";&] +[s7; num.CreateHead().s `= `"World`";&] +[s7; num.CreateTail().n `= 2;&] [s7; &] -[s7; -|Sort(num);&] -[s17; num `= `{ 3, 15.5 `}&] -[s5; &] -[s3; 7. Bidirectional containers&] -[s5; Vector and 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 like operations). In such -case, BiVector and BiArray should be used&] -[s7; -|BiVector n;&] -[s7; -|n.[* AddHead](1);&] -[s7; -|n.[* AddTail](2);&] -[s7; -|n.AddHead(3);&] -[s7; -|n.AddTail(4);&] -[s17; n `= `{ 3, 1, 2, 4 `}&] -[s7; -|n.[* DropHead]();&] -[s17; n `= `{ 1, 2, 4 `}&] -[s7; -|n.[* DropTail]();&] -[s17; n `= `{ 1, 2 `}&] -[s7; -|BiArray num;&] -[s7; -|num.CreateHead().n `= 3;&] -[s7; -|num.CreateTail().n `= 15.5;&] -[s7; -|num.CreateHead().n `= 2.23;&] -[s7; -|num.CreateTail().n `= 2;&] -[s17; num `= `{ 2.23, 3, 15.5, 2 `}&] -[s5; &] -[s3; 8. Index&] -[s5; Index is a container very similar to the plain Vector (it is -random access array of elements with fast addition at the end) -with one unique feature `- it is able to fast retrieve position -of element with required value using [* Find] method&] -[s7; -|[* Index] ndx;&] -[s7; -|ndx.[* Add](`"alfa`");&] -[s7; -|ndx.Add(`"beta`");&] -[s7; -|ndx.Add(`"gamma`");&] -[s7; -|ndx.Add(`"delta`");&] -[s7; -|ndx.Add(`"kappa`");&] -[s17; ndx `= `{ alfa, beta, gamma, delta, kappa `}&] -[s7; -|DUMP(ndx.[* Find](`"beta`"));&] -[s17; ndx.Find(`"beta`") `= 1&] -[s5; If element is not present in Index, Find returns a negative -value&] -[s7; -|DUMP(ndx.Find(`"something`"));&] -[s17; ndx.Find(`"something`") `= `-1&] -[s5; Any element can be replaced using [* Set] method&] -[s7; -|ndx.[* Set](0, `"delta`");&] -[s17; ndx `= `{ delta, beta, gamma, delta, kappa `}&] +[s7; DUMP(num);&] +[s0; &] +[s17; num `= `[World, 3, Hello, 2`]&] +[s0; &] +[s22; 19. [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 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 [* FindNext] method&] -[s7; -|int fi `= ndx.Find(`"delta`");&] -[s7; -|while(fi >`= 0) `{&] -[s7; -|-|DUMP(fi);&] -[s7; -|-|fi `= ndx.[* FindNext](fi);&] -[s7; -|`}&] -[s7; -|cout << `'n`';&] -[s17; 0 3-|&] -[s5; [* FindAdd] method retrieves position of element like Find, but -if element is not present in Index, it is added&] -[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`"));&] +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; &] -[s16; ndx.FindAdd(`"one`") `= 5&] -[s16; ndx.FindAdd(`"two`") `= 6&] -[s16; ndx.FindAdd(`"three`") `= 7&] -[s16; ndx.FindAdd(`"two`") `= 6&] -[s16; ndx.FindAdd(`"three`") `= 7&] -[s16; ndx.FindAdd(`"one`") `= 5&] -[s5; Removing elements from random access sequence is always expensive, -that is why rather than remove, Index supports [* Unlink] and [* UnlinkKey -]operations, which leave element in Index but make it invisible -for Find operation&] -[s7; -|ndx.[* Unlink](2);&] -[s7; -|ndx.[* UnlinkKey](`"kappa`");&] -[s7; &] -[s7; -|DUMP(ndx.Find(ndx`[2`]));&] -[s7; -|DUMP(ndx.Find(`"kappa`"));&] -[s7; &] -[s16; ndx.Find(ndx`[2`]) `= `-1&] -[s16; ndx.Find(`"kappa`") `= `-1&] +[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 -[* IsUnlinked] method&] -[s7; -|DUMP(ndx.[* IsUnlinked](1));&] -[s7; -|DUMP(ndx.IsUnlinked(2));&] +[*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; &] -[s16; ndx.IsUnlinked(1) `= 0&] -[s16; ndx.IsUnlinked(2) `= 1&] -[s5; Unlinked positions can be reused by [* Put] method&] -[s7; -|ndx.[* Put](`"foo`");&] -[s17; ndx `= `{ delta, beta, foo, delta, kappa, one, two, three `}&] -[s7; -|DUMP(ndx.Find(`"foo`"));&] -[s17; ndx.Find(`"foo`") `= 2&] -[s5; You can also remove all unlinked elements from Index using [* Sweep] -method&] -[s7; -|ndx.Sweep();&] -[s17; ndx `= `{ delta, beta, foo, delta, one, two, three `}&] -[s5; As we said, operations directly removing or inserting elements -of Index are very expensive, but sometimes this might not matter, -so they are available too&] -[s7; -|ndx.[* Remove](1);&] -[s17; ndx `= `{ delta, foo, delta, one, two, three `}&] -[s7; -|ndx.[* RemoveKey](`"two`");&] -[s17; ndx `= `{ delta, foo, delta, one, three `}&] -[s7; -|ndx.[* Insert](0, `"insert`");&] -[s17; ndx `= `{ insert, delta, foo, delta, one, three `}&] -[s5; Finally, [* PickKeys] operation allows you to obtain Vector of -elements of Index in low constant time operation (while destroying -source Index)&] -[s7; -|Vector d `= ndx.[* PickKeys]();&] -[s7; -|Sort(d);&] -[s17; d `= `{ delta, delta, foo, insert, one, three `}&] -[s5; &] -[s3; 9. Index and client types&] -[s5; In order to store elements to Index, they must be moveable (you -can use [* ArrayIndex] for types that are not) and they must have -defined the operator`=`= and a function to compute hash value. -Notice usage THE of [* CombineHash] to combine hash values of types -already known to U`+`+ into final result&] +[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 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; &] +[s22; 20. 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 `{&] [s7; -|String name;&] [s7; -|String surname;&] [s7; &] -[s7; -|unsigned [* GetHashValue]() const `{ return [* CombineHash](name, +[s7; -|unsigned GetHashValue() const `{ return CombineHash(name, surname); `}&] -[s7; -|bool [* operator`=`=](const Person`& b) const `{ return name -`=`= b.name `&`& surname `=`= b.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; .......&] +[s7; Index p;&] +[s7; p.Add(Person(`"John`", `"Smith`"));&] +[s7; p.Add(Person(`"Paul`", `"Carpenter`"));&] +[s7; p.Add(Person(`"Carl`", `"Engles`"));&] [s7; &] -[s7; -|Index 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`")));&] -[s17; p.Find(Person(`"Paul`", `"Carpenter`")) `= 1&] -[s5; &] -[s3; 10. VectorMap, ArrayMap&] -[s5; VectorMap is nothing more than a simple composition of Index -and Vector. You can use [* Add] methods to put elements into the -VectorMap&] -[s7; -|[* VectorMap] m;&] +[s7; DUMP(p.Find(Person(`"Paul`", `"Carpenter`")));&] +[s0; &] +[s17; p.Find(Person(`"Paul`", `"Carpenter`")) `= 1&] +[s0; &] +[s22; 21. [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 `{&] +[s7; -|String name;&] +[s7; -|String surname;&] [s7; -|&] -[s7; -|m.[* Add](`"1`", Person(`"John`", `"Smith`"));&] -[s7; -|m.Add(`"2`", Person(`"Carl`", `"Engles`"));&] +[s7; -|String ToString() const `{ return String() << name << `' `' +<< surname; `}&] [s7; &] -[s7; -|Person`& p `= m.[* Add](`"3`");&] -[s7; -|p.name `= `"Paul`";&] -[s7; -|p.surname `= `"Carpenter`";&] -[s5; VectorMap provides constant access to its underlying Index and -Vector&] -[s7; -|DUMP(m.[* GetKeys]());&] -[s7; -|DUMP(m.[* GetValues]());&] +[s7; -|Person(String name, String surname) : name(name), surname(surname) +`{`}&] +[s7; -|Person() `{`}&] +[s7; `};&] [s7; &] -[s16; m.GetKeys() `= `{ 1, 2, 3 `}&] -[s16; m.GetValues() `= `{ John Smith, Carl Engles, Paul Carpenter -`}&] +[s7; VectorMap m;&] [s7; &] -[s5; You can use indices to iterate map contents&] -[s7; -|for(int i `= 0; i < m.GetCount(); i`+`+)&] -[s7; -|-|LOG(m.[* GetKey](i) << `": `" << m[* `[]i[* `]]);&] +[s7; m.Add(`"1`", Person(`"John`", `"Smith`"));&] +[s7; m.Add(`"2`", Person(`"Carl`", `"Engles`"));&] [s7; &] -[s16; 1: John Smith&] -[s16; 2: Carl Engles&] -[s16; 3: Paul Carpenter&] +[s7; Person`& p `= m.Add(`"3`");&] +[s7; p.name `= `"Paul`";&] +[s7; p.surname `= `"Carpenter`";&] [s7; &] -[s5; You can use [* Find] method to retrieve position of element with -required key&] -[s7; -|DUMP(m.[* Find](`"2`"));&] +[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`& k : m.GetKeys())&] +[s7; -|DUMP(p);&] +[s0; &] +[s17; p `= Peter Carpenter&] +[s17; p `= Peter Carpenter&] +[s17; p `= Peter Carpenter&] +[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 `'unliked`' 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; 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&] -[s5; or [* Get] method to retrieve corresponding value&] -[s7; -|DUMP(m.[* Get](`"2`"));&] +[s0; &] +[s5; or Get method to retrieve corresponding value:&] +[s0; &] +[s7; DUMP(m.Get(`"2`"));&] +[s0; &] [s17; m.Get(`"2`") `= Carl Engles&] -[s5; Passing key not present in VectorMap as Get parameter is a logic -error, but there exists two parameter version that returns second -parameter if key is not in VectorMap&] -[s7; -|DUMP(m.Get(`"33`", Person(`"unknown`", `"person`")));&] +[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&] -[s5; As with Index, you can use [* Unlink] to make elements invisible -for Find operations&] -[s7; -|m.Unlink(1);&] -[s7; -|DUMP(m.Find(`"2`"));&] +[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&] -[s5; You can use [* SetKey] method to change the key of the element&] -[s7; -|m.[* SetKey](1, `"33`");&] -[s7; -|DUMP(m.Get(`"33`", Person(`"unknown`", `"person`")));&] +[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 Engles&] -[s5; If there are more elements with the same key in VectorMap, you -can iterate them using [* FindNext] method:&] -[s7; -|m.Add(`"33`", Person(`"Peter`", `"Pan`"));&] +[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; &] -[s16; m.GetKeys() `= `{ 1, 33, 3, 33 `}&] -[s16; m.GetValues() `= `{ John Smith, Carl Engles, Paul Carpenter, -Peter Pan `}&] +[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 Engles&] +[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; -|int q `= m.Find(`"33`");&] -[s7; -|while(q >`= 0) `{&] -[s7; -|-|cout << m`[q`] << `'n`';&] -[s7; -|-|q `= m.[* FindNext](q);&] -[s7; -|`}&] -[s7; -|&] -[s16; Carl Engles&] -[s16; Peter Pan&] +[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 ps `= m.PickValues();&] +[s7; Vector ks `= m.PickKeys();&] [s7; &] -[s5; You can reuse unlinked positions using [* Put] method:&] -[s7; -|m.[* UnlinkKey](`"33`");&] -[s7; -|m.[* Put](`"22`", Person(`"Ali`", `"Baba`"));&] -[s7; -|m.Put(`"44`", Person(`"Ivan`", `"Wilks`"));&] +[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; &] -[s16; m.GetKeys() `= `{ 1, 22, 3, 44 `}&] -[s16; m.GetValues() `= `{ John Smith, Ali Baba, Paul Carpenter, Ivan -Wilks `}&] +[s7; m `= VectorMap(pick(ks), pick(ps));&] [s7; &] -[s5; [* GetSortOrder] algorithm returns order of elements as Vector -container. You can use it to order content of VectorMap without -actually moving its elements&] -[s7; -|bool operator<(const Person`& a, const Person`& b)&] -[s7; -|`{&] -[s7; -|-|return a.surname `=`= b.surname ? a.name < b.name&] -[s7; -| : a.surname < b.surname;&] -[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 am;&] +[s7; am.Create(`"key`", `"new`", `"person`");&] [s7; &] -[s7; .......&] -[s7; &] -[s7; -|Vector order `= [* GetSortOrder](m.GetValues());&] -[s17; order `= `{ 1, 2, 0, 3 `}&] -[s7; -|for(int i `= 0; i < order.GetCount(); i`+`+)&] -[s7; -|-|cout << m.GetKey(order`[i`]) << `": `" << m`[order`[i`]`] << -`'n`';&] -[s7; &] -[s16; 22: Ali Baba&] -[s16; 3: Paul Carpenter&] -[s16; 1: John Smith&] -[s16; 44: Ivan Wilks&] -[s7; &] -[s5; You can get Vector of values or keys using [* PickValues] resp. -[* PickKeys] methods in low constant time, while destroying content -of source VectorMap&] -[s7; -|Vector ps `= m.[* PickValues]();&] -[s17; ps `= `{ John Smith, Ali Baba, Paul Carpenter, Ivan Wilks `}&] -[s5; If type of values does not satisfy requirements for Vector elements -or if references to elements are needed, you can use [* ArrayMap] -instead&] -[s7; -|[* ArrayMap] am;&] -[s7; -|am.Create(`"A`").n `= 11;&] -[s7; -|am.Create(`"B`").n `= 2.1;&] -[s7; &] -[s16; am.GetKeys() `= `{ A, B `}&] -[s16; am.GetValues() `= `{ 11, 2.1 `}&] -[s7; &] -[s7; -|DUMP(am.Get(`"A`"));&] -[s7; -|DUMP(am.Find(`"B`"));&] -[s7; &] -[s16; am.Get(`"A`") `= 11&] -[s16; am.Find(`"B`") `= 1&] -[s5; &] -[s3; 11. One&] -[s5; One is a container that can store none or one element of T or -derived from T. It functionally quite similiar to std`::unique`_ptr, -but has some more convenient features (like Create method).&] +[s7; DUMP(am);&] +[s0; &] +[s17; am `= `{key: new person`}&] +[s0; &] +[s22; 22. [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() `{`}&] @@ -491,272 +1891,576 @@ but has some more convenient features (like Create method).&] [s7; -|virtual String Get() `{ return `"Derived2`"; `}&] [s7; `};&] [s7; &] -[s7; void MakeDerived1(One`& t)&] -[s7; `{&] -[s7; -|t.Create();&] -[s7; `}&] -[s7; &] -[s7; void MakeDerived2(One`& t)&] -[s7; `{&] -[s7; -|t.Create();&] -[s7; `}&] -[s7; &] -[s7; .......&] -[s7; -|[* One] s;&] -[s5; Operator bool of one returns true if it contains an element:&] -[s7; -|DUMP([* (bool)]s);&] -[s7; &] -[s16; (bool)s `= false&] -[s7; &] -[s7; -|&] -[s7; -|s.[* Create]();&] -[s7; -|DUMP((bool)s);&] -[s7; -|DUMP(s`->Get());&] -[s7; &] -[s16; (bool)s `= true&] -[s16; s`->Get() `= Derived1&] -[s7; &] +[s7; One 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();&] +[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());&] +[s7; DUMP(s.Is());&] +[s7; DUMP(s.Is());&] +[s0; &] +[s17; s.Is() `= true&] +[s17; s.Is() `= true&] +[s17; s.Is() `= 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:&] -[s7; -|s.[* Clear]();&] -[s7; -|DUMP((bool)s);&] -[s7; &] -[s16; (bool)s `= false&] -[s7; &] -[s7; &] -[s5; One represents a convenient and recommended method how to deal -with class factories in U`+`+: Define them as a function (or method) -with reference to One parameter, e.g.:&] -[s7; void MakeDerived1(One`& t)&] -[s7; `{&] -[s7; -|t.Create();&] -[s7; `}&] -[s7; &] -[s7; void MakeDerived2(One`& t)&] -[s7; `{&] -[s7; -|t.Create();&] -[s7; `}&] -[s7; &] -[s7; VectorMap`&)> factories;&] -[s7; &] -[s7; INITBLOCK `{&] -[s7; -|factories.Add(0, MakeDerived1);&] -[s7; -|factories.Add(1, MakeDerived2);&] -[s7; `};&] -[s7; &] -[s7; void Create(One`& t, int what)&] -[s7; `{&] -[s7; -|(`*factories.Get(what))(t);&] -[s7; `}&] -[s5; &] -[s3; 12. Any&] -[s5; Any is a container that can contain none or one element of [*/ any] -type, the only requirement is that the type has default constructor. -Important thing to remember is that [* Is] method matches [/ exact] -type ignoring class hierarchies (FileIn is derived from Stream, -but if Any contains FileIn, Is() returns false).&] -[s7; void Do([* Any]`& x)&] -[s7; `{&] -[s7; -|if(x.[* Is]())&] -[s7; -|-|LOG(`"String: `" << x.[* Get]());&] -[s7; -|if(x.[* Is]()) `{&] -[s7; -|-|LOG(`"`-`-`- File: `");&] -[s7; -|-|LOG(LoadStream(x.[* Get]()));&] -[s7; -|-|LOG(`"`-`-`-`-`-`-`-`-`-`-`");&] -[s7; -|`}&] -[s7; -|if(x.[* IsEmpty]())&] -[s7; -|-|LOG(`"empty`");&] -[s7; `}&] -[s7; &] -[s7; .....&] -[s7; &] +[s0; &] +[s7; s.Clear();&] +[s7; DUMP((bool)s);&] +[s0; &] +[s17; (bool)s `= false&] +[s0; &] +[s22; 23. [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; -|x.[* Create]() `= `"Hello!`";&] -[s7; -|Do(x);&] -[s7; -|x.[* Create]().Open(GetDataFile(`"Ntl12.cpp`"));&] -[s7; -|Do(x);&] -[s7; -|x.[* Clear]();&] -[s7; -|Do(x);&] -[s5; &] -[s3; 13. InVector, InArray&] -[s5; InVector and InArray are vector types quite similar to Vector/Array, -but they trade the speed of operator`[`] with the ability to -insert or remove elements at any position quickly. You can expect -operator`[`] to be about 10 times slower than in Vector (but -that is still very fast), while Insert at any position scales -well up to hundreds of megabytes of data (e.g. InVector containing -100M of String elements is handled without problems).&] -[s7; -|[* InVector] v;&] -[s7; -|for(int i `= 0; i < 1000000; i`+`+)&] -[s7; -|-|v.Add(i);&] -[s7; -|v.[* Insert](0, `-1); // This is fast&] -[s7; &] -[s5; While the interface of InVector/InArray is almost identical -to Vector/Array, InVector/InArray in addition implements FindLowerBound/FindUpper -Bound functions `- while normal random access algorithms work, -it is possible to provide InVector specific optimization that -basically matches the performace of Find`*Bound on sample Vector.&] -[s7; &] -[s7; -|DUMP(v.[* FindLowerBound](55));&] -[s5; &] -[s3; 14. SortedIndex, SortedVectorMap, SortedArrayMap&] -[s5; SortedIndex is similar to regular Index, but keeps its elements -in sorted order (sorting predicate is a template parameter, defaults -to StdLess). Implementation is using InVector, so it works fine -even with very large number of elements (performance is similar -to tree based std`::set). Unlike Index, SortedIndex provides lower/upper -bounds searches, so it allow range search.&] -[s7; -|[* SortedIndex] x;&] -[s7; -|x.Add(5);&] -[s7; -|x.Add(3);&] -[s7; -|x.Add(7);&] -[s7; -|x.Add(1);&] +[s7; -|if(pass)&] +[s7; -|-|x.Create() `= `"Hello!`";&] +[s7; -|else&] +[s7; -|-|x.Create() `= Blue();&] [s7; -|&] -[s7; -|DUMPC(x);&] -[s7; -|DUMP(x.[* Find](3));&] -[s7; -|DUMP(x.[* Find](3));&] -[s7; -|DUMP(x.[* FindLowerBound](3));&] -[s7; -|DUMP(x.[* FindUpperBound](6));&] -[s7; &] -[s5; SortedVectorMap and SortedArrayMap are then SortedIndex based -equivalents to VectorMap/ArrayMap `- maps that keep keys sorted:&] -[s7; -|[* SortedVectorMap] m;&] -[s7; -|m.Add(`"zulu`", 11);&] -[s7; -|m.Add(`"frank`", 12);&] -[s7; -|m.Add(`"alfa`", 13);&] +[s7; -|if(x.Is())&] +[s7; -|-|LOG(`"Any is now String: `" << x.Get());&] [s7; -|&] -[s7; -|DUMPM(m);&] -[s7; -|DUMP(m.[* Get](`"zulu`"));&] -[s5; &] -[s3; 15. Tuples&] -[s5; U`+`+ has template classes Tuple2, Tuple3 and Tuple4 for combining -2`-4 values with different types. These are quite similiar to -std`::tuple class, with some advantages.&] -[s5; To create a Tuple value, you can use the Tuple function. If -correct types canot be deduced from parameters, you can specify -them explicitly:&] -[s7; -|Tuple3 x `= [* Tuple](12, -`"hello`", `"world`");&] +[s7; -|if(x.Is())&] +[s7; -|-|LOG(`"Any is now Color: `" << x.Get());&] +[s7; `}&] +[s0; &] +[s17; Any is now Color: Color(0, 0, 128)&] +[s17; Any is now String: Hello!&] +[s0; &] +[s22; 24. [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 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; &] +[s22; 25. [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 x;&] +[s7; x.Add(5);&] +[s7; x.Add(3);&] +[s7; x.Add(7);&] +[s7; x.Add(1);&] [s7; &] -[s5; Individual values are accessible as members a .. d:&] +[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 m;&] +[s7; m.Add(`"zulu`", 11);&] +[s7; m.Add(`"frank`", 12);&] +[s7; m.Add(`"alfa`", 13);&] [s7; &] -[s7; -|DUMP(x.a);&] -[s7; -|DUMP(x.b);&] -[s7; -|DUMP(x.c);&] -[s7; -|&] -[s5; As long as all individual types have conversion to String (AsString), -the tuple also has such conversion and thus can e.g. be easily -logged:&] +[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; &] +[s22; 26. 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 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());&] +[s0; &] +[s17; x.Get<1>() `= hello&] +[s17; x.Get() `= 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 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; -|DUMP(x);&] +[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 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; &] -[s5; Also, as long as individual types have defined GetHashValue, -so does Tuple:&] +[s7; Tie(i, s1, s2) `= x;&] [s7; &] -[s7; -|DUMP(GetHashValue(x));&] -[s7; &] -[s5; As long as individual types have defined operator`=`=, Tuple -has defined operator`=`= and operator!`=&] -[s7; &] -[s7; -|Tuple3 y `= x;&] -[s7; -|DUMP(x !`= y);&] -[s7; &] -[s5; Finally, as long as all individual types have defined SgnCompare -(most U`+`+ types have), Tuple has SgnCompare, Compare method -and operators <, <`=, >, >`=:&] -[s7; &] -[s7; -|DUMP(x.Compare(y));&] -[s7; -|&] -[s7; -|y.b `= `"a`";&] -[s7; -|&] -[s7; -|DUMP(SgnCompare(x, y));&] -[s7; -|DUMP(x < y);&] -[s7; -|&] -[s7; &] -[s5; U`+`+ Tuples are strictly designed as POD type, which allows +[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:&] -[s7; -|static Tuple2 map`[`] `= `{&] -[s7; -|-|`{ 1, `"one`" `},&] -[s7; -|-|`{ 2, `"one`" `},&] -[s7; -|-|`{ 3, `"one`" `},&] -[s7; -|`};&] -[s7; -|&] -[s5; &] -[s5; Simple FindTuple template function is provided to search for -tuple based on the first value:&] -[s5; &] -[s3; 15. Sorting&] -[s5; IndexSort is sort variant that is able to sort two random access -container (like Vector or Array) of the same size, based on values -in on of containers:&] -[s7; -|Vector a;&] -[s7; -|Vector b;-|&] -[s7; -|&] -[s7; -|a << 5 << 10 << 2 << 9 << 7 << 3;&] -[s7; -|b << `"five`" << `"ten`" << `"two`" << `"nine`" << `"seven`" -<< `"three`";&] -[s7; -|&] -[s7; -|[* IndexSort](a, b);&] -[s7; &] -[s16; a `= `[2, 3, 5, 7, 9, 10`]&] -[s16; b `= `[two, three, five, seven, nine, ten`]&] -[s7; &] -[s7; -|[* IndexSort](b, a);&] -[s7; &] -[s16; a `= `[5, 9, 7, 10, 3, 2`]&] -[s16; b `= `[five, nine, seven, ten, three, two`]&] -[s5; Order of sorted items is defined by sorting predicate. By default, -operator< comparing items of container is used (this predicate -can be provided by StdLess template), but it is possible to specify -different sorting order, e.g. by using predefined StdGreater -predicate:&] -[s7; -|Sort(a, [* StdGreater]());&] -[s7; &] -[s16; a `= `[10, 9, 7, 5, 3, 2`]&] -[s5; Sometimes, instead of sorting items in the container, it is -useful to know the order of items as sorted, using GetSortOrder:&] -[s7; -|Vector o `= [* GetSortOrder](a);&] -[s7; &] -[s16; o `= `[5, 4, 3, 2, 1, 0`]&] -[s5; FieldRelation predefined predicate can be used to sort container -of structures by specific field:&] -[s7; -|Vector p;&] -[s7; -|p << Point(5, 10) << Point(7, 2) << Point(4, 8) << Point(1, -0);&] -[s7; -|&] -[s7; -|Sort(p, [* FieldRelation](`&Point`::x, StdLess()));&] -[s7; &] -[s16; p `= `[`[1, 0`], `[4, 8`], `[5, 10`], `[7, 2`]`]&] -[s5; MethodRelation is good for sorting of structures based on constant -method of structure:-|&] -[s7; struct Foo `{&] -[s7; -|String a;&] -[s7; -|&] -[s7; -|int [* Get]() const `{ return atoi(a); `}&] -[s7; -|....&] +[s0; &] +[s7; static Tuple2 map`[`] `= `{&] +[s7; -|`{ 1, `"one`" `},&] +[s7; -|`{ 2, `"one`" `},&] +[s7; -|`{ 3, `"one`" `},&] [s7; `};&] -[s7; ....&] -[s7; -|Array f;&] -[s7; -|f << `"12`" << `"1`" << `"10`" << `"7`" << `"5`";&] -[s7; -|&] -[s7; -|Sort(f, [* MethodRelation](`&Foo`::[* Get], StdLess()));&] +[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; 27. 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 x `= `{ 1, 2, 3, 4, 5, 1, 2, 3, 4 `};&] [s7; &] -[s16; f `= `[1, 5, 7, 10, 12`]&] -[s5; Normal Sort is not stable `- equal items can appear in sorted -sequence in random order. If maintaining original order of equal -items is important, use StableSort variant (with slight performance +[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 base +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).name());&] +[s7; DUMP(typeid(ValueTypeOf).name());&] +[s7; DUMP(typeid(IteratorOf).name());&] +[s7; DUMP(typeid(ConstIteratorOf).name());&] +[s7; DUMP(typeid(SubRangeOf>).name());&] +[s0; &] +[s17; typeid(ValueTypeOf).name() `= int&] +[s17; typeid(ValueTypeOf).name() `= int&] +[s17; typeid(IteratorOf).name() `= int `*&] +[s17; typeid(ConstIteratorOf).name() +`= int `*&] +[s17; typeid(SubRangeOf>).name() `= class Upp`::SubRangeClass&] +[s0; &] +[s5; While containers themselves and SubRange are the two most common +range types, U`+`+ has two special ranges. [*C@5 ConstRange] simply +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 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 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; 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; &] +[s22; 28. Sorting&] +[s5; Unsurprisingly, [*C@5 Sort] function sorts a range. You can specify +sorting predicate, default is [*C@5 operator<]:&] +[s0; &] +[s7; Vector 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 a `{ 5, 10, 2, 9, 7, 3 `};&] +[s7; Vector 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 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):&] -[s7; -|Vector t;&] -[s7; -|t << Point(10, 10) << Point(7, 1) << Point(7, 2) << Point(7, -3) << Point(1, 0);&] -[s7; -|[* StableSort](t, FieldRelation(`&Point`::x, StdLess()));&] +[s0; &] +[s7; Vector 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; &] -[s16; t `= `[`[1, 0`], `[7, 1`], `[7, 2`], `[7, 3`], `[10, 10`]`]&] -[s5; &] -[s3; Recommended tutorials:&] -[s5; If you want to learn more, we have several tutorials that you -can find useful:&] -[s5;l160;i150;O0; [^topic`:`/`/Core`/srcdoc`/CoreTutorial`$en`-us^ U`+`+ +[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; 29. 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 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 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 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;%% Recommended tutorials:&] +[s5;%% If you want to learn more, we have several tutorials that +you can find useful:&] +[s5;l160;i150;O0;%% [^topic`:`/`/Core`/srcdoc`/CoreTutorial`$en`-us^ U`+`+ Core value types] `- if you miss this one`- it it a good occasion to catch up. Here you will learn about Core basics.&] -[s5;l160;i150;O0; [^topic`:`/`/Sql`/srcdoc`/tutorial`$en`-us^ SQL] +[s5;l160;i150;O0;%% [^topic`:`/`/Sql`/srcdoc`/tutorial`$en`-us^ SQL] `- learn how to use databases with U`+`+ framework.]] \ No newline at end of file