Topic
9 replies Latest Post - ‏2014-06-06T20:52:11Z by llandale
Mathias Mamsch
Mathias Mamsch
1762 Posts
ACCEPTED ANSWER

Pinned topic eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

‏2013-02-19T18:50:41Z |

Hi all,

there have been some post (e.g. here), that deal with the problem of a DXL script leaking memory or getting slower. A lot of the time happens, because a DXL script does not deallocate the memory it allocates. Most of the time a memory leak can be fixed by simply deallocating the objects or handling resources better.

For some data types DXL misses a function to deallocate the memory that has been allocated, e.g. Stream, ModuleProperties, Column, Baseline, string (list is not complete). For strings the matters are difficult, since strings can end up in the string table, where they will be permanent even after the DXL ends (see here). For the other data types we can use the fact, that DOORS will free the resources for these types, after the DXL ends.

For this you need to know the eval_ function. Look at the code and the comments:
 

// string eval_ (string) will simply execute some DXL code and return an error string
print "--- Test 1 ---\n"
string s1 = eval_ "print \"Hello eval_!\"" 
 
if (s1 == "") 
    print "\nNo error occured (which is good!)\n" 
else 
    print "\nWhoops! This should never happen\n"
 
// One can return a string from eval_ by using the return_ (string) function 
print "\n--- Test 2 ---\n"
string s2 = eval_ "return_ \"Hello return_!\""
if (s2 == "Hello return_!") 
    print "return_ worked! (which is good!)\n\n"
else 
    print "Whoops! This should never happen\n\n"
 
// If your code is incorrect (=syntax error, code cannot be parsed) eval_ will return the string
// 'errors in eval_ string'
 
print "\n--- Test 3 ---\n"
string s3 = eval_ "incorrect dxl code"
if (s3 == "errors in eval_ string") 
    print "eval_ dumped the above error message and returned our error string (which is good!):\n" s3 "\n"
else 
    print "Whoops! This should never happen\n"
    
 
// if your code contains has a valid syntax, but produces a run time error, the DXL program inside eval_ will stop.
// The calling DXL program however will resume  neither your eval_ dxl program, nor the calling program surpresses this error, then the calling DXL program will
// also stop. 
print "\n--- Test 4 ---\n"
string s4 = eval_ "print (1/0); return_ \"This will not be returned!\"" 
if (s4 == "") 
    print "eval_ dumped the above error messages and stopped but we are still running (which is good!)\n\n"
else 
    print "Whoops! This should never happen\n"
 
 
// You cannot catch a runtime error from the outside of the eval_ context, 
// nor can you suppress the dump of error messages to the console: 
print "\n- Test 5 ---\n"
noError(); 
string s5 = eval_ "print (1/0);" 
print "No error here: |" lastError() "|\n"
if (s5 == "") 
    print "the runtime error was not caught from the outside (which is good!)\n"
else 
    print "Whoops! This should never happen\n"
 
// So you need to catch runtime errors from the inside:
print "\n- Test 6 ---\n"
string s6 = eval_ "noError(); print (1/0); return_ lastError()"
if (s6[0:4] == "-R-E-") 
    print "Our eval_ returned our runtime errror (which is good!)\n"
else 
    print "Whoops! This should never happen\n"

 


So the above code shows the usage of eval_ and how eval_ deals with errors. I remember that Louie had some cases, where you explicitly need an noError() ... lastError() around eval_ (and I always put them there), but from the above examples it seems to be completely useless. If Louie can chime in here, with an example that would be great. Anyway, now you know the following facts:

 

 

 

  • eval_ executes DXL code that is stored in a string
  • to handle runtime errors inside eval_ you need to trap the errors inside the eval_ program and return them to the main program
  • if your eval_ has syntax errors, you will get a string error code 'errors in eval_ string' as a return value from eval_
  • you can return a string from eval_ using the return_ function.

