Tutorial #09: Container & Basics

Preface

The Murl Engine is a cross platform framework. All platform dependent implementations are abstracted to ensure the compatibility across different platforms.

Different platforms use different C++ compiler. These compilers differ slightly especially in the area of standard libraries and the STL (Standard Template Library).

Therefore you should avoid the usage of standard libraries like stdio.h to avoid possible problems when compiling the application with different compilers. The Murl Engine already provides all necessary functions and templates and usually further libraries aren't needed. This means that you should never use standard #includes in your application source code like e.g.:

#include <stdio.h>
#include <vector>

The following sections show how to implement applications with the Murl Engine without using these standard libraries.

The best cross platform compatibility is ensured when only interfaces and functions of the Murl headers are used.

Of course it is also possible to use other headers/libraries, if in a particular case the framework does not provide a proper implementation for a special feature. In such a case feel free to create a new thread in our forums to discuss possible new features or corrections.

This tutorial is huge and contains the following subsections:

NTL Container vs. STL Container

The probably most often used container classes in C++ are the classes of the Standard Template Library (STL).

Unfortunately these container implementations differ between different compilers especially in regard to the execution speed and in rare cases also in the behavior.

Due to this fact the Murl Engine provides adapted NTL container classes - see www.ultimatepp.org.

These container classes are very comfortable when you get used to them and provide a considerably faster and more reliable execution time compared to the STL container classes.

Mapping STL → Murl Container

STL string → String
STL vector → Array
STL (unordered) set/multiset → Index (similar)
STL map/multimap → Map

To improve the performance the memory usage of the NTL is a little bit simpler compared to the STL and does not use the copy constructor for internal memory movements. However, the copy constructor is used when elements are added (e.g. Add(), Insert()).

In general, we distinguish between simple containers and object containers. The following simple containers should only be used with primitive data types (e.g. UInt32, Real, etc.) and simple structs:

Array from murl_array.h (NTL Vector)
Index from murl_index.h (NTL Index)
Map from murl_map.h (NTL VectorMap)

Big structs and objects should always be stored in one of the following object containers:

ObjectArray from murl_object_array.h (NTL Array)
ObjectIndex from murl_object_index.h (NTL ArrayIndex)
ObjectMap from murl_object_map.h (NTL ArrayMap)

Object containers internally use simple containers to store references to the instances of their elements. Therefore it is never necessary to move the instances itself in the memory. Also external references to these instances remain always valid, which is another advantage of object containers. A detailed example about the differences can be found in the subsection Simple Container vs. Object Container.

Of course, the copy constructor is used when elements are copied or added to a container - regardless if a simple container or an object container is used. Only for internal memory movements the usage of copy constructors are avoided.

Efficient container classes are very important to achieve a good App performance. This is even more important for multimedia applications on mobile devices.

tut0109_container.png
Container

Text Output

The following sections cover mainly the usage of container classes. The results are simply shown as text output messages in the console window.

All listed source code fragments for this tutorial can be found in the file container_logic.cpp. The following header is used for the text output:

#include "murl_system_console.h"

The text output examples are implemented in the following method:

void App::ContainerLogic::PrintDemo()

The following subsections show different possibilities to generate text output with the Murl Engine:

Print a Simple Text

System::Console::Print("Hello World!\n");

Depending on the platform the text output can be seen in:

  • iOS/OSX: Xcode debug console
  • Windows: console window of the application
  • Android: LogCat message with the priority ANDROID_LOG_INFO

Debug Messages

The header murl_debug_trace.h contains methods to output debug messages. Debug messages are not printed in release builds.

MURL_TRACE(0, "Debug Hello World");

Prints the following text:

Murl::App::ContainerLogic::PrintDemo(), line 63: Debug Hello World

The first parameter (0) defines the "log level" for this message. Debug messages are suppressed, if the log level is higher than the global log level. If e.g. the global log level has been set to 1 only debug messages with a log level smaller or equal to 1 are printed. The global log level can be set with the method Debug::Logger::SetLogLevel. The method Debug::Logger::GetLogLevel can be used to read the global log level.

It is remarkable that also the method name and the line number are printed in front of the debug message.

The following alternative method can be used, if only the debug message should be printed:

Debug::Trace("Debug Hello World");

Prints the following text:

Debug Hello World

Debug messages on Android are printed as LogCat messages with the priority ANDROID_LOG_DEBUG.

Erro Messages

The header murl_debug_error.h contains methods to output error messages. Error messages are also printed in release builds.

MURL_ERROR("Error Hello World");

Prints the following text:

Murl::App::ContainerLogic::PrintDemo(), line 66: Error Hello World

Again, it is remarkable that also the method name and the line number are printed in front of the error message.

The following alternative method can be used, if only the message should be printed:

Debug::Error("Error Hello World");

Prints the following text:

Error Hello World

Error messages on Android are printed as LogCat messages with the priority ANDROID_LOG_ERROR.

Configuration

The methods IAppConfiguration::SetDebugOutputFunctionVerbosity() and IAppConfiguration::SetDebugTraceLevel() can be used to configure the MURL_TRACE and the MURL_ERROR message output.

Variables

All methods mentioned above (Console/Debug/Error) are of course also supporting the usage of variables like the well-known printf function.

UInt32 what = 42;
System::Console::Print("Hello World %d!\n", what);

Prints the following text:

Hello World 42!

Line Break

Debug and Error methods automatically add a line break to every message while the System::Console::Print method prints exactly the given text without additional line break. The character \n can be used to create a line break manually. Alternatively the PrintEndline() method can be used but be aware that this method does not support the usage of variables.

String Class

The C++ programming language does not provide a proper character string class. To compensate for that the class String declared in the header murl_string.h is provided.

The String examples are implemented in the following method:

void App::ContainerLogic::StringDemo()

String Class

A String object can easily be instantiated wherever needed:

String myText;

Such a instantiated String is initially empty (IsEmpty()):

if (myText.IsEmpty())
{
    System::Console::Print("Text is Empty\n");
}

Contrary to STL Strings, a NTL String always maintains a valid pointer to its character array. The method Begin() can be used to get this pointer (equivalent to c_str() from STL String).

System::Console::Print("myText contains '%s'\n", myText.Begin());

Assign Strings (=):

myText = "Hello";
System::Console::Print("myText contains '%s'\n", myText.Begin());

Append Strings (+=):

myText += " World!";
System::Console::Print("myText contains '%s'\n", myText.Begin());

Clear Strings (Clear()):

