1
votes

I am trying to transfer a string from a C routine to an Ada application. Instead of reading from a NI device I hard coded a string in the C file routine. The string does transfer across but there are other non-characters with it. The string is HELLO.

BUFFER = UH~I?H~C?^P?E?HELL?E?O?

The C routine is:


#include "gpib_intf.h"

#define MAX_SIZE 350

static ViStatus status, StatusSession;
static ViSession inst, defaultRM;
static char stringinput[256];
static ViUInt32 rcount, retCount;
static ViUInt32 BytesToWrite;
static unsigned char buffer[MAX_SIZE];

void setupDefaultRM()
{
   status = viOpenDefaultRM (&defaultRM);
   if (status < VI_SUCCESS)
      {
         printf("Could not open a session to the VISA Resource Manager!\n");
         exit (EXIT_FAILURE);
      }

   status = viOpen (defaultRM, "GPIB0::1::INSTR", VI_NULL, VI_NULL, &inst);
   if (status < VI_SUCCESS)
      {
         printf("Could not open a session to the GPIB Device!\n");
         exit (EXIT_FAILURE);
      }
}

void writeVI(char* stringinput)
{
   BytesToWrite = (ViUInt32)strlen(stringinput);
   status = viWrite (inst, (ViBuf)stringinput, BytesToWrite, &rcount);
}

void readVI()
{
   /*memset(buffer, 0, sizeof(buffer));*/
   const unsigned char version[5] = "HELLO";
   printf("\n");
   printf("version = %s\n", version);

   /*
   status = viRead (inst, buffer, MAX_SIZE, &retCount);
   if (status < VI_SUCCESS)
   {
      printf ("Error reading a response from the device.\n");
      }
   */
   /*return version;*/
}

The Ada code reading it is:

   -------------------------------------------------------------------------
   -- READVI
   --
   -- read from GPIB device
   --
   function READGPIB return STRING is
      
      function READVI return STRING is
      version : char_array(size_t);
      pragma Import (C, version, "readVI");
         
      begin
         --return "     0";
         return To_Ada(version, Trim_Nul => True);
      end;

   begin
      --Text_IO.Put_Line(READVI);
      return READVI;
   end READGPIB;
2
The function readVI defined in the C source code does not return a string, but the function READVI declared in the Ada code does return a string. the return statement in the C code is commented out. Furthermore, it does not appear that your Ada code uses the packages Interfaces.C and Interfaces.C.Strings. - Jim Rogers
Your Ada imports the C function readVI as a char_array. No wonder you get garbage. - Jeffrey R. Carter

2 Answers

4
votes

You’ve posted a version of the C code that looks as if it was from an early development stage. If that’s what you’re actually using it’s a wonder that you saw anything with HELLO in it at all, if for no other reason than that on the C side readVI is a void function, which corresponds to an Ada procedure, whereas the Ada side thinks it’s a char_array (size_t), whatever that means (size_t is a type, not a number).

On the C size, you want

char *readVI();

and on the Ada side the corresponding declaration is

function Read_VI return Interfaces.C.Strings.chars_ptr
with
  Import,
  Convention => C,
  External_Name => "readVI";

Putting it together, the C side might be

#include <string.h>
static char buffer[128];

char *readVI()
{
  strcpy(buffer, "Hello!");
  return buffer;
}

and the Ada side

with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C.Strings;

procedure Joseph is
   function Read_VI return Interfaces.C.Strings.chars_ptr
   with
     Import,
     Convention => C,
     External_Name => "readVI";
begin
   Put_Line ("received '"
               & Interfaces.C.Strings.Value (Read_VI)
               & "'");
end Joseph;
0
votes

I found my problem based upon Simon's recommendation.

C code

/*
 *  Read from a GPIB device
*/
unsigned char* readVI()
{
   status = viRead (inst, buffer, MAX_SIZE, &retCount);
   /* If successful read */
   if (status == VI_SUCCESS)
   {
      /* copy data into return buffer */
      strcpy(buff, buffer);
   }
   return buff;
}

Ada Code

       -------------------------------------------------------------------------
   -- READVI
   --
   -- read from GPIB device
   --
   procedure READGPIB is
      
      function READVI return Interfaces.C.Strings.chars_ptr
        with
          Import,
          Convention => C,
          External_Name =>"readVI";
      
   begin
     declare
        Str : STRING := Interfaces.C.Strings.Value (READVI);
     begin
        for I in MSG3235_TYPE'Range loop
           GLOBALS.BUFFER(I) := Str(I);
           exit when Str (I) = ASCII.CR or Str (I) = ASCII.LF or Str (I) = ASCII.SEMICOLON;
         end loop;
     end;
  end READGPIB;