Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a cdecl function call from C to Pascal that uses callback with variable argument lists

I am converting a C header file for a video encoder DLL to Delphi Pascal.

I've run into some issues with access violations with the following function and need help resolving this:

h264venc_tt * MC_EXPORT_API h264OutVideoNew(
  void * (MC_EXPORT_API * get_rc)(const char* name),
  const struct h264_v_settings * set,
  int32_t options,
  int32_t CPU,
  int32_t frame0,
  int32_t nframes);

Note: MC_EXPORT_API = cdecl

The get_rc is declared as follows:

// resource functions dispatcher
void * MC_EXPORT_API get_rc(const char* name)
{
  if (!strcmp(name, "err_printf"))
    return (void*) error_printf;
  else if (!strcmp(name, "prg_printf"))
    return (void*) progress_printf;
  else if (!strcmp(name, "wrn_printf"))
    return (void*) warn_printf;
  else if (!strcmp(name, "inf_printf"))
    return (void*) info_printf;
  return NULL;
}

This function returns a pointer to another function that has a Variable Argument list. One of these is declared as this:

void error_printf(const char * fmt, ...)
{
  char lst[256];
  va_list marker;
  va_start(marker,fmt);
  vsprintf(lst,fmt,marker);
  va_end(marker);
  printf("%s\n", lst);
}

I've translated this the function call and the get_rc to this Delphi Pascal code:

PErrorMessageHandler = function (const Name: String): Pointer; cdecl varargs;

function h264OutVideoNew(
  get_rc: PErrorMessageHandler;
  settings: Ph264_v_settings;
  options: int32;
  CPU: int32;
  frame0: int32;
  nFrames: int32
): Pointer; cdecl; external 'mc_enc_avc.dll' index 4;

Unfortunately, I have no idea how to implement the C-style method error_printf shown above. Can anyone help to point me in the right direction? I am also curious if I have implemented the other functions correctly as I am getting access violations when I try to call the h264OutVideoNew() function.

PS! I've not included the packed record Th264_v_settings/P_h264_v_settings in this post as this is very long and not really the problem.

like image 635
TomRay74 Avatar asked Feb 24 '13 10:02

TomRay74


1 Answers

A C parameter of type char* is a pointer to a null-terminated array of 8 bit characters. In Delphi the equivalent type is PAnsiChar. You cannot use string since that is a managed Delphi type that has no equivalent in C.

In addition, the error function prototype has void return value. You are returning a pointer and that's an error.

The bigger problem that you have is that you cannot readily implement a C style function that receives a variable number of arguments in Delphi. You can declare and call such a function, but you cannot implement one. This means that such a function, with variable arguments, has to be an external function.

Now, you could write your own assembler routine to full off the variable arguments. However, that's not the route I would take. I would write the function in C and then compile it to a .obj file that can be linked into your Delphi program with $LINK.

If you don't actually need to read off the variable arguments, you can ignore them like this:

TErrorMessageHandler = procedure(Name: PAnsiChar); cdecl;

Note that I have made the following changes:

  1. Change the type name to be prefixed with T which is standard.
  2. Corrected the type of the Name parameter.
  3. Changed from function to procedure to match the C declaration.
  4. Removed the varargs which we cannot implement in Delphi and thus ignore the additional parameters.

Then your imported function would look like this:

function h264OutVideoNew(
  get_rc: TErrorMessageHandler;
  settings: Ph264_v_settings;
  options: int32;
  CPU: int32;
  frame0: int32;
  nFrames: int32
): Pointer; cdecl; external 'mc_enc_avc.dll' index 4;

And then you can implement the error callback function like this:

procedure error_printf(Name: PAnsiChar); cdecl;
begin
  // do stuff here
end;
like image 56
David Heffernan Avatar answered Sep 29 '22 06:09

David Heffernan