myText.Clear();
System::Console::Print("myText contains '%s'\n", myText.Begin());
Note
Caution! The method .Begin() have always to be used when printing the content of a String object with %s. This method returns a pointer to a valid null-terminated character array. Omitting the .Begin() part causes error messages or erroneous output depending on the individual platform. Example:
String valString = "Value = ";
UInt32 val = 42;
Debug::Trace("%s %d", valString, val);
Debug::Trace("%s %d", valString.Begin(), val);
Prints the following text in Visual Studio (Xcode shows an error message):
Value = 8
Value = 42
In the first case the String object is put onto the stack instead of the null-terminated character array but the print method is expecting a standard C string followed by a integer value which is in the end causing the erroneous output. In the second case the output is correct.

String Manipulation

Initialize strings (String(), =):

String murlText("Murl");
String engineText = "Engine";

Concatenate strings (+):

System::Console::Print(murlText + "-" + engineText + "\n");

Create a character sequence (String()):
Note that single quotes need to be used because this method requires a single character.

String text('X', 4); 
// text contains now: "XXXX"
System::Console::Print(text + "\n"); 

Concatenate characters (Cat()):
Note that single quotes need to be used because this method requires a single character.

text.Cat('Y', 5);
// text contains now: "XXXXYYYYY"
System::Console::Print(text + "\n"); 

Set characters (Set()):
Note that single quotes need to be used because this method requires a single character.

text.Set(7, 'Z');
text.Set(8, 'Z');
// text contains now: "XXXXYYYZZ"
System::Console::Print(text + "\n");

Insert characters or strings (Insert()):

text.Insert(0, 'A');
// text contains now: "AXXXXYYYZZ"
System::Console::Print(text + "\n");
text.Insert(2, "BB");
// text contains now: "AXBBXXXYYYZZ"
System::Console::Print(text + "\n");

Remove characters (Remove()):

text.Remove(5, 2);
// text contains now: "AXBBXYYYZZ"
System::Console::Print(text + "\n");

Replace substrings (Replace()):
All substrings matching with "YY" are replaced with the replace string "CCC".

text.Replace("YY", "CCC");
// text contains now: "AXBBXCCCYZZ"
System::Console::Print(text + "\n");

Substring Operations

Remove leading and/or trailing whitespace characters (Trim(), TrimLeft(), TrimRight()):
Note that the method returns a new string (no modification of the source string).

String whiteSpace(" white  ");
System::Console::Print("'" + whiteSpace.Trim() + "'\n");      // prints: 'white'
System::Console::Print("'" + whiteSpace.TrimLeft() + "'\n");  // prints: 'white  '
System::Console::Print("'" + whiteSpace.TrimRight() + "'\n"); // prints: ' white'

Create substrings (Left(), Right(), Mid()):
Note that the method returns a new string (no modification of the source string).

String someText("Spraylight");
System::Console::Print(someText.Left(5) + "\n");   // prints: Spray
System::Console::Print(someText.Right(5) + "\n");  // prints: light
System::Console::Print(someText.Mid(2, 6) + "\n"); // prints: raylig

String length (number of bytes) (GetLength()):
Note that the method (GetLength()) returns the number of bytes. The number of UTF-8 characters may differ and can be obtained with the method (GetLengthUTF8()).

someText += "ray";
System::Console::Print("Text: %s, Length: %d", someText.Begin(), someText.GetLength());
// prints: Text: Spraylightray, Length: 13

Find substrings (Find(), ReverseFind()):
The method returns the index of the substring in the range of 0 .. (GetLength() – 1) or a negative number if the substring does not occur. Optionally the search can be started from a given index in the valid range.

System::Console::Print("Position: %d\n", someText.Find("what"));
System::Console::Print("Position: %d\n", someText.Find("ray", 1));
System::Console::Print("Position: %d\n", someText.Find("ray", 3));
System::Console::Print("Position: %d\n", someText.ReverseFind("ray"));

Find the first occurrence of a character from a pool of characters (FindFirstOf(),FindFirstNotOf()):

System::Console::Print("Position: %d\n", someText.FindFirstOf("igl"));
System::Console::Print("Position: %d\n", someText.FindFirstNotOf("prS"));

Verify the start/end of a string (StartsWith(), EndsWith()):

System::Console::Print("Check: %s\n", someText.StartsWith("who") ? "yes" : "no");
System::Console::Print("Check: %s\n", someText.StartsWith("Spra") ? "yes" : "no");
System::Console::Print("Check: %s\n", someText.EndsWith("tray") ? "yes" : "no");

Compare strings (==, !=, >, <):

System::Console::Print("Check: %s\n", someText == "Spray" ? "yes" : "no");
System::Console::Print("Check: %s\n", someText != "ray" ? "yes" : "no");
System::Console::Print("Check: %s\n", someText == "Spraylightray" ? "yes" : "no");
System::Console::Print("Check: %s\n", someText > "Spray" ? "yes" : "no");
System::Console::Print("Check: %s\n", someText < "light" ? "yes" : "no");

UTF-8 Strings

Basically the whole Murl Engine framework is solely working with UTF-8 strings. The String class provides some special methods to handle UTF-8 strings.

Make sure to set the standard encoding of the editor to UTF-8 in order to be able to enter UTF-8 strings directly in the source code!
Note that unfortunately the output of special UTF-8 characters does not work properly in the Windows console (wrong codepage, font).

String utf8String("Dürüm Döner");
System::Console::PrintEndline(utf8String);
utf8String += "® €2,49 𝄞";
System::Console::PrintEndline(utf8String);

Lowercase and uppercase conversion (ToLowerUTF8(), ToUpperUTF8()):
Note that the method returns a new string (no modification of the source string).

System::Console::PrintEndline(utf8String.ToLowerUTF8());
System::Console::PrintEndline(utf8String.ToUpperUTF8());

UTF-8 length vs. byte length (GetLength(), GetLengthUTF8()):
The number of bytes for some UTF-8 characters is bigger than 1. Therefore the number of bytes in a UTF-8 string is not necessarily the same as the number of UTF-8 characters!

System::Console::Print("Bytes %d != %d Chars", utf8String.GetLength(), utf8String.GetLengthUTF8());

Modifying UTF-8 characters (GetUTF8Chars()):
A String can be converted into a StringArray to allow the manipulation of individual UTF-8 characters. Each UTF-8 character is stored as a separate String element in the Array.

StringArray utf8Chars;
utf8Chars = utf8String.GetUTF8Chars();

