- Written and documented by David Deley


1:     2:     3:     4:     5:     6:     7:     8:     9:     10:     11:     12.1:     12.2:     12.3:     12.4:    

Introduction to CMac Chapter 10: ARRAYS & STRUCTURES
Test code is provided below as an attachment.)

Arrays of integers and real numbers can only be defined within a structure. First a review of structures:

Code:
struct tstruct1
{
  int i;
  real j;
  str s1;
}

void test1
{
  struct tstruct1 m;

  m.i = 10;
  m.j = 3.14;
  m.s1 = "This is a test";

  make_message( 'm.i=' + str(m.i)
              + ' m.j=' + rstr(m.j,7,4)
              + ' m.s1="' + m.s1 + '"');
}


ARRAYS INSIDE A STRUCTURE
Code:
struct tstruct2
{
  int i[4];
  real j[4];
  str s1;
}

void test2
{
  struct tstruct2 m;

  m.i[0] = 10;
  m.i[1] = 12;
  m.i[2] = 123;
  m.i[3] = 988;

  m.j[0] = 3.14;
  m.j[1] = 2.718;
  m.j[2] = 1.1213;
  m.j[3] = 9.876;

  m.s1 = "This is another test";

  make_message( 'm.i[3]=' + str(m.i[3])
              + ' m.j[2]=' + rstr(m.j[2],7,4)
              + ' m.s1="' + m.s1 + '"');
}


HOW TO STORE A STRUCTURE IN A GLOBAL STRING VARIABLE
using Struct_to_Str and Str_to_Struct:
Code:
global
{
  //See CMAC help on "Global Variables"
  //for explanation of @,!,~,#
  str g_MyGlobalStr "@MyGlobalStr";
}

struct tstruct3
{
  int i[4];
}

void test3()
{
  struct tstruct3 m;
  str tempstr;

  m.i[0] = 10;
  m.i[1] = 12;
  m.i[2] = 123;
  m.i[3] = 988;

  Struct_to_Str(tempstr, m);
  g_MyGlobalStr = tempstr;
  make_message("Structure m stored in global string g_MyGlobalStr");
}

void test4()
{
  struct tstruct3 m;
  str tempstr;

  tempstr = g_MyGlobalStr;
  Str_to_Struct(m, tempstr);

  make_message( 'm.i[3]=' + str(m.i[3]));
}

Above first run test3, then run test4.
Notice the use of local variable 'tempstr'. Struct_to_Str and Str_to_Struct require local variables.
For another example see:
http://www.multieditsoftware.com/forums/viewtopic.php?p=2162#2162


ARRAY OF STRINGS
CMAC does not directly support "an array of strings". However, here are two methods of emulating an array of strings.

1. Array of fixed length strings in one long string:
Code:
void test5()
{
  str month_names = "January  " + //  1
                    "February " + //  2
                    "March    " + //  3
                    "April    " + //  4
                    "May      " + //  5
                    "June     " + //  6
                    "July     " + //  7
                    "August   " + //  8
                    "September" + //  9
                    "October  " + // 10
                    "November " + // 11
                    "December ";  // 12
  int month_name_length = 9;

  str month;
  int month_number = 7;  //Select "July     "

  //EXTRACT OUR STRING
  month = copy( month_names,
                (1 + ((month_number-1) * month_name_length)),
                month_name_length);

  //TRIM TRAILING SPACES (Also see "Remove_Space" function)
  month = shorten_str(month);

  Make_Message('month="' + month + '"');
}

NOTE: Strings, both global and local, are limited to MAX_LINE_LENGTH, currently 16,383 characters. (This restriction may change in Multi-Edit 10.)
For another example see
http://www.multieditsoftware.com/forums/viewtopic.php?p=1537#1537


2. A Multi-Edit window can be an array of strings. Here's an example of creating a hidden Multi-Edit window and using it as an array of strings.

Code:
macro_file test6;

#include MEW.sh //Defines _wa_SystemHidden

prototype test6
{
  void Create_Str_Array( int &win_id );
  void Clear_Str_Array( int win_id );
  void Put_Str_Array( int index, str elem, int win_id );
  str  Get_Str_Array( int index, int win_id );
  void Delete_Str_Array( int win_id );
}

//-------------------------------------------------------------
// NOTE: Notice the ampersand &win_id, we are returning a
// value in win_id so ampersand says pass argument by
// reference instead of by value so we can return the value.

void Create_Str_Array( int &win_id )
{
  int start_win_id = Window_ID;
  int save_refresh = Refresh;
  Refresh = FALSE;

  //CREATE NEW WINDOW FOR STRING ARRAY
  Create_Window;
  if (Error_Level > 0) {
      RM('MEERROR');
      goto EXIT;
  }
  win_id = window_id;  //Window ID of new window
  Window_Attr = _wa_SystemHidden;

  Switch_Win_Id(start_win_id);  //Go back to original window

EXIT:
  Refresh = save_refresh;
}