So now that we know what we can do with eval_ how does this help us? Well it is important to understand a bit about the concepts of DXL contexts. A DXL context created by DOORS for every DXL program that is executed. DOORS executes multiple DXL scripts simultaneously at the same time. What?? Will some people ask, but DOORS is single threaded. Yes! But only one DXL program is running at a certain point of time. But there are a couple of conditions, where a DXL program is suspended to run another DXL program:

 

 

 

  • if a DXL program calls 'show(DB)' then the execution of the DXL program is suspended and will not be resumed, except if window messages to the GUI arrive, in which case a GUI callback inside the DXL program can be executed.

 

 

  • If a DXL script opens a module or reads an attribute value, then a lot of stuff can happen. Triggers will be executed, attribute DXL will be calculated, etc. When this is happening the DXL that opened the module will be suspended and be resumed after the operation.

 

 

  • If a DXL script calls eval_ then the eval_ program will run and the script will be resumed afterwards.


So this means, that the DXL Engine needs to stop a DXL program and resume it later. Between these calls it needs to keep track of the current instruction, the values of variables of the DXL program, etc. This information is stored in the DXL context. It also stores the stack size, the runLim (and other pragma settings) the instructions themselfes, the declared types, the allocated objects, etc. ...

The allocated objects list is a special thing. It keeps a reference to all resources that a DXL has allocated (e.g. each buffer, skip, etc.). When an object is deallocated, it will be removed from that list. After a DXL ends, the DXL interpreter will go through this list and call the destructor for each allocated objects to free the resources, that the DXL did not deallocate. We can use this, to make DOORS free all resources, that we cannot free directly from DXL.

But now imagine we want to create a View and for this we want to create columns on the current module. Unfortunately every column that we create, allocates an object that cannot be freed. So if you now want to create views in all modules of your database, then your code is doomed for getting slower and crash, because of the columns you create and delete. But fortunately we can use eval_ to create the views for us, and after your eval_ ends, the columns will be freed by DOORS. So the most simple thing you can do is put the code for creating a view in an eval string and execute it there (Note that to run this code, *you need to have a formal module /Playground/My Module*)

 

int i = 0; count = 0; 
Column c = null
Module m = read("/Playground/My Module", true) 
 
// clear the view
for c in m do count ++;  // no leak here by the way! 
for (i = 0; i < count; i++) delete column 0 // leak here, since column 0 allocates an object
c = insert column 0
attribute (c, "Object Identifier"); width(c, 150)
c = insert column 1
main (c); width(c, 400)


Now to put that code to an eval_ context, we need to escape the quotes and the backslashes (we have none) with a backslash (which can be done in every editor) and store it in a string. So we can do:

 

 