The individual characters can explicitly be used as Strings:

for (UInt32 i = 0; i < utf8Chars.GetCount(); i++)
{
   System::Console::Print(utf8Chars[i] + " ");
}
System::Console::Print("\n");

The StringArrays can be used to modify individual characters, e.g. replacing the Euro character with a Dollar character:

for (UInt32 i = 0; i < utf8Chars.GetCount(); i++)
{
    if (utf8Chars[i] == "€")
    {
        utf8Chars[i] = "$";
    }
}

The method Cat() can be used to create a String object from a StringArray:

String newUtf8;
newUtf8.Cat(utf8Chars);
System::Console::PrintEndline(newUtf8);

Further String Methods

Most of the shown String methods also exist with additional parameter sets. A complete list of all methods can be found in the String API reference or directly in the header file murl_string.h.

String Conversion

The header file murl_util_string_conversion.h provides several string conversion functions. A detailed description for all methods can be found in the API reference on the String Conversion Functions page.

Convert strings into numbers:

Util::StringToBool()
Util::StringToUInt64()
Util::StringToSInt64()
Util::StringToUInt32()
Util::StringToSInt32()
Util::StringToDouble()
Util::StringToFloat()
Util::StringToColor()
Util::StringToColorComponent()
Util::HexStringToUInt64()
Util::HexStringToUInt32()
Util::AngleStringToDouble()

Example:

String numString = "12345.678";

Double doubleNumber;
if (Util::StringToDouble(numString, doubleNumber))
{
    System::Console::Print("Double Number %f\n", doubleNumber);
}

SInt32 intNumber;
if (Util::StringToSInt32(numString, intNumber))
{
    System::Console::Print("SInt32 Number %d\n", intNumber);
}

if (!Util::StringToDouble("not a number", doubleNumber))
{
    System::Console::PrintEndline("Not a number!");
}

Array Conversion:

Util::StringToBoolArray()
Util::StringToUInt64Array()
Util::StringToSInt64Array()
Util::StringToUInt32Array()
Util::StringToSInt32Array()
Util::StringToDoubleArray()
Util::StringToFloatArray()

Example:

String someNumbers = "17;08;32;42;15;26";

UInt32Array numberArray;
Util::StringToUInt32Array(someNumbers, ';', numberArray);

for (UInt32 i = 0; i < numberArray.GetCount(); i++)
{
    System::Console::Print("%d ", numberArray[i]);
}
System::Console::Print("\n");

Convert numbers into strings:

Util::UInt64ToString()
Util::SInt64ToString()
Util::UInt32ToString()
Util::SInt32ToString()
Util::DoubleToString()
Util::BoolToString()

Example:

UInt64 uint64Value = 123456789;
String uint64String = Util::UInt64ToString(uint64Value);
System::Console::PrintEndline(uint64String);

System::Console::PrintEndline(Util::DoubleToString(765.432, "%5.2f"));

String Helper Objects and Helper Functions

The header file murl_util_string.h provides several helper functions for string operations. A detailed description for all methods can be found in the API reference on the Murl Util String Functions page.

Static String Objects:

Util::StaticEmptyString()
Util::StaticWhitespaceString()
Util::StaticEmptyStringArray()

Sort StringArray:

Util::SortStringArray()

Split String in a StringArray or in a StringIndex:

Util::SplitString()

Join StringArray elements or StringIndex elements to a String:

Util::JoinStringArray()
Util::JoinStringIndex()

Trim StringArray:

Util::TrimStringArray()

Parse String:

Util::GetLine()
Util::GetWord()

File Path and File Name:

Util::GetFilePath()
Util::GetFileName()
Util::GetFileExtension()
Util::StripExtension()
Util::StripPathAndExtension()
Util::JoinPaths()
Util::GetUnixPath()
Util::GetWindowsPath()
Util::GetNormalizedPath()
Util::GetRelativePath()
Util::GetAbsolutePath()

C++ Name:

Util::HasCppScope()
Util::GetCppScope()
Util::StripCppScope()
Util::HasScope()
Util::GetScope()
Util::StripScope()

Attributes:

Util::StripIndex()
Util::StripCount()
Util::IsIdValid()

Numeric/Alphanumeric:

Util::IsNumeric()
Util::IsAlphaNumeric()

Single Character Verification:

Util::IsDigit()
Util::IsAlpha()
Util::IsAlphaNumeric()
Util::IsPunctuation()
Util::IsSpace()
Util::IsHexDigit()
Util::IsControl()

Array Class

The C++ programming language does not provide a proper container array class. To compensate for that the class Array declared in the header murl_array.h is provided.

The Array examples are implemented in the following method:

void App::ContainerLogic::ArrayDemo()

Array Template Class

The following predefined Array types exist to simplify the creation of Array instances for frequently used data types:

UInt8Array
SInt8Array
UInt16Array
SInt16Array
UInt32Array
SInt32Array
UInt64Array
SInt64Array
BoolArray
StringArray
RealArray
FloatArray
DoubleArray

The predefined Array types spare the usage of the "unhandsome" notation with angle brackets (e.g. Array<UInt32>.

An Array object can easily be instantiated wherever needed:

UInt32Array myArray;

Such an instantiated Array is initially empty (IsEmpty()):

if (myArray.IsEmpty())
{
    System::Console::Print("myArray is Empty\n");
}

Append an element to the end of this Array (Add()):

myArray.Add(17);
myArray.Add(42);
myArray.Add(54);
myArray.Add(33);
myArray.Add(22);
System::Console::Print("myArray has %d elements\n", myArray.GetCount());

Get the element at a specified position in this Array ([]):

Note
Note that the iteration through NTL containers is generally index based.
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %d\n", i, myArray[i]);
}
System::Console::Print("\n");

Overwrite an element at a specific position in this Array (Set()):

myArray.Set(2, 44);
System::Console::Print("myArray has %d elements\n", myArray.GetCount());
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %d\n", i, myArray[i]);
}

Insert an element at a specific position in this Array (Insert()):

myArray.Insert(2, 55);
System::Console::Print("myArray has %d elements\n", myArray.GetCount());
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %d\n", i, myArray[i]);
}

Remove one or more element(s) at a specific position in this Array (Remove()):

myArray.Remove(3, 2);
System::Console::Print("myArray has %d elements\n", myArray.GetCount());
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %d\n", i, myArray[i]);
}