//-------------------------------------------------------------
void Clear_Str_Array( int win_id )
{
  int start_win_id = Window_ID;
  int save_refresh = Refresh;
  Refresh = FALSE;

  if (!Switch_Win_Id(win_id)) {goto EXIT;}
  RM('block^selectall');
  Delete_Block;
  Switch_Win_Id(start_win_id);  //Go back to original window

EXIT:
  Refresh = save_refresh;
}

//-------------------------------------------------------------
void Put_Str_Array( int index, str elem, int win_id )
{
  Put_Line_To_Win(elem, index, TranslateWindowID(win_id), FALSE);
}

//-------------------------------------------------------------
str Get_Str_Array( int index, int win_id )
{
  return (Get_Line_From_Win(index, TranslateWindowID(win_id)));
}

//-------------------------------------------------------------
void Delete_Str_Array( int win_id )
{
  int start_win_id = Window_ID;
  int save_refresh = Refresh;
  Refresh = FALSE;

  if ( Switch_Win_Id(win_id) )
  {
    delete_window;
    Switch_Win_Id(start_win_id);  //Go back to original window
  }
  Refresh = save_refresh;
}
//-------------------------------------------------------------
//Now test it out:
void test6()
{
  int MyStrWinID;

  Create_Str_Array( MyStrWinID );
  Put_Str_Array( 1, "This is a test line #1", MyStrWinID );
  Put_Str_Array( 2, "This is a test line #2", MyStrWinID );
  Put_Str_Array( 3, "This is a test line #3", MyStrWinID );
  Put_Str_Array( 4, "This is a test line #4", MyStrWinID );
  Put_Str_Array( 5, "This is a test line #5", MyStrWinID );

  Make_Message( Get_Str_Array( 3, MyStrWinID ));

  Delete_Str_Array(MyStrWinID);
}

The above is a bit complicated, so here are a few notes:

macro_file test6; - The compiled output file will be test6.mac

prototype test6 - test6 here should match test6 in macro_file test6; above.

void Create_Str_Array( int &win_id ) - The & here means the caller should pass the variable by reference instead of by value so we can return a value in it. See CMAC help on "Reference parameters". (An interesting caveat is the caller does not need to change anything. The CMAC compiler takes care of the rest from here.)

Refresh = FALSE; - This cuts down on screen flickering. Notice we save the initial setting of system variable 'Refresh' and restore it when returning.

Window_Attr = _wa_SystemHidden; - Setting the newly created window attributes to _wa_SystemHidden means the window won't show up as a user window when you do a WINDOW -> LIST, and you don't have to give it a filename.

void Clear_Str_Array( int win_id ) - Although not actually used in the above example, this is how to erase a window if you ever wanted to. (A caveat of the system function 'Erase_Window' is that it erases not only the contents of the window but also the File_Name associated with the window.)

Put_Line_To_Win - This replaces the current line with the new line you specify. If the current line is beyond the [EOF] it still inserts the new line at the proper line number and [EOF] gets moved to the new end of file.

TranslateWindowID(win_id) - There's a difference between a window number and a window ID. A window number is a number between 1 - 127 (assuming that's the highest number of windows we can have. The limit might be 255.) A window ID is something different. It's a very large number. Some system functions ask for a window ID while other system functions ask for a window number. 'TranslateWindowID' converts a window ID to a window number. Tech note: A window's number may change as windows are added and deleted. A window's ID will not change. Window numbers are useful when you want to cycle through all existing windows.)

For testing the above macro you could comment out the call to 'Delete_Str_Array'. Then to view the newly created window and its contents do MACRO -> RUN (or click the Image button) and enter the command "SWITWIN /SYSTEM=2" (there has to be a space before the /SYSTEM=2). Your window should be the one at the bottom called "?No-File?". You can switch to it and view it. You can delete this window manually. Be careful not to mess up the contents of other system windows. Also be careful not to create too many windows. The limit I think is 127. (If you view a system window, when you're done with the window right click the button for that window or right-click the window's title bar and select "Hide". We don't want to close a system window.)

You could expand the above code to save your window as a file, and to load the saved file next time you run Multi-Edit. (That's a bit more complicated. Maybe someday I'll create an example of that.)

The above example is a modified version of that given by gwhite at
http://www.multieditsoftware.com/forums/viewtopic.php?p=1560#1560



test1-6.zip
 Description:
Code for tests 1-6

Download
 Filename:  test1-6.zip
 Filesize:  5.41 KB
 Downloaded:  188 Time(s)

1:     2:     3:     4:     5:     6:     7:     8:     9:     10:     11:     12.1:     12.2:     12.3:     12.4: