New Core Tutorial

git-svn-id: svn://ultimatepp.org/upp/trunk@10537 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2016-12-12 10:06:03 +00:00
parent 802f08375f
commit dd88feaf2e
4 changed files with 2358 additions and 1228 deletions

View file

@ -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; ]
[s0; ]]

View file

@ -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.&]

View file

@ -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, `"<inserted>`");&]
[s7; &]
[s16; a `= 012345<inserted>6789&]
[s7; &]
[s7; a.Remove(2, 2);&]
[s7; &]
[s16; a `= 0145<inserted>6789&]
[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) `= 5<i&]
[s7; &]
[s7; a.Mid(3)&]
[s7; &]
[s16; a.Mid(3) `= 5<inserted>6789&]
[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 <class T>&]
[s7; inline String AsString(const T`& x)&]
[s7; `{&]
[s7; -|return x.ToString();&]
[s7; `}&]
[s7; &]
[s7; template <class T>&]
[s7; inline Stream`& operator<<(Stream`& s, const T`& x)&]
[s7; `{&]
[s7; -|s << AsString(x);&]
[s7; -|return s;&]
[s7; `}&]
[s7; &]
[s7; template <class T>&]
[s7; inline String`& operator<<(String`& s, const T`& x)&]
[s7; `{&]
[s7; -|s.Cat(AsString(x));&]
[s7; -|return s;&]
[s7; `}&]
[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<int>() `= true&]
[s16; a.Is<double>() `= false&]
[s16; b.Is<double>() `= true&]
[s16; c.Is<int>() `= false&]
[s16; c.Is<Date>() `= true&]
[s16; d.Is<String>() `= 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<Foo>() `= true&]
[s16; q.To<Foo>().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<Foo, 10010> `{&]
[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<Foo>();&]
[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<Foo>() `= 54321&]
[s7; &]
[s7; -|String s `= StoreAsString(a);&]
[s7; -|LoadFromString(v, s);&]
[s7; &]
[s16; v.Get<Foo>() `= 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<Foo, 10010> `{&]
[s7; -|int x;&]
[s7; -|&]
[s7; ......&]
[s7; -|&]
[s7; -|operator Value() `{ return RichToValue(`*this); `}&]
[s7; -|Foo(const Value`& v) `{ `*this `= v.Get<Foo>(); `}&]
[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`+`+.]]

File diff suppressed because it is too large Load diff