Remove several elements (Remove()):
Note that it is not possible to remove multiple elements within one loop because the iterator value is off after the removal of an element. One solution for this problem is the usage of an additional Array which contains the indices of the elements which should be removed.
Caution! The indices must be sorted in ascending order.
The following example removes all odd numbers:

SInt32Array indicesToRemove;
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    if (myArray[i] & 1)
    {
        indicesToRemove.Add(i);
    }
}
myArray.Remove(indicesToRemove);

System::Console::Print("myArray has %d elements\n", myArray.GetCount());
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %d\n", i, myArray[i]);
}

Find the index of an element in this Array (Find(), FindLast()):
The method returns the index of the first occurrence of the specified element in the range of 0 .. (GetLength() – 1) or a negative number if the element does not occur. Optionally the search can be started from a given index in the valid range.

myArray.Add(44);
myArray.Add(22);
SInt32 findIndex;
findIndex = myArray.Find(17);
System::Console::Print("Find 17 at %d\n", findIndex);
findIndex = myArray.Find(22);
System::Console::Print("Find 22 at %d\n", findIndex);
findIndex = myArray.FindLast(22);
System::Console::Print("FindLast 22 at %d\n", findIndex);

The method IsIndexValid() can be used to check if a given index value is valid.

UInt32 i=0;
System::Console::Print("myArray contains [ ");
while (myArray.IsIndexValid(i))
{
    System::Console::Print("%d ", myArray[i++]);
}
System::Console::Print("]\n");

Duplicate Arrays (Array(), =):

UInt32Array my2ndArray(myArray);

UInt32Array my3rdArray;
my3rdArray = myArray;

Concatenate Arrays (Add()):

myArray.Add(my2ndArray);
System::Console::Print("myArray has %d elements\n", myArray.GetCount());
for (UInt32 i = 0; i < myArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %d\n", i, myArray[i]);
}

Get the first element in this Array (Bottom())::

System::Console::Print("Bottom element is %d\n", myArray.Bottom());

Get the last element in this Array (Top()):

System::Console::Print("Top element is %d\n", myArray.Top());

Remove the last element in this Array(Pop()):

System::Console::Print("Top element was %d\n", myArray.Pop());

Clear Arrays (Clear(), Empty()):
Clear() removes all of the elements from this Array and frees all internal allocated memory. Empty() removes all of the elements from this Array but keeps the internal allocated memory for further usage. Using the method Empty() can be considerably faster, if the same Array object is cleared and refilled with new elements several times.

my2ndArray.Clear();
my3rdArray.Empty();

The number of Array elements can also be set with SetCount(). The default constructor is used for each element.
Caution! No default constructors exist for primitive data types which will cause uninitialized elements (e.g. UInt32Array, FloatArray etc.). The StringArray elements will be initialized with empty Strings by the default constructor provided from the String class.

FloatArray myFloatArray;
myFloatArray.SetCount(10);
for (UInt32 i = 0; i < myFloatArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %f\n", i, myFloatArray[i]);
}

Manually initialize all elements after SetCount():

myFloatArray.SetCount(10);
for (UInt32 i = 0; i < myFloatArray.GetCount(); i++)
{
    myFloatArray[i] = 0;
}
for (UInt32 i = 0; i < myFloatArray.GetCount(); i++)
{
    System::Console::Print("Element %d has value %f\n", i, myFloatArray[i]);
}

Further Array Methods

Most of the shown Array methods also exist with additional parameter sets. A complete list of all methods can be found in the Array API reference in the section Murl Container Classes or directly in the header files murl_array.h and murl_object_array.h.

ObjectArray Template Class

As previously mentioned, NTL containers do not use copy constructors for internal memory movements to improve performance. Object containers like the ObjectArray from murl_object_array.h should be used to store big structs and objects.

class MyObject
{
public:
    // Default Constructor
    MyObject()
    : mNumber(0)
    , mStatus(false)
    {
    }

    String GetDescription() const
    {
        String description = Util::UInt32ToString(mNumber);
        description += " " + Util::BoolToString(mStatus);
        description += " '" + mName + "'";
        return description;
    }

    UInt32 mNumber;
    Bool mStatus;
    String mName;
};

Generally all methods of the Array class are also provided by the ObjectArray class, except for a few.

ObjectArray<MyObject> myObjectArray;
myObjectArray.SetCount(3);
System::Console::Print("myObjectArray has %d elements\n", myObjectArray.GetCount());
myObjectArray[0].mName = "Element 0";
myObjectArray[1].mName = "Element 1";
myObjectArray[2].mName = "Element 2";
for (UInt32 i = 0; i < myObjectArray.GetCount(); i++)
{
    System::Console::Print("Element %d is %s\n", i, myObjectArray[i].GetDescription().Begin());
}

Add an existing object:

MyObject anotherObject;
anotherObject.mNumber = 42;
anotherObject.mStatus = true;
anotherObject.mName = "New One!";
myObjectArray.Add(anotherObject);
System::Console::Print("myObjectArray has %d elements\n", myObjectArray.GetCount());
for (UInt32 i = 0; i < myObjectArray.GetCount(); i++)
{
    System::Console::Print("Element %d is %s\n", i, myObjectArray[i].GetDescription().Begin());
}

SetCount(3) creates three objects and stores them in the ObjectArray. The method Add(anotherObject) creates a copy of the object anotherObject and stores this copy in the ObjectArray.

The method Remove() can be used to remove an object from the ObjectArray and to free the memory of it. The ObjectArray frees the memory for all of its objects automatically when the ObjectArray is destroyed.

Specific ObjectArray Methods

If the copy constructor of an object is private, the object obviously cannot be added with the above shown method Add(anotherObject). Therefore ObjectArrays support the addition of elements which are directly allocated with "new". The ObjectArray takes the owner ship of the object and frees the memory using "delete" in due course.

myObjectArray.Add(new MyObject);
myObjectArray.Set(1, new MyObject);
myObjectArray.Insert(2, new MyObject);

It is also possible to remove an object including owner ship from an ObjectArray. Such an object must then be released explicitly with "delete" (Detach(), PopDetach()):

delete myObjectArray.Detach(1);
delete myObjectArray.PopDetach();

Simple Container vs. Object Container

As already mentioned in subsection NTL Container vs. STL Container, object containers internally store references to the instances of their elements, which therefore do not need to be moved in the memory. The following example should illustrate the differences between object containers and simple containers:

We create a simple Array arrayNamesA and ObjectArray arrayNamesB and fill both with the same data. In addition we create an object reference objRefA / objRefB and an object copy objCopyA / objCopyB from one element.