// Cool thing that DXL takes multi line strings, so you can just replace " by \" in some editor and paste the code to your DXL
string sCode = "
int i = 0; count = 0; 
Column c = null
Module m = read(\"/Playground/My Module\", true) 
 
// clear the view
for c in m do count ++;  // no leak here by the way! 
for (i = 0; i < count; i++) delete column 0 // leak here, since column 0 allocates an object
c = insert column 0
attribute (c, \"Object Identifier\"); width(c, 150)
c = insert column 1
main (c); width(c, 400)
"
 
eval_ sCode


You will note that I hardcoded the module name inside that code. Why did I not use current? Well the problem here is, that each DXL program has its own current module (current functions show some weird behaviour, so this is maybe not 100% true), but in any way the current Module will NOT be passed on to the eval_ context, neither will be the current Folder. Both of them will be null. Additionally you might want to execute the same code on different modules, so how can you pass parameters to the eval code and how can you get back return values? The problem is, that both DXL programs run completely independent and do not know anything from each other, including their variables.

The naive approach is to create code, that declares DXL variables and assign them with values the values inside the eval_ code:

 

void printString(string sPar) {
 
// note that sCode is a multi line string, into which the 
// string sPar is inserted: "..." sPar "..." 
 
    string sCode = "
string x = \"" sPar "\"
print x
"
   print "Executing code:\n" sCode "\n"
   eval_ sCode
}
 
printString "Hello eval_ function!"


However this approach has two very bad side effects:

 

 

  • The function litters the string table tremendously if called often, since a new code string is build every time
  • You can only pass literals (i.e. strings, integers) like this to the eval_. You cannot pass a Module like this, you would need to allocate the module, by inserting a read/edit() statement.


One could think that you can work around the string table littering by using a buffer and eval_ tempStringOf bufCode, but the string that is passed to eval_ is still copied to the string table. Rats. So what can we do about this? DXL knows the concept about references. You can read all about it here:

https://www.ibm.com/developerworks/forums/thread.jspa?messageID=14684538&#14684538

So we can pass a reference to a variable in the DXL context. For this we need to know the address of that variable in memory and pass that number to the DXL code. This way inside the DXL code we can recreate that reference to a variable again:

 

int i = 10 
int adInt = (addr_ (&i)) int
 
// Note all non basic types are already references, so we do not need to create a reference
// Note that if we pass the address of a variable (&i) in, we need to create a reference in the eval_
// code too: int &iEval
 
// If we pass the value in (which is a reference by its nature) --> Module m
// then we create a normal variable in the DXL context too:  Module modVar 
Module m = current 
int adMod = (addr_ m) int
 
string sCode = "
int &iEval = addr_ " adInt "
Module modEval = addr_ " adMod "
 
print \"From eval: I = \" iEval \" and M = \" (fullName modEval) \"!\n\"
"
 
print sCode
eval_ sCode


The DXL code that we are creating here is something like this:

 

 

int &iEval = addr_ 182754240
Module modEval = addr_ 181847240
print "From eval: I = " iEval " and M = " (fullName modEval) "!\n"


As you can see, the reference iEval is initialized to the memory address 182754240 (which is the memory address of i in the main DXL program). And modEval is assigned to 181847240, the value of the variable m from the main DXL program. To understand that, you need to know what addr_ does. There is not much to say. addr_ is a function that returns the parameter you pass in.

So it works like this:

 

 

int addr_ (int x) { return x }


pretty simple, eh? The good thing about addr_ is that it uses underscore types for both, the parameter and the return value, which means you can pass any type in and get any type out. So this function is a type-casting function. You can use it to convert one type into another, for example a char into an int:

 

 

char ch = 'A'
int a = (addr_ ch) int 
print a // should give something like 65


In the eval_ code above we pass therefore pass the value of m and the address of i to the eval_ context. This works, since m is itself a reference, the same is true for buffers. Note that we could also pass the value of the integer i to the DXL context. In this case we could not modify I from the DXL context. If we would modify iEval we would change the value if I in the main context, if we would modify modEval, we would NOT modify the contents of m. This is actually very easy to understand. If you read the following code:

 

 

Module m = current 
Module modEval = m 
modEval = read("MyModule")

Then you can see, that the modification of modEval does not result in a modification of m. If you do however

 

 

Buffer buf = create() 
Buffer b2 = buf
b2 += "Hello"


Then a modification of b2 would result in a modification of buf. The same is true for the eval. Passing the value of a Buffer in we would be able to modify the buffer contents, but not reassign the buffer to another Buffer object. So in this case we would NOT modify the contents buf:

 

 

Buffer buf = create() 
Buffer b2 = buf
b2 = create()

So the cool thing about the references and memory addresses is, that two DXL programs can share the same variables, that means we can pass in each variable we like in the eval context. This always works, since we can be sure, that the variables of the main program stay in memory, while the eval_ executes and do not change. Unfortunately this is not true the other way around. So if you create something in the eval_ context, and you want to pass it out, then this will eventually fail, since all allocated objects that were created in the eval_ context will be destroyed after the eval_ code ends. So this code, does not do what you want:

 

 

Skip sk = create() 
 
string sCode = "
Skip skEval = addr_ " ((addr_ sk) int) "
string sEval = \"A String in eval_\"
sEval = sEval[0:7] // Comment this line out and everything will work
put (skEval, 0, sEval)
"
 
print "The code: " sCode "\n\n"
eval_ sCode
 
// Comment the below line in, if the sEval line is active, and things 
// will start to break: 
// int i, ar[100]; for (i = 0; i < 100; i++) ar[i] = 123
 
string s = ""
if (find(sk, 0, s)) {
   print "Found string with index 0: |" s "|\n"
}


Now this is very important to understand. The above code seems to work as it is. The eval_ code puts a string to the Skip that it got passed. The main program prints the string. Good? No! This code contains a critical bug, which shows up, when you remove the comments from the line with the array. Weird? The array line has nothing to do with the string it just creates and fills an integer array, why makes it stuff break? Because the string that was created in the eval_ context is freed afte the eval_ ends. The content however is still in memory and intact. Therefore if we print the string contents they are fine, because nothing overwrote the content yet. However if we comment in the array code, then we will destroy the contents of the string, since the array will be allocated in the same space where the string was before.

Therefore the following rule applies to eval_

 

 

 

  • Never pass out any object that was allocated inside the eval_ context to the main program.
  • The only safe types to pass in and out are non reference types, e.g. int.


So how can we return information then safely from the eval_ context? This simply does not work for all cases. The solution for 90% of the cases is, to simply create the object outside of the eval context and pass that object to the context and modify it there. In some cases, this does not work however. First of all, some types are non-modifiable (like strings). So you cannot modify a string that you pass to the eval_ context. Use a Buffer instead. And in some cases you might want to create stuff inside the eval_ context (e.g. open a file stream) and use it from the main program. In this case, you need to change your program logic. Your Stream will be closed, when the DXL ends. No way around that (except keeping the DXL context alive, which is part of another tutorial ;-))

So now we know how to pass information in and out of an eval_ context and know of the dangers of returning objects that were allocated inside the eval_. Now the only thing that we need to cope with, is to avoid the creation of code for each call of the eval_ to not litter the string space. The solution to this is, to declare global reference variables, for example buffers that are used for passing information from and to the eval_ context. You never reallocate those objects, you just read and modify their values from inside the DXL context. This way, the memory address that the eval_ context uses will always stay the same. So we will execute the same eval_ code over and over again and we just modify the values of the variables everytime. For example if we take the code from above and modify it a bit:

 

Buffer gBufPrintCode = create() // never reallocate this buffer
string sGlobalPrintCode = "
    Buffer bufEval = addr_ " ((addr_ gBufPrintCode) int) "
    print tempStringOf bufEval
"
 
 
void printString(string sPar) {
   gBufPrintCode = sPar
   eval_ sGlobalPrintCode 
}
 
printString "Hello eval_ function!"


This way we can call the printString functions 1000000 times, without creating a new string for the code each time. So we reached our goals:

 

 

 

 

  • We did not litter the string table
  • We passed parameters to the eval_ context
  • Inside the eval_ context instead of printing a string we could write or read a file and read/set the contents of the global buffer
  • After calling eval_ the leaked object will be gone.


Of course you need to be aware that eval_ will actually take some time, since the DXL code that you pass in will be parsed and executed. This is fast for small code, but never include some file over a network inside the eval_ or performance will drop greatly. Also consider to move more code to the eval_ instead of having very fast code inside the eval, since the relative slow down of the eval_ statement will not be so big.

So folks, I hope you liked the small tutorial (LOL) and I hope it might help someone with something ;-) If you managed to read to the end of this, than feel free to post feedback.

