![[Images/tutorial.gif]](Images/tutorial.gif)
![[bar]](Images/rule.gif)
Here is a small guide to what can go wrong in Khoros programs. I divided the possible errors and solutions (or at least lists of places that could be causing errors) in categories.
Important note: I didn't really try to create all errors that are mentioned here, and I cannot guarantee that the code will really generate an error. The examples in this sections are just basic guidelines on common errors that one can do while programming Khoros routines.
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
... kpds_create_mask(my_obj); ...could bring you problems, specially if the program don't create a mask and later you try to access the mask data. Doing
...
if (!kpds_create_mask(my_obj))
{
kerror("library","routine","cannot create mask in my_obj");
kexit(KEXIT_FAILURE);
}
...
is safer since it checks whether the mask was created or not. Doing
...
if (!kpds_query_mask(my_obj)) /* check if there is a mask */
{ /* if not */
if (!kpds_create_mask(my_obj)) /* try to create it */
{ /* if can't create error and quit */
kerror("library","routine","cannot create mask in my_obj");
kexit(KEXIT_FAILURE);
}
}
...
is even better because it checks for the existence of the mask before attempting to create one.
Note that almost all functions in Khoros has return values that indicate whether the routine was executed correctly or not. Please refer to the routines' kman pages for more information. Check also the next section for more instances of this problem.
![[bar2]](Images/rule2.gif)
This is another place where things can go badly wrong. Suppose you want to get a plane of data and allocate it as:
double *plane; ... plane = (double*)kmalloc(w*h*sizeof(double));but without checking whether plane is really allocated. You will probably get a nasty error when trying to access the data.
There are several variations on this problem that won't be noticed by the compiler, like declaring a pointer to doubles but calling kmalloc with
plane = (double*)kmalloc(w*h*sizeof(int));or simply not allocating the memory and trying to access it. A similar error would be create a routine that will process only data with the same size (say, 128x128x1x1x1) and allocate the memory for this kind of data and later try the same routine with data of different sizes.
![[bar2]](Images/rule2.gif)
kobject in_obj;
in_obj = kpds_open_input_object("filename.dat");
...
but if you don't check whether the file was opened or not you could run into problems. I guess that routines that deal with kobjects can say whether the kobject is valid or not but it would be safer to do something like
kobject in_obj = NULL;
in_obj = kpds_open_input_object("filename.dat");
if (in_obj == NULL)
{
kerror("my lib","my routine", /* of course you should write meaningful stuff here */
"Cannot open input object.");
kexit(KEXIT_FAILURE);
}
![[bar2]](Images/rule2.gif)
kpds_set_attributes(my_obj,KPDS_VALUE_DATA_TYPE,KUINT, KPDS_VALUE_SIZE,120,120,0,0,3);will probably get you a nice segmentation fault sooner or later. The correct call should be
kpds_set_attributes(my_obj,KPDS_VALUE_DATA_TYPE,KUINT, KPDS_VALUE_SIZE,120,120,0,0,3, NULL);All functions with variable attributes must have NULL passed as the last attribute.
![[bar2]](Images/rule2.gif)
I am not sure if Khoros will catch those errors and tell you what went wrong. Anyway if you have an kobject and try to do something like
... kpds_create_value(my_obj); ...without checking if the object already has or not a value segment, you could get runtime errors. Doing
...
if (!kpds_query_value(my_obj)) /* if there isn't a value segment */
{
if (!kpds_create_value(my_obj)) /* try to create it */
{
kerror("library","routine","cannot create value in my_obj");
kexit(KEXIT_FAILURE);
}
}
...
will check if the segment is there and will create only if it is not there, and will also check whether the segment was created or not.
![[bar2]](Images/rule2.gif)
In the code below:
kobject in_obj = NULL; /* input to serve as base */
...
if (clui_info->ibase_flag) /* ibase is optional, check the flag before opening it */
{
if ((in_obj = kpds_open_input_object(clui_info->ibase_file)) == KOBJECT_INVALID)
{
kerror(lib,rtn,"Cannot open input object %s",clui_info->ibase_file);
kexit(KEXIT_FAILURE);
}
}
...
if (!kpds_copy_object_attr(in_obj, out_obj))
{
kerror(lib,rtn,"Unable to copy attributes from input to output object.");
kexit(KEXIT_FAILURE);
}
You will get a segmentation fault, because if the ibase_flag was false, the in_obj was still NULL when you tried to copy it to another object.
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
Is the data in the expected dimensions and/or data type ?
Some routines will work with specific data dimensions or types. Poorly written routines will not give errors or warnings if the expected dimensions or data types are wrong. Specially problematic are routines that has calls like:
int *plane = NULL; ... plane = (int)kmalloc(w*h*sizeof(int)); ... kpds_get_data(my_obj,KPDS_DATA_PLANE,(kaddr)plane); ...This would work if the data is of integer type, but could cause weird errors if the input data is double since the program isn't checking or converting the data prior to accessing it. The program should issue warnings and errors and/or convert the data prior to accessing. See the section Recommendations for code that can be trusted , item 3.
Does the data has more or less segments than the expected ?
Most functions will operate only on the value segment, but some should consider also the mask, location, time and map segments. The program should check whether the required segments are present or not in the input data, and create them or issue warnings and/or errors.
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
![[bar2]](Images/rule2.gif)
If you just created it with craftsman / composer and it does not appear in the glyph list in Cantata, check if you set the "Install in Cantata" option to "Yes" - from craftsman , select a toolbox and the object in the toolbox and click the menu button "Object Operations" and select "Object Attributes", or from composer , select the button "Attributes" and set "Install in Cantata" to "Yes".
Note that if you had Cantata running before you created the object, Cantata will not recognize it, you must quit Cantata and restart it so the object will appear on the glyph list.
![[bar]](Images/rule.gif)
This is a small list of errors that will be really hard to track, even with gdb (see also Using gdb and Khoros ).
![[bar2]](Images/rule2.gif)
In the code segment
int *vector = NULL; int a; ... vector = (int)kmalloc(e*sizeof(int)); ... a = -1; ... vector[a] = 0; ...there is an obvious error - the code is trying to access an element of the vector that does not exist. This error won't be caught by the compiler (nor by gdb). In a code similar to the above, I had a segmentation fault when free'ing the allocated memory - worse, sometimes the error happened and other times it didn't. The only way to avoid the problem was examining the code in detail.
![[bar]](Images/rule.gif)
![[bar]](Images/rule.gif)
![[bar]](Images/rule.gif)
![[bar2]](Images/rule2.gif)
![[bar2]](Images/rule2.gif)
![[bar2]](Images/rule2.gif)