Array<MyObject> arrayNamesA;
arrayNamesA.SetCount(3);
arrayNamesA[0].mName = "Pac Man A";
arrayNamesA[1].mName = "Super Mario A";
arrayNamesA[2].mName = "Lara Croft A";
MyObject& objRefA = arrayNamesA.Get(1);
MyObject  objCopyA = arrayNamesA.Get(1);

ObjectArray<MyObject> arrayNamesB;
arrayNamesB.SetCount(3);
arrayNamesB[0].mName = "Pac Man B";
arrayNamesB[1].mName = "Super Mario B";
arrayNamesB[2].mName = "Lara Croft B";
MyObject& objRefB = arrayNamesB.Get(1);
MyObject  objCopyB = arrayNamesB.Get(1);

We use Debug::Trace to show the content:

   Debug::Trace("Initial:");
   Debug::Trace(" objRefA: " + objRefA.GetDescription());
   Debug::Trace("objCopyA: " + objCopyA.GetDescription());
   Debug::Trace(" objRefB: " + objRefB.GetDescription());
   Debug::Trace("objCopyB: " + objCopyB.GetDescription());
Initial:
objRefA: 0 false 'Super Mario A'
objCopyA: 0 false 'Super Mario A'
objRefB: 0 false 'Super Mario B'
objCopyB: 0 false 'Super Mario B'

All variables show the same content but point to different memory regions.

tut0109_array_v1.png
Memory regions after initialization.

As next step we change the array elements located at index position 1 and print again the variable content using Debug::Trace.

arrayNamesA[1].mName = "Giana Sisters A";
arrayNamesB[1].mName = "Giana Sisters B";
Changed Array Item 1:
objRefA: 0 false 'Giana Sisters A'
objCopyA: 0 false 'Super Mario A'
objRefB: 0 false 'Giana Sisters B'
objCopyB: 0 false 'Super Mario B'

As expected objRefA and objRefB show the altered values while the variables objCopyA and objCopyB show the "old" values.

tut0109_array_v2.png
Memory regions after element change.

Now we remove the element located at index position 0 from each array.

arrayNamesA.Remove(0);
arrayNamesB.Remove(0);
Removed Array Item 0:
objRefA: 0 false 'Lara Croft A'
objCopyA: 0 false 'Super Mario A'
objRefB: 0 false 'Giana Sisters B'
objCopyB: 0 false 'Super Mario B'

The reference objRefB points to the correct element in the array. The reference objRefA points still to the element with index position 1 although the correct element now has index position 0 due to the delete operation.

tut0109_array_v3.png
Memory regions after element removal.

Another advantage of object containers is the usually better performance of delete and insert operations because lesser memory needs to be moved (only the pointers to the elements needs to be moved and not the whole elements itself).

Index Class

An Index container can be used to store and retrieve elements. The container is using a hash algorithm internally to accelerate the search process. Each element is associated with an index value. The classes Index and ObjectIndex are implemented in the header files murl_index.h and murl_object_index.h and utilize the base class IndexBase from murl_index_base.h.

The Index examples are implemented in the following method:

void App::ContainerLogic::IndexDemo()

Index Template Class

The following predefined Index types exist to simplify the creation of Index instances for frequently used data types:

UInt8Index
SInt8Index
UInt16Index
SInt16Index
UInt32Index
SInt32Index
UInt64Index
SInt64Index
StringIndex
RealIndex
FloatIndex
DoubleIndex

The predefined types spare the usage of the "unhandsome" notation with angle brackets (e.g. Index<String>).

Creation of an Index object:

StringIndex myStringIndex;
myStringIndex.Add("null");
myStringIndex.Add("one");
myStringIndex.Add("two");
myStringIndex.Add("three");
myStringIndex.Add("four");
myStringIndex.Add("five");
// "null"->0, "one"->1, "two"->2, "three"->3, "four"->4, "five"->5
System::Console::Print("myStringIndex one is %d\n", myStringIndex.Find("one"));
System::Console::Print("myStringIndex two is %d\n", myStringIndex.Find("two"));
System::Console::Print("myStringIndex four is %d\n", myStringIndex.Find("four"));

It is possible to add the same element several times:

myStringIndex.Add("two");
// "null"->0, "one"->1, "two"->2, "three"->3, "four"->4, "five"->5, "two"->6
System::Console::Print("myStringIndex has %d elements\n", myStringIndex.GetCount());
for (UInt32 i = 0; i < myStringIndex.GetCount(); i++)
{
    System::Console::Print("Element %s has index %d\n", myStringIndex[i].Begin(), i);
}

The method RemoveKey() removes each appearance of the specified element from the container:

myStringIndex.RemoveKey("two");
// "null"->0, "one"->1, "three"->2, "four"->3, "five"->4
System::Console::Print("myStringIndex has %d elements\n", myStringIndex.GetCount());
for (UInt32 i = 0; i < myStringIndex.GetCount(); i++)
{
    System::Console::Print("Element %s has index %d\n", myStringIndex[i].Begin(), i);
}

The method Remove() removes the element at the specific position:

myStringIndex.Remove(3);
// "null"->0, "one"->1, "three"->2, "five"->3
System::Console::Print("myStringIndex has %d elements\n", myStringIndex.GetCount());
for (UInt32 i = 0; i < myStringIndex.GetCount(); i++)
{
    System::Console::Print("Element %s has index %d\n", myStringIndex[i].Begin(), i);
}

To accomplish an Index with unique elements the method FindAdd() needs to be used. The method will add an element only if such an element doesn't exist in the container already. This avoids multiple occurrences of the same element:

myStringIndex.Pop();
myStringIndex.FindAdd("two");
myStringIndex.FindAdd("three");
// "null"->0, "one"->1, "three"->2, "two"->3
System::Console::Print("myStringIndex has %d elements\n", myStringIndex.GetCount());
for (UInt32 i = 0; i < myStringIndex.GetCount(); i++)
{
    System::Console::Print("Element %s has index %d\n", myStringIndex[i].Begin(), i);
}

Index Hash

Classes need to provide a public GetHashValue() method in order to be able to use the class objects as index elements. The method should calculate and return a proper hash value.

Example: Hash of a Data objects from murl_data.h:

UInt32 GetHashValue() const
{
    return Util::Hash::GetMemoryHashValue(mData, mByteSize);
}

Further Index Methods

The Index methods are very similar to the Array methods e.g. Set(), Insert(), Remove(), etc.