Regards, Mathias

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Updated on 2014-01-06T11:42:56Z at 2014-01-06T11:42:56Z by iron-man
  • llandale
    llandale
    2809 Posts
    ACCEPTED ANSWER

    Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

    ‏2013-02-19T21:29:45Z  in response to Mathias Mamsch

    Like I said, "VooDoo".

    Let me expand upon two examples in that post; which I had to write in order to get a Handle on it ... pun intended.

    Here we see that "b2" is a separate variable but at first is assigned the value of b1 (the location of the string). Appending a string to b2 appends to b1.
    But when we assign b2 to a new Buffer (create), its value changes (new string location) and modifications to it do NOT change the original buffer.

    Buffer b1, b2
    b1 = create() 
    b1 += "after 'b1 = create()'"
     
    b2 = b1
    b2 += " after 'b2 = b1'"    // this ends up in b1
     
    b2 = create()
    b2 += "after 'b2 = create()'" // this is in the new b2
     
    print "\t" b1 "\n"
    print "\t" b2 "\n"
    /*  Output:
            after 'b1 = create()' after 'b2 = b1'
            after 'b2 = create()'
    */
    


    Here we have 3 variables, m2 gets the value of m1 but m3 is assigned as a reference. Again, m2 = m1 means it gets the value of m1 (location of the "Module" block of memory),
    but when we change m2 by opening the 2nd module, m1 does not change. But when we set m3 we ARE indeed modifying m1; since it is a reference for it.

    Module  m1 = current 
    Module  m2 = m1
    Module &m3 = m1
     
    print "\t" name(m1) "\t<">\t" name(m3) "\n"
     
    m2 = read("Color Test", false)
    print "\t" name(m1) "\t<">\t" name(m3) "\n"
     
    m3 = read("Color Test", false)
    print "\t" name(m1) "\t<">\t" name(m3) "\n"
     
    /*   Output:
        AttrLayoutTests     <AttrLayoutTests> AttrLayoutTests
            AttrLayoutTests <Color Test>      AttrLayoutTests
            Color Test      <Color Test>      Color Test
    */
    


    -Louie

     

    Updated on 2014-01-06T11:43:31Z at 2014-01-06T11:43:31Z by iron-man
  • SystemAdmin
    SystemAdmin
    3180 Posts
    ACCEPTED ANSWER

    Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

    ‏2013-02-20T09:05:21Z  in response to Mathias Mamsch

    Thanks for taking the time to write that.

    Also consider to move more code to the eval_ instead of having very fast code inside the eval, since the relative slow down of the eval_ statement will not be so big.
     

    I think you are saying this:

    Try to avoid multiple calls to eval_ because the code must be parsed for each call.
    Therefore, do as much as possible in one eval_ call

    Or did you mean something else?

     

    Updated on 2014-01-06T23:16:43Z at 2014-01-06T23:16:43Z by iron-man
    • Mathias Mamsch
      Mathias Mamsch
      1762 Posts
      ACCEPTED ANSWER

      Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

      ‏2013-02-20T11:46:54Z  in response to SystemAdmin
      I meant if you call eval_ a lot, then your slowdown will not be so bad, when you have code, that has an execution time longer than the overhead for the eval.

      If your code runs for one millisecond seconds, and the eval_ adds another millisecond to the evaluation, you have a 100% slowdown. If your code runs 20 ms and eval_ adds one ms, then you only have a slowdown of 5%.

      eval_ is very fast, so its not problem to call it often. But it might be more efficient, to create a whole view with eval_ than to create one single column and call this function 20 times.

      Regards, Mathias


      Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS
      • Mathias Mamsch
        Mathias Mamsch
        1762 Posts
        ACCEPTED ANSWER

        Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

        ‏2013-02-20T12:00:52Z  in response to Mathias Mamsch

        By the way, if you want to avoid having to copy the contents of a buffer, that you pass to eval ( which can be relevant if you pass megabytes of data), then you can use can pass a reference to the eval_ by using a pointer. The problem is you need a static reassignable reference (=pointer) gBufPrintCode and put its static address to the eval code. Unfortunately you have to dereference twice in the eval code. Not nice, but fits its purpose ;-)
         

        Buffer *gBufPrintCode 
        string sGlobalPrintCode = "
            Buffer **bufEval = addr_ " ((addr_ (&gBufPrintCode)) int)"
            Buffer &buf = **bufEval
            print tempStringOf (buf) 
        "
         
         
        void printString(Buffer &sPar) {
           gBufPrintCode = &sPar
           eval_ sGlobalPrintCode 
        }
         
         
        Buffer buf = create(); buf = "Hello eval_ function!"
        printString buf
        

         


        Regards, Mathias

         

         

         


        Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

         

         

        Updated on 2014-01-06T11:44:29Z at 2014-01-06T11:44:29Z by iron-man
        • SystemAdmin
          SystemAdmin
          3180 Posts
          ACCEPTED ANSWER

          Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

          ‏2013-02-22T10:24:31Z  in response to Mathias Mamsch

          Hello Mathias

          > If you managed to read to the end of this, than feel free to post feedback.

          Actually I've done something more than this. We have some programms running in the night and one of the takes about one hour and an other more than four. One of the is a logger and acutually each log-line creates a leak. Avoiding this leaks seems that the time ressource needed by this program can be reduced down to 35 Minutes (I hope to see results at monday).

          But you can automate the process above a little bit more.

          Let us start using the dxl-script-includes:
           

          #include <CommonWindowsRootPath.inc>;
          #include <library/evalCode/GlobalDataForEval_.inc>;
          #include <library/evalCode/createEval_String.inc>;
          


          The first is a module which actually holds one constants (for this problem) and this is the windowsRoot-Path. The second actually only holds your buffer and the third hold a specific funcition which creates a standard-dxl-script directly from a file to a script which can be by eval_().

          The idea is demonstrated in the following snippet.

          #include <CommonWindowsRootPath.inc>;
          #include <library/evalCode/GlobalDataForEval_.inc>;
          #include <library/evalCode/createEval_String.inc>;
           
          static string sFileEvalString = windowsRoot "library/evalCode/createEval_String.inc";
          static string sFileLogWithoutLeak = windowsRoot "library/evalCode/logWithoutLeak.inc";
           
          bufForEval_ = sFileEvalString;
          static string sCreateEvalString = createEval_String(bufForEval_);
          bufForEval_ = sFileLogWithoutLeak;
          static string sLogWithoutLeak = eval_(sCreateEvalString);
          

          So the createEval_String()-Routine creates itself a string which can be used by eval_() and in the next step, the new string converts an other script to an eval_()-String. So you do not have and administration taks to perform, you write and develope your script and if you use in in the program, the script itself does the conversion.

          This is the script:

           

           

           

           

          pragma encoding, "UTF-8"
           
          string createEval_String(Buffer bufLog) { // HEADERLINE
              
                  #include <ZF_PKW/library/common/Strings.inc>;
                  
                  // ENDOFINCLUDES
           
                  string sData = stringOf(bufLog);
                  
                  Skip skpWork = SplitString(readFile(sData), "\n");
                  int iOffset = null;
                  int iLength = null;
                  
                  bufLog = "";
                  
                  string sLine;
                  bool bDone = false;
                  for sLine in skpWork do {
                          if (bDone) { continue }
                          if (findPlainText(sLine,"HEADER" "LINE", iOffset, iLength, true)) {
                                  // Skip this line ...
                                  continue;
                          } 
                          if (findPlainText(sLine, "END" "OF" "INCLUDES", iOffset, iLength, true)) {
                                  string sH = "Buffer bufLog = addr_(" ((addr_ bufLog) int) ");"
                                  bufLog += sH;
                                  continue;
                          } 
                          if (findPlainText(sLine, "END" "OF" "CODE", iOffset, iLength, true)) {
                                  string sH = "return_(sResultString);"
                                  bufLog += sH;
                                  bDone = true;
                                  continue;
                          }
                          bufLog += sLine "\n";
                  }
                  delete(skpWork);
                  
                  string sResultString = stringOf(bufLog)
                  
                  return sResultString; // ENDOFCODE
          }
          



          The script has three specific lines which are marked by comment lines. The "HEADERLINE" will be deleted completely. The eval-String can support includes. But in order to avoid conflicts with the rest of the program, you shall include this includes inside the routine you are writing. In this case you have to define a line, where the reference of the buffer shall be placed. This is "ENDOFINCLUDES". The routine requires that you define a string named sResultString and that you assing the return value. This line is the ENDOFCODE-line. The rest i.e. the } will be scipped.

          So you have to do nothing but including the code snippet either by



           

           

          #include <library/evalCode/createEval_String.inc>;
          

           

          Then you use the routine directly and of course, you have the leaks. 
           
          Or you use:
          


          static string sFileLogWithoutLeak = windowsRoot "library/evalCode/logWithoutLeak.inc";
          bufForEval_ = sFileLogWithoutLeak;
          static string sLogWithoutLeak = eval_(sCreateEvalString)
          {code}

          The you can use it without leaks.

          Thank you very much for this help. I've the feeling, that now I can solve some problems in this smal world.

          Best regards
          Wolfgang

           

          Updated on 2014-01-06T11:46:04Z at 2014-01-06T11:46:04Z by iron-man
          • Richard_Good
            Richard_Good
            45 Posts
            ACCEPTED ANSWER

            Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

            ‏2013-03-01T09:54:45Z  in response to SystemAdmin
            Nice post Mathias. I vote for ousting the present DXL development head or whatever the title is and installing you. A coup headed by forum members is in order ;-) Still reading these posts makes me happy I have more dealings with vb and c# these days than the bad old days of c and c++.
  • Anaheim
    Anaheim
    3 Posts
    ACCEPTED ANSWER

    Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

    ‏2013-10-11T00:41:31Z  in response to Mathias Mamsch

    Hi Mathias

    Can we use the eval_ function to load a module? If so, do you have a sample code to do that?

    Thanks,

  • MaltePlath
    MaltePlath
    4 Posts
    ACCEPTED ANSWER

    Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

    ‏2014-06-06T12:14:35Z  in response to Mathias Mamsch

    Hi Mathias,

    thank you for another brilliant lesson in DXL!  (Actually several lessons just in this thread.)

    I have one addition regarding the behaviour of noError() and lastError() with eval_(): You can catch both syntax errors and runtime errors in the evaluated code.  The trick is to have one noError() outside the eval and one within the eval; any errors that occur are propagated to the calling context.

    bool try_eval(string code, string &outcome)
    {
      string errmsg;
      noError(); // catch syntax errors in code
      outcome = eval_("noError();" code); // catch runtime errors in code
      errmsg = lastError();
      if (null errmsg)
        return true;
      else {
        outcome = errmsg;
        return false;
      }
    }
    
    // Test it...
    string dxl_code[5];
    string result;
    
    // Syntax Error
    dxl_code[0] = "int 2 = 2";
    // Runtime Error
    dxl_code[1] = "return_ fullName(null Module)";
    // User defined error
    dxl_code[2] = "error(\"My own error message!\")";
    // Valid code with return value
    dxl_code[3] = "int x = 1+2+3; x++;\n return_ x\"\"";
    // Valid code without return value
    dxl_code[4] = "print \"today is \"today\"\n\"";
    
    int i;
    for i in 0:sizeof(dxl_code)-1 do {
      print "try_eval():\n";
      print "|" dxl_code[i] "|\n";
      print ((try_eval(dxl_code[i],result)) ? "Result" : "Error") ":\n>>"result"<<\n";
      print "----\n\n";
    }
    

    You can see that the function error(string) only registers as "-I- DXL: execution halted"; you cannot pass out your own error message. But that's a minor niggle.

    Updated on 2014-06-06T12:19:54Z at 2014-06-06T12:19:54Z by MaltePlath
    • llandale
      llandale
      2809 Posts
      ACCEPTED ANSWER

      Re: eval_ / addr_ / DXL contexts / memory leaks: An advanced tutorial

      ‏2014-06-06T20:52:11Z  in response to MaltePlath

      You can also send the eval DXL code to function "checkDXL" in order to trap interpret errors.  Let the code itself trap it's own run time errors.

      -Louie