Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site













SourceForge.net Logo

U++ Core value types tutorial


1. String

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.

You can assign a C string literal to String

 

String a;

a = "Hello";

 

a = Hello

 

 

concatenate with another String or literal

 

a = a + " world";

 

a = Hello world

 

 

or single character or specified number of characters from another String or literal

 

a.Cat('!');

 

a = Hello world!

 

a.Cat("ABCDEFGHIJKLM", 3);

 

a = Hello world!ABC

 

 

Clear method empties the String

 

a.Clear();

 

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

 

for(int i = 0; i < 10; i++)

    a << i << ", ";

 

a = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

 

Sometimes is is useful to use operator<< to produce a temporary String value (e.g. as real argument to function call).

 

String().Cat() << "Number is " << 123 << "."

 

Note: This strange special Cat method is needed because C++ does not allow non-cont references to temporary objects.

 

Number is 123.

 

String provides methods for obtaining character count, inserting characters into String or removing them

 

a = "0123456789";

 

a.GetLength() = 10

 

a.Insert(6, "<inserted>");

 

a = 012345<inserted>6789

 

a.Remove(2, 2);

 

a = 0145<inserted>6789

 

 

as well as a couple of searching and comparing methods

 

a.Find('e')

 

a.Find('e') = 8

 

a.ReverseFind('e')

 

a.ReverseFind('e') = 11

 

a.StartsWith("ABC")

 

a.StartsWith("ABC") = false

 

a.EndsWith("KLM")

 

a.EndsWith("KLM") = false

 

a.Find("ted")

 

a.Find("ted") = 10

 

You can get slice of String using Mid method; with single parameter it provides slice to the end of String

 

a.Mid(3, 3)

 

a.Mid(3, 3) = 5<i

 

a.Mid(3)

 

a.Mid(3) = 5<inserted>6789

 

You can also decrease the length of String using Trim

 

a.Trim(4);

 

a = 0145

 

You can obtain int values of individual characters using operator[]

 

a[0]

 

a[0] = 48

 

or the value of first character using operator* (note that if GetLengt() == 0, this will return zero terminator)

 

    DUMP(*a);

 

*a = 48

 


2. StringBuffer

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

 

void CApiFunction(char *c)

{

    strcpy(c, "Hello");

}

 

..........

 

    StringBuffer b;

    b.SetLength(200);

    CApiFunction(b);

    b.Strlen();

    String x = b;

 

x = Hello

 

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).

Another usage scenario of StringBuffer is about altering existing String

 

    b = x;

    b[1] = 'a';

    x = b;

 

x = Hallo

 

Similar to assigning StringBuffer to String, assigning String to StringBuffer clears the source String.

StringBuffer also provides appending operations:

    b = x;

    b.Cat('!');

    x = b;

 

x = Hallo!

 

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.

 


3. WString

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.

 

    WString x = "characters 280-300: ";

    for(int i = 280; i < 300; i++)

        x.Cat(i);

 

x = characters 280-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī

 

    String y = x.ToString();

    DUMP(y);

 

y = characters 280-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī

 

    y.Cat(" (appended)");

    x = y.ToWString();

 

x = characters 280-300: ĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪī (appended)    

 

(Note: y content is displayed as result of conversion from utf-8 encoded data)

 


4. Date and Time

To represent date and time, U++ provides Date and Time concrete types.

 

    Date date = GetSysDate();

 

date = 01/21/2007

 

All data members of Date structure are public:

 

date.year = 2007

date.month = 1

date.day = 21

 

Dates can be compared:

 

date > Date(1970, 1, 1) = true

 

Adding a number to Date adds a number of days to it:

 

date + 1 = 01/22/2007

 

Subtraction of dates yields a number of days between them:

 

date - Date(1970, 1, 1) = 13534

 

U++ defines the beginning and the end of era, most algorithms can safely assume that as minimal and maximal values Date can represent:

 

Date::Low() = 01/01/-4000

Date::High() = 01/01/4000

    

 

Time is derived from Date, adding members to represent time:

 

    Time time = GetSysTime();

 

time = 01/21/2007 23:28:59

(Date&)time = 01/21/2007

(int)time.hour = 23

(int)time.minute = 28

(int)time.second = 59

 

Times can be compared:

 

time > Time(1970, 0, 0) = true

 

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:

 

time > date = false

time > ToTime(date) = true

 

 

Like Date, Time supports add and subtract operations, but numbers represent seconds (using int64 datatype):

 

time + 1 = 01/21/2007 23:29:00

time + 24 * 3600 = 01/22/2007 23:28:59

time - date = 0

time - ToTime(date) = 84539

 

Time also defines era limits:

 

Time::Low() = 01/01/-4000 00:00:00

Time::High() = 01/01/4000 00:00:00

 


5. AsString, ToString and operator<<

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++ headers):

 

template <class T>

inline String AsString(const T& x)

{

    return x.ToString();

}

 

template <class T>

inline Stream& operator<<(Stream& s, const T& x)

{

    s << AsString(x);

    return s;

}

 

template <class T>

inline String& operator<<(String& s, const T& x)