The methods Find(), FindLast(), FindNext() and FindPrev() can be used to find multiple occurrences of elements.

All methods which are containing the term "Put" in their names are only relevant if the method Unlink() is used. The method Unlink() can be used to exclude elements from search operations.

A complete list of all methods can be found in the API reference or directly in the header files murl_index_base.h, murl_index.h and murl_object_index.h.

ObjectIndex Template Class

As previously mentioned, NTL containers do not use copy constructors for internal memory movements to improve performance. Object containers like the ObjectIndex from murl_object_index.h should be used to store big structs and objects.

Generally all methods of the Index class are also provided by the ObjectIndex class, except for a few.

Queue Class

The Queue class can be used to create FIFO containers and allow fast adding and removing of elements at the beginning and at the end of a sequence.

The classes Queue and ObjectQueue are implemented in the header files murl_queue.h and murl_object_queue.h.

The Queue examples are implemented in the following method:

void App::ContainerLogic::QueueDemo()

Queue Template Class

Create a Queue object

Queue<String> myStringQueue;

The following predefined Queue types exist to simplify the creation of Queue instances for frequently used data types:

UInt8Queue
SInt8Queue
UInt16Queue
SInt16Queue
UInt32Queue
SInt32Queue
UInt64Queue
SInt64Queue
BoolQueue
StringQueue
RealQueue
FloatQueue
DoubleQueue

The predefined Queue types spare the usage of the "unhandsome" notation with angle brackets:

StringQueue myStringQueue;

Add some values to the queue (AddHead(), AddTail()):

myStringQueue.AddTail("item 1");
myStringQueue.AddTail("item 2");
myStringQueue.AddTail("item 3");
myStringQueue.AddHead("item 0");

Get the first element by reference (Head(), Tail()):

String& head =  myStringQueue.Head();
System::Console::PrintEndline(head);

Get the element at a specified position ([]):

String& item1 =  myStringQueue[1];
System::Console::PrintEndline(item1);

Delete the first element (DropHead(), DropTail()):

myStringQueue.DropHead();

Get and remove the first element (DropGetHead(), DropGetTail()) until the queue is empty:

while (!myStringQueue.IsEmpty())
{
    head = myStringQueue.DropGetHead();
    System::Console::PrintEndline(head);
}

ObjectQueue Template Class

As previously mentioned, NTL containers do not use copy constructors for internal memory movements to improve performance. Object containers like the ObjectQueue from murl_object_queue.h should be used to store big structs and objects.

Most methods of the Queue class are also provided by the ObjectQueue class.

Map Class

A map object can be used to map a key to one or more elements. The container is using a hash algorithm internally to accelerate the search process. The class Map and ObjectMap are implemented in the header files murl_map.h and murl_object_map.h and utilize the base class MapBase from murl_map_base.h.

The Map examples are implemented in the following method:

void App::ContainerLogic::MapDemo()

Map Template Class

Create a Map object, e.g. association of a String key to a Double value:

Map<String, Double> nameToDouble;
nameToDouble.Add("null", 0.0);
nameToDouble.Add("pi", Math::PI);
nameToDouble.Add("double", 2.0);
nameToDouble.Add("halve", 0.5);

Get an element from a Map (GetKey(), []):

System::Console::Print("nameToDouble has %d elements\n", nameToDouble.GetCount());
for (UInt32 i = 0; i < nameToDouble.GetCount(); i++)
{
    System::Console::Print("Key %s ", nameToDouble.GetKey(i).Begin());
    System::Console::Print("has value %f\n", nameToDouble[i]);
}

Find an element (Find()):
Note that the iteration through NTL containers is generally index based.

SInt32 index = nameToDouble.Find("double");
if (index >= 0)
{
    System::Console::Print("double value %f\n", nameToDouble[index]);
}

index = nameToDouble.Find("something");
if (index < 0)
{
    System::Console::PrintEndline("something not found");
}

The above shown approach has the advantage that you can easily react if a key does not exist in the Map.

A simpler but also more "dangerous" approach is the use of the method Get().
Caution! In this case the application will stop with an ASSERT if the key does not exist!

System::Console::Print("pi is %f", nameToDouble.Get("pi"));
System::Console::Print("halve is %f", nameToDouble.Get("halve"));

The class provides also a "defused" variant of the method Get() which allows to specify a default value if the key does not exist. This default value is then added to the Map:

System::Console::Print("quater is %f\n", nameToDouble.Get("quater", 0.25));
System::Console::Print("nameToDouble has %d elements\n", nameToDouble.GetCount());
for (UInt32 i = 0; i < nameToDouble.GetCount(); i++)
{
    System::Console::Print("Key %s has value %f\n", nameToDouble.GetKey(i).Begin(), nameToDouble[i]);
}

It is possible to add more than one value to the same key:

nameToDouble.Add("odd", 1.0);
nameToDouble.Add("odd", 3.0);
nameToDouble.Add("odd", 7.0);

index = nameToDouble.Find("odd");
while (index >= 0)
{
    System::Console::Print("Found 'odd' with value %f\n", nameToDouble[index]);
    index = nameToDouble.FindNext(index);
}

To avoid multiple values per key the method FindAdd() needs to be used:

nameToDouble.RemoveKey("odd");
nameToDouble.FindAdd("halve", 0.5);
nameToDouble.FindAdd("ten", 10.0);
System::Console::Print("nameToDouble has %d elements\n", nameToDouble.GetCount());
for (UInt32 i = 0; i < nameToDouble.GetCount(); i++)
{
    System::Console::Print("Key %s has value %f\n", nameToDouble.GetKey(i).Begin(), nameToDouble[i]);
}

Further Map Methods

The Map methods are very similar to the Array / Index methods e.g. Insert(), Remove(), etc.

The methods Find(), FindLast(), FindNext() and FindPrev() can be used to find multiple occurrences of elements.

All methods which are containing the term "Put" in their names are only relevant if the method Unlink() is used. The method Unlink() can be used to exclude elements from search operations.

A complete list of all methods can be found in the API reference or directly in the header files murl_map_base.h, murl_map.h and murl_object_map.h.

ObjectMap Template Class

As previously mentioned, NTL containers do not use copy constructors for internal memory movements to improve performance. Object containers like the ObjectMap from murl_object_map.h should be used to store big structs and objects.

Generally all methods of the Map class are also provided by the ObjectMap class, except for a few.

Time and Date

The Murl Engine provides methods related to the system time in the header file murl_system_time.h.

The time examples are implemented in the following method:

