Simple C program use of API for read/write files

Hi,
I’ve been starting to rewrite a bunch of my C code which relied on GMT4.X.X type calls to read headers and grids, and write grids. I wonder if someone could perhaps comment on pitfalls regarding memory leaks, or file I/O issues. I read through the API guide but am confused at times, I’m afraid.

For example, I have a code where the main routine makes and later destroys an API, and in between I call a subroutine that writes a grid like so:

  pad = -1;
  inc[GMT_Y] = dy;inc[GMT_X] = dx;
  par[0] = nlon;par[1] = nlat;
  wesn[0] = xmin;wesn[1] = xmax;wesn[2] = ymin; wesn[3] = ymax;wesn[4]=wesn[5]=0;
 G = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE,GMT_CONTAINER_AND_DATA,
		       par,wesn,inc,GMT_GRID_NODE_REG,pad,NULL);
  if(!G){
    fprintf(stderr,"my_gmt_write_grd: error creating grid for GMT > 4 mode\n");
    exit(-1);
  }
  for(j=0;j < nlon;j++){
    for(i=0;i < nlat;i++){
      ij = gmt_M_ijp (G->header, i, j);
      G->data[ij] = phival[i*nlon+j];
    }
  }
  GMT_Write_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, 
  GMT_CONTAINER_AND_DATA, NULL, grdfilename, G);
  GMT_Destroy_Data(API,G->data);

will this do an OK job in making sure the file pipeline is handled and take care of getting rid off (most) of the allocated stuff? Would this be OK to call, say, 100 times and still have no memory leaks?

I noticed (after) that the F77 wrappers do something like what I want, such as

  if (name == NULL) {
		GMT_Report (API, GMT_MSG_ERROR, "No filename given to GMT_F77_writegrd\n");
		return GMT_ARG_IS_NULL;
	}
	if ((API = GMT_Create_Session (argv, 0U, 0U, NULL)) == NULL) return GMT_MEMORY_ERROR;
	file = strndup (name, lname);

	gmt_M_memset (&header, 1, struct GMT_GRID_HEADER);	/* To convince Coverity that header->index_function has been initialized */
	gmt_grd_init (API->GMT, &header, NULL, false);
	if (full_region (limit)) {	/* Here that means limit was not properly given */
		GMT_Report (API, GMT_MSG_ERROR, "Grid domain not specified for %s\n", file);
		gmt_M_str_free (file);
		GMT_Destroy_Session (API);
		return GMT_ARG_IS_NULL;
	}
	if (inc[GMT_X] == 0.0 || inc[GMT_Y] == 0.0) {	/* Here that means grid spacing was not properly given */
		GMT_Report (API, GMT_MSG_ERROR, "Grid spacing not specified for %s\n", file);
		gmt_M_str_free (file);
		GMT_Destroy_Session (API);
		return GMT_ARG_IS_NULL;
	}

	/* Set header parameters */

	gmt_M_memcpy (header.wesn, limit, 4U, double);
	gmt_M_memcpy (header.inc, inc, 2U, double);
	header.n_columns = dim[GMT_X];	header.n_rows = dim[GMT_Y];
	header.registration = dim[GMT_Z];
	gmt_set_grddim (API->GMT, &header);
	if (title) F_STRNCPY (header.title, title, GMT_GRID_TITLE_LEN80, ltitle);
	if (remark) F_STRNCPY (header.remark, remark, GMT_GRID_REMARK_LEN160, lremark);

	if (dim[3] == 1) gmtlib_inplace_transpose (array, header.n_rows, header.n_columns);

	/* Write the file */

	if (gmtlib_write_grd (API->GMT, file, &header, array, no_wesn, GMT_no_pad, 0)) {
		GMT_Report (API, GMT_MSG_ERROR, "Failure while writing file %s\n", file);
		gmt_M_str_free (file);
		GMT_Destroy_Session (API);
		return GMT_GRID_WRITE_ERROR;
	}
	gmt_M_str_free (file);

	if (GMT_Destroy_Session (API) != GMT_NOERROR) return GMT_MEMORY_ERROR;
	return GMT_NOERROR;

This obviously does a better job in terms of checking for all kinds of errors, but is there something in terms of handling of memory or I/O that would be crucial to emulate?

Thanks for any comments!

Thorsten

Looks fine. Any memory allocated by GMT_Create_Data and GMT_Read_Data will automatically be freed by GMT_Destroy_Session (we built a C garbage collector in GMT). If you suspect any memory leaks from GMT you can compile GMT with -DDEBUG -DMEM_DEBUG and you will get nasty messages if there are things not freed that was allocated.

I would advise to never use exit(). If you create GMT modules that use the GMT IO your programs automatically qualify to be used from Matlab and Julia wrappers (with no further changes). But then, any exit means killing the Matlab or Julia calling environment.
Just use return with some error code.

Thank you!

Thanks! In this case, killing the calling code is what I wanted, but I do take your point!