{

    s.Cat(AsString(x));

    return s;

}

 

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++:

 

    FileOut fout(ConfigFile("test.txt"));

    String  sout;

    

    fout << 1.23 << ' ' << GetSysDate() << ' ' << GetSysTime();

    sout << 1.23 << ' ' << GetSysDate() << ' ' << GetSysTime();

 

sout = 1.23 01/22/2007 01/22/2007 17:58:58

 

Getting client types involved into this schema is not too difficult (example shows both methods):

struct BinFoo {

    int x;

    

    String ToString() const   { return FormatIntBase(x, 2); }

    

    BinFoo(int x) : x(x) {}

};

 

struct RomanFoo {

    int x;

    

    RomanFoo(int x) : x(x) {}

};

 

namespace Upp {

template <>

String Upp::AsString(const RomanFoo& a) { return FormatIntRoman(a.x); }

};

.......

 

    sout << BinFoo(30) << ' ' << RomanFoo(30);

 

 

sout = 11110 xxx

 


6. Value

U++ provides one special value type, Value, that can be used to store and retrieve other values.

 

    Value a = 1;

    Value b = 2.34;

    Value c = GetSysDate();

    Value d = "hello";

 

Usually, value types define typecast operator to Value and constructor from Value, so that interaction is for the most part seamless:

 

    int x = a;

    double y = b;

    Date z = c;

    String s = d;

 

x = 1

y = 2.34

z = 01/24/2007

s = hello

 

As for primitive types, Value seamlessly works with int, int64, bool and double.

Casting Value to a type that it does not contain causes runtime error. On the other hand, conversion between related types is possible:

 

 

    double i = a;

    int j = b;

    Time k = c;

    WString t = d;

 

i = 1

j = 2

k = 01/24/2007

t = hello

 

To determine type of value stored in Value, you can use Is method:

 

a.Is<int>() = true

a.Is<double>() = false

b.Is<double>() = true

c.Is<int>() = false

c.Is<Date>() = true

d.Is<String>() = true

 

Note that Is tests for absolute type match, not for compatible types. For that reason, for widely used compatible types utility functions are defined:

 

 

IsNumber(a) = true

IsNumber(b) = true

IsDateTime(c) = true

IsString(d) = true

 


7. Null

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.

 

    int x = Null;

    int y = 120;

    Date d = Null;

    Date e = GetSysDate();

 

IsNull(x) = true

IsNull(y) = false

IsNull(d) = true

e > d = true

 

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.

As a special case, if Value contains Null, it is convertible to any value type that can contain Null:

 

 

    Value v = x;

    e = v;

 

IsNull(e) = true

 

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).

 

    int a = Null;

    int b = 123;

    int c = 1;

 

Nvl(a, b, c) = 123

 


8. Client types and Value, RawValue, RichValue

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:

 

struct RawFoo {

    String x;

};

 

 

(int this case, default copy constructor and assignment operator are provided by compiler).

 

 

    RawFoo h;

    h.x = "hello";

    Value q = RawToValue(h);

 

q.Is<Foo>() = true

q.To<Foo>().x = "hello"

 

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:

 

struct Foo : ValueType<Foo, 10010> {

    int x;

    

    Foo(const Nuller&)                  { x = Null; }

    Foo(int x) : x(x) {}

    Foo() {}

 

    String ToString() const             { return AsString(x); }

    unsigned GetHashValue() const       { return x; }

    void Serialize(Stream& s)           { s % x; }

    bool operator==(const Foo& b) const { return x == b.x; }

    bool IsNullInstance() const         { return IsNull(x); }    

};

 

INITBLOCK {

    Value::Register<Foo>();

}

.......

 

    Value a = RichToValue(Foo(54321));

    Value b = RichToValue(Foo(54321));

 

(a == b) = true

IsNull(a) = false

v.Get<Foo>() = 54321

 

    String s = StoreAsString(a);

    LoadFromString(v, s);

 

v.Get<Foo>() = 54321

 

To avoid RichToValue and ValueTo calls, the client type can also provide constructor from Value and cast operator to Value:

 

struct Foo : ValueType<Foo, 10010> {

    int x;

    

......

    

    operator Value()    { return RichToValue(*this); }

    Foo(const Value& v) { *this = v.Get<Foo>(); }

};

 

......

 

    Value c = Foo(321);

    Foo x = c;

 

x = 123

 


9. CombineHash

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:

 

struct Foo {

    String a;

    int    b;

    

    unsigned GetHashValue() const { return CombineHash(a, b); }

};

 

Note that GetHashValue is defined as function template that calls GetHashValue method of its argument, therefore defining GetHashValue method defines GetHashValue function too.

 

    Foo x;

    x.a = "world";

    x.b = 22;

 

GetHashValue(x) = 4272824901

 

    x.a << '!';

 

GetHashValue(x) = 3378606405

 


Recommended tutorials:

If you want to learn more, we have several tutorials that you can find useful:

Containers Tutorial - everything you should know about NTL (Not standard Template Library) containers. This is the natural continuation of issues raised in this article.

GUI tutorial - learn about creating GUI application with U++.

Last edit by klugier on 08/27/2016. Do you want to contribute?. T++