void TimeDemo(const Logic::IState* state)

The method GetNow() returns the current time as System::Time object:

System::Time currentTime = System::Time::GetNow();

System::Time objects store the time as the number of seconds that have elapsed since 00:00, 1 January 1970. System::Time objects can easily be converted into readable date objects (System::DateTime):

System::DateTime currentDate(currentTime);
System::Console::Print("Date %4d-%02d-%02d", currentDate.mYear, currentDate.mMonth, currentDate.mDay);
System::Console::Print(" Time %02d:%02d:%02d\n", currentDate.mHour, currentDate.mMinute, currentDate.mSecond);

Get the boot time of the computer system (GetBootTime()):

System::Time bootTime = state->GetEngineConfiguration()->GetBootTime();
System::DateTime bootDate(bootTime);
System::Console::Print("Boot %4d-%02d-%02d", bootDate.mYear, bootDate.mMonth, bootDate.mDay);
System::Console::Print(" Time %02d:%02d:%02d\n", bootDate.mHour, bootDate.mMinute, bootDate.mSecond);

System::Time objects provide also calculation operators:

System::Time upTime = currentTime - bootTime;
System::Console::Print("Uptime %llu seconds", upTime.GetSeconds());
System::Console::Print(" %llu milliseconds\n", upTime.GetMilliSeconds());

Print out the system up time:

System::DateTime upDate(upTime);
System::Console::Print("Uptime %d Months %d Days", upDate.mMonth - 1, upDate.mDay - 1);
System::Console::Print(" %02d:%02d:%02d\n", upDate.mHour, upDate.mMinute, upDate.mSecond);

GetTickCount

The method GetTickCount() can be used to create System::Time objects that are not affected by changes in the system time-of-day clock. Adjustments of the system time have no effect to this time base. If the system is rebootet, this time base typically is also reset. For relative time measurements in the application this method should be preferred.

Further Time Methods

A complete list of all methods can be found in the API reference (System::Time, System::DateTime) or directly in the header murl_system_time.h.

Note that it is also possible to compare and sort System::Time objects; see also next section Sorting.

Time Measurement (Benchmarks)

The file murl_debug_time.h provides convenient methods for time measurements:

The parameter key is used to assign the BEGIN and END methods to each other. Hence also interleaved calls are possible.

MURL_TRACE_TIME_BEGIN(t1);
CalculateDelaunayTriangulation();
MURL_TRACE_TIME_END(t1,0, "CalculateDelaunayTriangulation() ");

MURL_ERROR_TIME_BEGIN(t2);
CalculateConvexHull();
MURL_ERROR_TIME_END(t2, "CalculateConvexHull() ");

The messages are printed in the console window as debug messages or as "error messages" depending on the used methods.

Murl::App::BenchmarkLogic::Bench000(), line 100: CalculateDelaunayTriangulation() time: 7872.019000 ms
Murl::App::BenchmarkLogic::Bench000(), line 104: CalculateConvexHull() time: 5034.857000 ms
Note
Sometimes the logic tick duration (GetCurrentTickDuration()) is used as benchmark value. Please note that the Murl Engine by default is averaging time differences between logic ticks and is using an upper limit of one second for the maximum logic tick duration. The method IAppConfiguration::SetClockAveragingFactor() can be used to change the averaging factor (1 means no averaging). The bounds for the logic tick duration can be adjusted with the method IEngineConfiguration::SetBoundsForLogicTickDuration().

Sorting

The Murl Engine provides methods to sort data in the header file murl_util_sort.h.

Sort Standard Data Types

The best way to sort standard data type elements is the usage of Arrays and the method Util::SortArray:

SInt32Array numbers;
numbers.Add(45);
numbers.Add(23);
numbers.Add(67);
numbers.Add(98);
numbers.Add(12);
Util::SortArray(numbers, true);
for (UInt32 i = 0; i < numbers.GetCount(); i++)
{
    System::Console::Print("Number %d has value %d\n", i, numbers[i]);
}

The method Util::SortArray is available for the following Array types:
UInt64Array, SInt64Array, UInt32Array, SInt32Array, RealArray, DoubleArray and StringArray

StringArray names;
names.Add("home");
names.Add("friends");
names.Add("all");
names.Add("go");
names.Add("now");
Util::SortArray(names, true);
for (UInt32 i = 0; i < names.GetCount(); i++)
{
    System::Console::Print("Name %d has value %s\n", i, names[i].Begin());
}

Sort Custom Objects

To sort other elements it is necessary to implement your own comparison function.

In the following example we will implement a simple class to store receipts with a value and a timestamp.

To be able to sort an Array of such receipt objects, we need to implement a comparison function which can compare two instances (source1 and source2).

We will implement two comparison functions: one to compare the timestamps (CompareTime) and one to compare the values (CompareValue).

The return value of a comparison function needs to be:
Null if source1 is equal to source2.
Negative if source1 is less than source2.
Positive if source1 is greater than source2.

class MyReceipt
{
public:
    // Default Constructor
    MyReceipt()
    : mValue(0)
    {
    }

    MyReceipt(const System::Time& time, Double value)
    : mTime(time)
    , mValue(value)
    {
    }

    static SInt32 CompareTime(const MyReceipt* source1, const MyReceipt* source2)
    {
        if (source1->mTime > source2->mTime)
        {
            return 1;
        }
        if (source1->mTime < source2->mTime)
        {
            return -1;
        }
        return 0;
    }

    static SInt32 CompareValue(const MyReceipt* source1, const MyReceipt* source2)
    {
        if (source1->mValue > source2->mValue)
        {
            return 1;
        }
        if (source1->mValue < source2->mValue)
        {
            return -1;
        }
        return 0;
    }

    String GetDescription() const
    {
        System::DateTime date(mTime);
        String description = Util::DoubleToString(mValue, "% 8.2f");
        description += " " + Util::UInt32ToString(date.mYear, "%4d");
        description += "-" + Util::UInt32ToString(date.mMonth, "%02d");
        description += "-" + Util::UInt32ToString(date.mDay, "%02d");
        description += " " + Util::UInt32ToString(date.mHour, "%02d");;
        description += ":" + Util::UInt32ToString(date.mMinute, "%02d");;
        description += ":" + Util::UInt32ToString(date.mSecond, "%02d");;
        return description;
    }

    System::Time mTime;
    Double mValue;
};

Create receipts:

