Introduction

The two cornerstone features of any generic programming platform are encapsulation and reuse. These related ideas are simply the ability to write a piece of code once and then use it many times.

Being able to call processes within other processes is a useful technique for performing a set repeated task, but what if you would like the two processes to interact and communicate?

“Hey”, I hear you say, “TI has that covered, doesn’t it?” Well, sort of!  Yes, the ability to call a process from within another can allow processes to act like functions or procedures, and yes, they do accept parameters, so the calling process can modify the behavior of child process.

But what about return values? What if you want to build a utility function that performs some processing, and returns a value to the calling process?

This post will discuss various ways to tackle this very problem.

Techniques

Global temporary cube

Since cubes are globally available to all TI processes, it’s a natural first choice in storing global variables that can act as return values. You would probably take the following steps:

  • The calling process would call a initialization function, that would receive a parameter to tell it what particular “global variable” measure to create.
  • The calling process would then call the child process, passing a parameter containing the name of the “global variable” measure.
  • The child process would do it’s processing, and write it’s result to the parameter cube using the appropriate CellPut function.
  • The calling process would read this value and continue.

Immediately, you can probably see issues with this approach. For one, it’s pretty messy, and creates annoying control cubes, and elements with temporary names. Sure, you can hide these with security, but it still clutters up the view for administrators.

Secondly, it’s quite inefficient. You could potentially have predetermined elements for your return values, but then you have to worry about concurrent processing and if a user mistakenly interferes with values in the cube using Perspectives or Excel. The speed of writing to and reading from a cube is probably not optimal for such a core requirement.

User-defined Global Variables

TI supports the idea of user-defined global variables. These are the same as local variables, but are available to all the functions being called within the call stack of the parent process or chore group. This is quite useful for the purposes of return values, and avoids the mess of using cubes.

Here’s an example:

StringGlobalVariable('sReturnValue');

ExecuteProcess('GetReply', 'pStatement', 'Hello');

sReply = sReturnValue;

In the child process, you could do the following:

StringGlobalVariable('sReturnValue');

IF(pStatement @= 'Hello');

    sReturnValue = 'Goodbye';

ENDIF;

ProcessBreak;

The end result of this would be your variable, ‘sReturnValue’ would contain the string ‘Goodbye’.

The limitation of this approach is mostly dictated by the maximum length of string values in Turbo Integrator. In the current version, TM1 10.1, this is 65536 characters, which is ample for many applications. Just be wary of using this technique in earlier versions of TM1, because prior to 9.4 and the Unicode upgrade, the maximum length of a string was 256 characters.

Update: The upper limit of a string passed to ASCIIOUTPUT is 64K. However, a string variable appears to be able to have a larger size, depending on the amount of available RAM. On my laptop with 4GB RAM installed, I was able to create a string variable of around 512K. Anything larger and my entire system would crash!

You’re also well-advised to grab the value of the global variable immediately after the ExecuteProcess call and assign it to a local variable. This is simply so you don’t get confused if making multiple calls to other processes which may use the same global variable name as others called in your parent process.

Conclusion

TI Libraries like Bedrock are great for processes that perform stand-alone operations, but do not support the idea of reusable processing functions that return a value to their caller.

These two a simple techniques can be extremely powerful for creating generic, reusable processes. Start using it in your development, and you’ll begin to see what I mean!

Happy modelling!