7. Hello Example – Using MiST Engine API

7.1. The source code
7.2. How it works - step by step
7.3. Building the sample application on Linux with GCC
7.4. Building the sample application on Windows with GCC (MinGW)
7.5. Building the sample application on Windows with Microsoft build tools

The example considered in this section is similar to the final version of the example from the previous section. However, everything will be done here via MiST Engine API from an application written in C. It is recommended to read Section 6, “Hello Example – Using MiST Engine Command Line Tool” first as most of the explanations and notes are not repeated here.

Note that the templates will be processed in the memory in the example below rather than loaded from the file system. The resulting value(s) will be output to stdout.

The sample application described here can be built on Linux with GCC and on Microsoft Windows with MinGW or with the compiler and linker from Microsoft Visual Studio.

It is assumed here that MiST Engine is installed to /opt/mist_engine/ directory on Linux and to c:\mist_engine\ directory on Windows. If you have installed MiST Engine to some other location, please adjust the paths to the header files and libraries accordingly when calling the compiler and linker as described below.

7.1. The source code

First of all, here is the full source code of the sample application. Suppose this code is in a file named sample.c. Note that, for brevity, error handling is omitted for mist_tg_add_value() and mist_tg_clear_values() calls. In a real world application, you should check the return value of these functions too.

#include <stdio.h>
#include <stdlib.h>

#include <mist_engine/mist_engine.h>

int 
main()
{
    CMistNameValuePair source[] = {
    /* "Greetings" template, the top-level one */
        {"Greetings", 
         "Someone says<$if concat(Salutation)$>:\n"
         "<$Hello : join(\\n)$><$else$> nothing.<$endif$>"},
        
    /* "Hello" template */
        {"Hello", 
         "<$Salutation$>, <$if Subject$><$Subject$><$else$>stranger<$endif$>!"}
    };
    size_t ntemplates = sizeof(source) / sizeof(source[0]);
    
    EMistErrorCode ec = MIST_OK;
    CMistTGroup* tg = NULL;
    char* err = NULL;
    size_t bad_index;
    
    const char** result = NULL;
    size_t nvals = 0;
    
    // Initialize MiST Engine.
    ec = mist_engine_init(MIST_ENGINE_API_MAX_VERSION);
    if (ec != MIST_OK)
    {
        printf("Error at line %u: code = %d\n", __LINE__, ec);
        return 1;
    }

    // Create "Greetings" template group from 'source[]' array.
    ec = mist_tg_create(&tg, source, ntemplates, 0, "<$", "$>", 
        &bad_index, &err);
    if (ec != MIST_OK)
    {
        printf("Error at line %u: code = %d\n", __LINE__, ec);
        printf("bad_index = %u, message: %s\n", (unsigned int)bad_index, 
            ((err == NULL) ? "<empty>" : err));
        free(err);
        return 1;
    }
    
    // Set the values of parameters.
    ec = mist_tg_add_value(tg, "Salutation", "Hello");
    ec = mist_tg_add_value(tg, "Subject", "");
    
    ec = mist_tg_add_value(tg, "Salutation", "Hail to you");
    ec = mist_tg_add_value(tg, "Subject", "Dr. Freeman");
    
    ec = mist_tg_add_value(tg, "Salutation", 
        "You are the guy who owes me a beer");
    ec = mist_tg_add_value(tg, "Subject", "");
    
    // Evaluate the template group (create the resulting values).
    ec = mist_tg_evaluate(tg, &result, &nvals);
    if (ec != MIST_OK)
    {
        printf("Error at line %u: code = %d\n", __LINE__, ec);
        mist_tg_destroy(tg);
        return 1;
    }
    
    printf("Number of resulting values: %u\n", 
        (unsigned int)nvals);
    printf("Value #0:\n--------\n%s\n--------\n", 
        result[0]);

    // Clear the values of parameters we have set as well as 
    // the resulting values stored in the template group.
    ec = mist_tg_clear_values(tg);

    // Set the values of parameters again.
    ec = mist_tg_add_value(tg, "Subject", "World");
    ec = mist_tg_add_value(tg, "Subject", "Dr. Freeman");
    ec = mist_tg_add_value(tg, "Subject", "Barney");
    
    // Evaluate the template group once more.
    ec = mist_tg_evaluate(tg, &result, &nvals);
    if (ec != MIST_OK)
    {
        printf("Error at line %u: code = %d\n", __LINE__, ec);
        mist_tg_destroy(tg);
        return 1;
    }
    
    printf("Number of resulting values: %u\n", 
        (unsigned int)nvals);
    printf("Value #0:\n--------\n%s\n--------\n", 
        result[0]);
    
    // Clean up
    mist_tg_destroy(tg);
    return 0;
}

If you now build and run the sample application as decribed below, the output will look as follows:

Number of resulting values: 1
Value #0:
--------
Someone says:
Hello, stranger!
Hail to you, Dr. Freeman!
You are the guy who owes me a beer, stranger!
--------
Number of resulting values: 1
Value #0:
--------
Someone says nothing.
--------

7.2. How it works - step by step

The relevant parts of the source code of the sample application are explained below.

#include <mist_engine/mist_engine.h>

To use MiST Engine API in an application written in C or C++, you need to #include one header file, mist_engine/mist_engine.h.

CMistNameValuePair source[] = {
    /* "Greetings" template, the top-level one */
        {"Greetings", 
         "Someone says<$if concat(Salutation)$>:\n"
         "<$Hello : join(\\n)$><$else$> nothing.<$endif$>"},
        
    /* "Hello" template */
        {"Hello", 
         "<$Salutation$>, <$if Subject$><$Subject$><$else$>stranger<$endif$>!"}
    };
    size_t ntemplates = sizeof(source) / sizeof(source[0]);

source[] array defined here contains an element for each template in the template group to be created. Each element is a structure containing two strings (const char*): name of the template and the string to load the template from. The element corresponding to the main (top-level) template of the group is not required to be the first in the array. Its index will be passed to mist_tg_create() function as one of the parameters.

ntemplates is the number of the elements of source[] array (it is 2 in this case).

    EMistErrorCode ec = MIST_OK;
    CMistTGroup* tg = NULL;
    char* err = NULL;
    size_t bad_index;
    
    const char** result = NULL;
    size_t nvals = 0;

Other variables needed by the application are defined here.

tg will represent the template group.

ec will be the error code returned by MiST Engine API functions, MIST_OK code means that no error occured.

err and bad_index will be passed to mist_tg_create() function. If this function fails to create the template group (due to a syntax error in a template, for example) it will return a description of the failure in *err and the index of that bad template in *bad_index.

After the group is evaluated, result will be the array of the resulting string values and nvals will contain the number of these values.

    ec = mist_engine_init(MIST_ENGINE_API_MAX_VERSION);
    if (ec != MIST_OK)
    {
        printf("Error at line %u: code = %d\n", __LINE__, ec);
        return 1;
    }

You need to call mist_engine_init() before calling any other MiST Engine API function. Its argument will usually be MIST_ENGINE_API_MAX_VERSION, see Section 9, “MiST Engine API Reference” for details on using different versions of the API.

    ec = mist_tg_create(&tg, source, ntemplates, 0, "<$", "$>", 
        &bad_index, &err);
    if (ec != MIST_OK)
    {
        <...>
        free(err);
        return 1;
    }

mist_tg_create() attempts to create a template group from the data contained in source array. ntemplates elements of this array are to be processed. The element corresponding to the top-level template has index zero in this case, therefore zero is passed to the function as the fourth argument.

<$ and $> are the markers to denote the beginning and the end of each placeholder in the templates.

