Below is a simple example of a C function which is called from a QA Build program:
int b_open_file()
{
char * impfile;
/* obtain the filename from the local QAB field, IMPFILE */
impfile = (char *) Qget_pointer(0, "IMPFILE");
if (tf_check(impfile))
file_nr = tf_open(impfile, VF_RO);
return 0;
}
|
The major points to consider are function names, database and local fields used in the QA Build program and return values. These topics are covered in detail in the following sections.
The function name is the same as that of the procedure body, in lowercase and prefixed with b_.
The use of the b_ prefix avoids any clashes with library functions. However, if you wish to change or delete the prefix, you can edit maps 9 and 10 of the mapset PROG2C on the SYSTEM database. These maps are used by the Program Compiler to generate the calls to your C functions in the .C file.
Consider a procedure body in a QA Build program called OPEN_FILE. The procedure type is Link to C. Within the Program Editor the procedure body is named:
OPEN_FILE
Within the C source code the function is named:
b_open_file
The local fields used in the calling QA Build program may be accessed using pointers.
For a generic Program Interpreter you need to use the function Qget_pointer() to obtain the value of the pointer. Value of pointer = Qget_pointer(0, "field name");
The field name should be the name used in the QA Build field processing. The name can be in upper or lower case but should not contain underscores.
Within a QA Build program IMP_FILE is defined as a character field of length 12 and single occurs.
Within the C source code IMP_FILE is accessed using a pointer defined:
char * impfile;
The appropriate value of the pointer is set using Qget_pointer():
impfile = (char *) Qget_pointer(0, "IMPFILE");
For a dedicated Program Interpreter you do not need to set the value of the pointer.
If you wish to remove the v_ prefix you should edit the maps 25 to 38 in the PROG2C mapset on the SYSTEM database.
Within a QA Build program IMP_FILE is defined as a character field of length 12 and single occurs.
Within the C source code IMP_FILE is accessed using a pointer defined:char * v_impfile;
Standard fields, such as TODAY are not accessed using Qget_pointer(), but are accessed directly. Thus: this_day = today;
Database fields used in the calling QA Build program may be accessed using structure pointers. These pointers are defined in the .H file generated by LIBMAKE. Note that for a Generic PROGRUN, you must obtain the value of the pointer with Qget_pointer() before a record can be accessed: record pointer = Qget_pointer(Table Reference Number, "role name")
For either type of PROGRUN fields may be accessed, in lowercase, using the syntax:
table_name.table_role->field_role.field_name
For blank field and table roles the syntax becomes:table_name.field_name
Consider a table EMPLOYEE which is given a role of MAIN within the Program Editor. Within the C source code the record pointer is obtained using Qget_pointer():
employee.main = (struct EMPLOYEE*) Qget_pointer(_EMPLOYEE,"MAIN");
The field EMP_NR with role MANAGER can then be accessed by:
employee.main->manager.emp_nr
Note that this is the reverse to the field role order within QA Build formula processing (employee.main.emp_nr.manager).
The function Qget_pointer() returns 0 if the named item is not one of the variables in the current QA Build program. You will need to test that the value returned is not 0 before using it. One way to do this is to write your own enclosing routine for calls to Qget_pointer():
void *my_Qget_pointer(int number, char * name)
{
void *retcode = Qget_pointer(number,name);
if (retcode == 0)
{
map_pause(HORRIBLE_ERROR);
halt();
}
}
|
If you want to add table variables of your own, not used in the QA Build program, you may have problems. LIBMAKE will generate a structure definition and an initialised pointer variable for the structure. You need a simple local variable to allocate space for the structure in the data segment. If your table is called TABLE, you can declare a record like this:
struct TABLE my_table = {sizeof(struct TABLE),_TABLE};
Consider the field EMP_NR with a role of MANAGER, belonging to the table EMPLOYEE with a role of MAIN.
Within QA Build formula processing it is accessed by:employee.main.emp_nr.manager
Within the C code of a C exit it is accessed by:employee.main->manager.emp_nr
The function should be of type int. It returns a value which the QA Build program interprets as follows:
| Returned Value | Meaning |
|---|---|
| 0 | Successful termination |
| ESCAPE | Exit the calling QA Build procedure |
| SPACE_BAR | Abort the current instance of the procedure (for repeated procedures only) |
int b_read_line()
{
...
if (s != END_OF_FILE)
{
...
return 0;
}
else
{
tf_close(file_nr);
return ESCAPE;
}
}
|
#include "dp4.h"
#include "dex2.h"
/* file_nr is used by both C functions */
int file_nr;char *s;
/* pointers to handle local variables used in QAB program */
boolean * filefound;
char * impfile;
/* Offsets defined from input file */
#define NAME_OFFSET 4
#define DAY_OFFSET 24
#define MONTH_OFFSET 27
#define YEAR_OFFSET 30
#define SEX_OFFSET 35
void * test_Qget_pointer(int number, char * name);
/* Function to open file */
int b_open_file()
{
filefound = (boolean *) test_Qget_pointer(0, "FILEFOUND");
impfile = (char *) test_Qget_pointer(0, "IMPFILE");
if (*filefound = tf_check(impfile))
file_nr = tf_open(impfile, VF_RO);
return 0;
}
int b_read_line()
{
/* Function to read the lines in the file */
employee.main = (struct EMPLOYEE *) test_Qget_pointer(_EMPLOYEE, "MAIN");
if (s = tf_txt_read(file_nr))
{
employee.main emp_nr = atoi(s);
memcpy(employee.main emp_name,s+NAME_OFFSET,20);
employee.main->date_of_birth.day = atoi(s+DAY_OFFSET);
employee.main->date_of_birth.month= atoi(s+MONTH_OFFSET);
employee.main->date_of_birth.year= atoi(s+YEAR_OFFSET);
memcpy(employee.main sex,s+SEX_OFFSET,1);
return 0;
}
else
{
tf_close(file_nr);
return ESCAPE;
}
}
void * test_Qget_pointer(int number, char * name)
{
void * retcode = Qget_pointer(number,name);
if (retcode == 0)
{
map_pause(HORRIBLE_ERROR);
halt();
}
}
Example Source Code (Dedicated PROGRUN)
/* file_nr is used by both C functions */
int file_nr;
/* Offsets corresponding to input file */
#define NAME_OFFSET 4
#define DAY_OFFSET 24
#define MONTH_OFFSET 27
#define YEAR_OFFSET 30
#define SEX_OFFSET 35
int b_open_file()
{
/* Function to open file */
if (*v_filefound = tf_check(v_impfile))
file_nr = tf_open(v_impfile, VF_RO);
return 0;
}
int b_read_line()
{
/* Function to read the lines in the file */
char *s;
if (s = tf_txt_read(file_nr))
{
employee.main->emp_nr = atoi(s);
memcpy(employee.main emp_name,s+NAME_OFFSET,20);
employee.main->date_of_birth.day = atoi(s+DAY_OFFSET);
employee.main->date_of_birth.month = atoi(s+MONTH_OFFSET);
employee.main->date_of_birth.year = atoi(s+YEAR_OFFSET);
memcpy(employee.main->sex,s+SEX_OFFSET,1);
return 0;
}
else
{
tf_close(file_nr);
return ESCAPE;
}
}
|