Array<MyReceipt> myReceipts;
System::Time time = System::Time::GetNow();
myReceipts.Add(MyReceipt(time, 100.5));
myReceipts.Add(MyReceipt(time + System::Time::FromSeconds(5000), 203.19));
myReceipts.Add(MyReceipt(time - System::Time::FromSeconds(4000), 1060.75));
myReceipts.Add(MyReceipt(time + System::Time::FromSeconds(2000), 2007.25));
myReceipts.Add(MyReceipt(time - System::Time::FromSeconds(8000), 412.34));

Sort receipts according to timestamps:

Util::SortArray(myReceipts, MyReceipt::CompareTime);
for (UInt32 i = 0; i < myReceipts.GetCount(); i++)
{
    System::Console::Print("Receipt %d is %s\n", i, myReceipts[i].GetDescription().Begin());
}

Sort receipts according to values:

Util::SortArray(myReceipts, MyReceipt::CompareValue);
for (UInt32 i = 0; i < myReceipts.GetCount(); i++)
{
    System::Console::Print("Receipt %d is %s\n", i, myReceipts[i].GetDescription().Begin());
}
Note
Hint: To sort the array in reverse order simply create additional comparison functions with inverted return values.

Further Sort Methods

It is also possible to sort data arrays without using the Array container class; see Util::QuickSort(), Util::BubbleSort() and Util::BinarySearch().

SInt32 compare (const SInt32 * a, const SInt32 * b)
{
  return (*a - *b );
}

void sort()
{
  SInt32 a[] = {3,4,8,2,1};
  Util::QuickSort(a, 5, compare);
  for (int i=0; i<5; i++)
      Debug::Trace(Util::SInt32ToString(a[i]));
}

A complete list of all methods can be found in the API reference (Sort Functions) or directly in the header file murl_util_sort.h.

Mathematical Functions

The Murl Engine provides mathematical functions and constants in murl_math.h, murl_math_types.h and murl_math_limits.h.

Functions

The header murl_math.h provides the following mathematical functions:

  • trigonometric functions
  • hyperbolic function
  • exponential and logarithmic functions
  • raising to powers functions
  • rounding and other functions

Constants

The header murl_math_types.h provides the following constants:

  • Euler's number and pi and several multiplied/divided pi values.
  • Conversion coefficients to convert between degree and radiant.
  • Conversion coefficients to convert between metric and inch.

Range of Numbers

The header file murl_math_limits.h provides the following limit functions:

Further Mathematical Classes

The Murl Engine provides further classes to work with vectors, matrices, quaternions etc. A complete list of all math classes can be found in the Math API reference.

Data Class

Some functions return or expect Data objects: e.g. if a file is read or if data is received from the network. The Data class simplifies the handling of raw data and is specified in the header file murl_data.h.

Using Data objects avoids in most cases the usage of "unsafe" functions like memcpy().

Example:

Data myData("Hello World!");
System::Console::PrintHex(myData);

myData.ResizeData(30);
System::Console::PrintHex(myData);

Data otherData("Spraylight");
myData += otherData;
System::Console::PrintHex(myData);

Data moreData("Murl");
myData.CopyDataFrom(moreData, 15);
System::Console::PrintHex(myData);

Further Data Classes and Methods

The Data class is derived from MutableData which on the other hand is derived from ConstData. This hierarchy is useful to define who is providing the data and who has the owner ship of the raw data.

The method Util::StaticEmptyData() provides an empty Data object.

A complete list of all Data, MutableData and ConstData methods can be found in the Data API reference or in the header file murl_data.h.

Pointer

Usually it is not necessary to use pointers at all when developing Murl Engine applications.

However, if it is inevitable to allocate objects the following classes may be helpful:

AutoPointer

The class AutoPointer is defined in the header file murl_auto_pointer.h.

To a certain degree an AutoPointer takes the memory management owner ship for an object. If an AutoPointer is destroyed (released), the AutoPointer ensures that also the memory of the object to which it points gets released automatically.

Be careful when using AutoPointer: When a pointer is assigned to another pointer also the owner ship is transferred and the source pointer is suddenly 0 after the assignment!

There is a golden rule when working with AutoPointers: There can be only one! ;-)

SharedPointer / WeakPointer

As previously mentioned, due to compatibility issues the Murl Engine does not use the C++ library Boost.

However, the framework provides the two classes SharedPointer and WeakPointer. The usage is exactly the same as with the Boost library.

The SharedPointer class is defined in the header file murl_shared_pointer.h.

The WeakPointer class is defined in the header file murl_weak_pointer.h.

Like the AutoPointer, a SharedPointer releases automatically the memory of the object to which it points. But - in contrast to the AutoPointer, multiple SharedPointer objects may point to the same object. A reference counter counts the total number of smart pointers for each object. The counter is incremented on pointer assignments and decremented on pointer destructions. When the counter reaches zero the object gets released automatically.

A WeakPointer is a pointer to a SharedPointer object which does not prevent the release of the object. The reference counter is not incremented for WeakPointers. You can request a SharedPointer object from the WeakPointer object when needed. If the object has been released already, the returned SharedPointer will be 0. If the object still exists, the reference counter is increased and the returned SharedPointer object points to the object.

Further documentation and examples can be found in the documentation of the Boost library (www.boost.org/doc).

Utility Functions

The following subsections list some useful utility functions. A complete list can be found in the API reference on the pages Murl::Util Functions, Murl::Math Functions and Murl::System::CLib Functions.

Util & Math Functions

A lot of utility functions are implemented as templates in the header files murl_util.h and murl_math.h; e.g.:

Math::Abs()
Math::Max()
Math::Min()
Math::Clamp()
Math::IsEqual()
Util::RoundToNextPowerOfTwo()
Util::IsPowerOfTwo()
etc.

System::CLib Functions

Forwards to the C library are defined in the header file murl_system_clib.h; e.g.:

System::CLib::PrintToString()
System::CLib::ScanString()
System::CLib::StrCmp()
System::CLib::StrLen()
etc.

Memory Manipulation

If it is inevitable to directly manipulate memory regions, the following functions can be used:

Util memory manipulation functions defined in murl_util.h:

Util::MemClear()
Util::MemSet()
Util::MemCopy()
Util::MemCopyArray()
Util::MemMove()
Util::MemCompare()
Util::Fill()
Util::FillArray()

System::CLib memory manipulation functions defined in murl_system_clib.h (forwards to the C library):

System::CLib::MemSet()
System::CLib::MemCopy()
System::CLib::MemMove()
System::CLib::MemCompare()


Copyright © 2011-2024 Spraylight GmbH.