If an error occurs, err may contain a string with the description of this error. If so, this string should be freed when no longer needed.

    ec = mist_tg_add_value(tg, "Salutation", "Hello");
    ec = mist_tg_add_value(tg, "Subject", "");
    
    ec = mist_tg_add_value(tg, "Salutation", "Hail to you");
    ec = mist_tg_add_value(tg, "Subject", "Dr. Freeman");
    
    ec = mist_tg_add_value(tg, "Salutation", 
        "You are the guy who owes me a beer");
    ec = mist_tg_add_value(tg, "Subject", "");

These calls set the values of Salutation and Subject parameters (3 values for each of the parameters).

    ec = mist_tg_evaluate(tg, &result, &nvals);

Now that the template group is created and the values of the parameters are set, you can call mist_tg_evaluate() to produce the resulting values. These are the values of the top-level template, actually. The function will return the values in result[] and the number of values in nvals.

If you would like to evaluate the template group for another set of input values, you should reset the group first:

    ec = mist_tg_clear_values(tg);

This will clear the values of the parameters and the templates in the template group. After that, you can assign new values to the parameters.

    ec = mist_tg_add_value(tg, "Subject", "World");
    ec = mist_tg_add_value(tg, "Subject", "Dr. Freeman");
    ec = mist_tg_add_value(tg, "Subject", "Barney");

A new set of values is assigned here to Subject parameter. Note that Salutation parameter will have no values assigned this time. This is the same as if it had only one value, an empty string.

    ec = mist_tg_evaluate(tg, &result, &nvals);

The template group is now evaluated for the new set of values.

    mist_tg_destroy(tg);

When you are done with the template group, you should call mist_tg_destroy() for it to release memory it occupies.

7.3. Building the sample application on Linux with GCC

To build the sample application on Linux with GCC (version 4.0 or newer is preferable), you can use the following command:

gcc -I/opt/mist_engine/include      \
    -o sample sample.c              \
    -Wl,-rpath,/opt/mist_engine/lib \
    -L/opt/mist_engine/lib -lmist_engine

The newly created application can now be run:

./sample

Note

You may omit that linker option dealing with RPATH (-Wl,-rpath,/opt/mist_engine/lib). This option allows the sample application to know where to load MiST Engine shared library from when the application is executed. If you plan to use some other technique to tell the application where the shared library is (via LD_LIBRARY_PATH or in a different way), you do not need that linker option.

7.4. Building the sample application on Windows with GCC (MinGW)

To build the sample application on Microsoft Windows with MinGW (GCC version 4.0 or newer is preferable), you can use the following command:

gcc -Ic:\mist_engine\include -o sample.exe sample.c -Lc:\mist_engine\lib -lmist_engine

Note

Note that you do not need to use MSYS here but you can if you want to.

Before you execute the newly created application, make sure MiST Engine shared library (mist_engine.dll) is visible to it. For example, you can copy the DLL to the directory where the application is located or add c:\mist_engine\lib directory to PATH environment variable, etc.

After that, the sample application can be run:

sample.exe

7.5. Building the sample application on Windows with Microsoft build tools

To build the sample application on Microsoft Windows the compiler and linker from Microsoft Visual Studio you can execute the following commands:

cl.exe /c /Ox /Ic:\mist_engine\include\ sample.c
link.exe /OUT:sample.exe /LIBPATH:c:\mist_engine\lib mist_engine.lib sample.obj

This should work with Visual Studio 2003, 2005 and 2008, may work with other versions as well.

If you would like to build the sample application from Visual Studio IDE rather than from a command prompt, you should set the following properties of your Visual Studio project among other things:

  • Add c:\mist_engine\include\ directory to Additional Include Directories (C/C++, General).

  • Add c:\mist_engine\lib\ directory to Additional Library Directories (Linker, General).

  • Add mist_engine.lib import library to Additional Dependencies (Linker, Input).

Before you execute the newly created application, make sure MiST Engine shared library (mist_engine.dll) is visible to it. For example, you can copy the DLL to the directory where the application is located or add c:\mist_engine\lib directory to PATH environment variable, etc.

After that, the sample application can be run:

sample.exe