TM1 Stack Frame for Function Returns

Post Reply
bgregs
Posts: 77
Joined: Wed Sep 12, 2018 11:19 am
OLAP Product: TM1 / Planning Analytics
Version: 2.0
Excel Version: 2016

TM1 Stack Frame for Function Returns

Post by bgregs »

* Mods feel free to move this if it is in the wrong place!

In an attempt to simplify child -> parent return data, I decided to see if I could implement a reliable stack structure within TM1 to easily pass values between functions without having to write anything to a cube. My main requirements were:

1) No cube writes - I wanted to see if I could easily pass values between processes without having to rely on cubes (and views); I want simple, functional programming style returns
2) Minimal code - this will never fly if I have to remember to initialize/call 5+ TIs
3) No outside code - can only use TM1 functions and objects

What I came up with expands the functionality of session variables by allowing multiple values to be returned through a reference to a dimension acting as a stack. The basic concept is as follows:

Code: Select all

StringSessionVariable('session.stack');		        /* reference to the stack name (private) */
StringSessionVariable('session.stack_prefix');	        /* reference to a predefined prefix (private) */
StringSessionVariable('session.stack_return');	        /* reference to a popped value (public)
NumericSessionVariable('session.stack_pointer');	/* reference to the stack pointer (private) */
NumericSessionVariable('session.stack_sanity');	        /* reference to the sanity check (public) */
Each time a value is pushed to the stack, the stack_pointer is incremented and the prefix is prepended to the value in order to ensure it is unique. When the value is popped, the opposite happens - stack_pointer is decremented and the value is deleted from the dimension (stack). The full pushed value on the stack will look like: "1: test1" after the prefix is added. When popped, the value will be loaded to session.stack_return, which can be accessed by any process.

The stack_sanity variable is included as a sort of macro to ensure that our stack is always in sync. On each successful pop or push the stack_sanity variable is redefined to DIMSIZ(session.stack) - stack_pointer. In this way, we can ensure that our stack is synced in case of a bad run that causes our pointer to be misaligned with the current element count. It is probably safer to check that stack_sanity is equal to 0 before using a popped value.

A simple implementation could look something like the following:

Code: Select all

StringSessionVariable('session.stack_return');
NumericSessionVariable('session.stack_sanity');

ExecuteProcess('push', 'psStr', 'test1');		        /* push "test1"; the stack will initialize if not already done */
ExecuteProcess('push', 'psStr', '15');			        /* push "15"; all values are pushed as strings - ints must be cast
								                        appropriately before use after popping */
ExecuteProcess('pop', 'pnType', 0);			        /* pop the last value ("15") LIFO style */

if (session.stack_sanity = 0);				        /* check that we're synced */
        asciioutput('test_file.csv', session.stack_return);	/* session.stack_return contains our popped value */
endif;
This is obviously a pretty rough draft and the idea could definitely be expanded on to add some much needed quality-of-life features, but it's functional as it currently stands. This was built to avoid having to use cubes (and therefore assembled views) to transfer linear lists of data. I have found it useful in capturing errors in child processes by pushing a reference to them to the stack as they occur, and then popping them off when I get back to the parent process - basically pushing pointers to the error messages. In this way, I can easily record what happened during the entire execution chain without needing to keep a logging cube or something similar.

If you want to pop multiple values, consider pushing the number of elements you want to pop as the final element of the stack. Then when you pop the first value in a different process, you can use that as the condition for a while loop to pop the rest of the values.

This may not be game changing and probably won't ever see anything more than a development environment, but it was at least fun to play with. If you're interested, the code with the sample implementation can be found at my github link: https://github.com/bgregs514/tm1_code
User avatar
Mike Cowie
Site Admin
Posts: 482
Joined: Sun May 11, 2008 7:07 pm
OLAP Product: IBM TM1/PA, SSAS, and more
Version: Anything thru 11.x
Excel Version: 2003 - Office 365
Location: Alabama, USA
Contact:

Re: TM1 Stack Frame for Function Returns

Post by Mike Cowie »

Wow, bravo for seeing this through and thanks for sharing! I did something similar once in a small stack data structure (in temp cube + dimensions, of course) to help recurse through a hierarchy regardless of the numbers of levels of depth. While I was proud it worked without a lot of TI script code and pollution of message logs with TI proc executions it definitely didn't feel like something too many people would be able to follow. I really wish this was the sort of thing TI supported natively along with other data structures... maybe someday the language will get an update.
Mike Cowie
QueBIT Consulting, LLC

Are you lost without Print Reports in Planning Analytics for Excel (PAfE)? Get it back today, for free, with Print Reports for IBM Planning Analytics for Excel!
David Usherwood
Site Admin
Posts: 1454
Joined: Wed May 28, 2008 9:09 am

Re: TM1 Stack Frame for Function Returns

Post by David Usherwood »

It's arguably a step too far to go to TM1PY just for a decent set of coding structures, but it certainly makes the experience more enjoyable.
Post Reply