Menu

interfacing COBOL and C (return/receive custom struct *)

2024-09-24
2024-10-13
  • Simon Sobisch

    Simon Sobisch - 2024-09-24

    Wild Eest wrote on 2024/09/23:

    Howdy, everyone.

    I'm calling C-function from COBOL code, and expecting it to return a custom structure:

    • C-function signature:
    struct mystruct* cmalloc(LPCSTR lpszItemAmount);
    
    • C-struct:
    struct mystruct {
        unsigned short itemSize;
         int*     ptr;
    } ;
    
    • COBOL-side:
            linkage                             section.
            01  out-struct-grp.
                03  item-size-in-bytes   binary-short unsigned value 0.
                03  c-out-arr-grp.
                    05  c-out        binary-int occurs 5 .
           *------------------------------------^---------------------------*
            procedure                           division .
                call "cmalloc" using  by content item-amnt
                               returning        out-struct-ptr .
                set address of out-struct-grp  to  out-struct-ptr .
    

    internally C-function does a malloc call:
    myStructInstancePtr->ptr = (int*)malloc(itemAmount * sizeof(int));

    The content of "c-out-arr-grp" does not arrive correctly, event hough the item count is aligned between C and COBOL routines.
    Any tips would be great.

    P.S. I have already went through "GnuCOBOL interfacing COBOL and C" pdf.
    P.P.S. I have also tried
    struct __attribute__ ((__packed__)) mystruct
    but it didn't help.

    [resent as this post was strangely removed]

    @univac Can you please give a bit more information on the system (GCC
    based, GNU/Linux?) and GnuCOBOL version, as well as ass the compile
    command(s) used?

    Have you tried CALL STATIC "cmalloc" as well?

     
    👍
    1

    Last edit: Simon Sobisch 2024-09-24
    • Wild Eest

      Wild Eest - 2024-09-24

      Hey, Simon. Thanks for restoring the posts!

      Compiler version (used under Microsoft Windows [Version 10.0.17763.6189]) :

      Setting environment for GnuCOBOL 2.0 RC-2 with MinGW binaries
      (GCC 5.3.0, PDcurses 3.4, GMP 6.1.1, BDB 6.2.23.NC)

      cobc (GnuCOBOL) 2.0.0
      Copyright (C) 2016 Free Software Foundation, Inc.
      License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
      This is free software; see the source for copying conditions. There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      Written by Keisuke Nishida, Roger While, Ron Norman, Simon Sobisch, Edward Hart
      Built Nov 13 2016 01:30:50
      Packaged Nov 06 2016 22:36:19 UTC
      C version "5.3.0"

      Compile commands:
      1. set_env.cmd
      2. cobc -x -o hello hello.cob msgbox.c

      No, I haven't tried CALL STATIC yet.

      It seems that when I'm trying to dereference C-structure in my COBOL code, I get 8 bytes total, while unsigned short takes 4 bytes and pointer to int array is also 4 bytes.
      Now, I'm able to see the right value of the primitive unsigned short, but dereferencing structure member int * ptr always fails for me. Either I'm getting "memory not allocated" when trying to access the int array, after set address of cobol-arr to c-int-ptr, or im getting "garbage" from the memory, when dereferencing it like this: set address of cobol-arr to address of c-int-ptr (i know it's the wrong way to dereference).

       

      Last edit: Wild Eest 2024-09-24
    • Mickey White

      Mickey White - 2024-09-24

      "[resent as this post was strangely removed]"
      sorry mixed it up with some spam... I will be more careful

       
      👍
      1
  • László Erdős

    László Erdős - 2024-09-25

    Hi,
    I think, it would be better to give back the result value not in "returning". The RETURNING clause allows you to specify a numeric data item into which the subroutine should return a numeric value. It is same as with the RETURN-CODE special register. The RETURN-CODE defined as BINARY-LONG SIGNED. These values in RETURN-CODE are by convention used to signify success (usually with a value of 0) or failure (usually with a non-zero value) of the process the program was attempting to perform.

    You can write a C wrapper function for the cmalloc, and give back the pointer in the argument, not in Returning.

    László

     
    👍
    1
  • Wild Eest

    Wild Eest - 2024-10-06

    Hi, sorry for late reply - I was ill.

    I've tried returning structure via the input ('by reference' input) and I still get something weird at the end, example:

    /*
          *------------------------------------^---------------------------*
           identification                      division .
           program-id. aaaMAIN.
           data                                division .
          *------------------------------------^---------------------------*
           working-storage                     section .
           01  item-amnt           binary-int value 5.
           01  i                               pic 9(3) value 0.
           01  c-out-struct-grp   .
               03  c-begin-str        pic x(13) value spaces.
               03  c-item-size-in-bytes     binary-short value 0.
               03  c-out-arr-grp  .
                   05  arr-item      binary-int occurs 5  .
               03  c-end-str          pic x(13) value spaces.
          *------------------------------------^---------------------------*
           procedure                           division .
    
               call "cstructdemo" using  by value  item-amnt
                                       by reference c-out-struct-grp .
    
               display   "01: " c-begin-str.
               display   "02: " c-item-size-in-bytes .
               display   "03: " c-out-arr-grp .
               display   "04: " c-end-str.
               perform varying i from 1 by 1   until i > 5
                   display "  05: " arr-item(i)
               end-perform .
    
               goback .
               end program aaaMAIN.
          *------------------------------------^---------------------------*
    */
    // ------------------ aaaSUB.c ---------------------------
    #include <windows.h>
    
    struct mystruct3 {
        char    beginStr[13];
        short   itemSize;
        int     intArr[5];
        char    endStr[13];
    } ;
    
    void cstructdemo(int itemAmount, struct mystruct3* myStructPtr) {
    
        myStructPtr->itemSize = sizeof(int) ;
        //----------------------
        int iArr[] = {101, 202, -303, 404, 505} ;
        memcpy(myStructPtr->intArr, iArr, sizeof(myStructPtr->intArr));
        //----------------------
        char cArrBeginStr[] = "*** begin ***";
        strncpy(myStructPtr->beginStr, cArrBeginStr, 13);
        //----------------------
        char cArrEndStr[] = "**** end ****";
        strncpy(myStructPtr->endStr, cArrEndStr, 13);
    
    }
    
    /*------------- OUTPUT : --------------------- 
    
    01: *** begin ***
    02: +01024
    03:  e      ╤■  Ф☺  ∙☺
    04:  **** end ***
      05: +0000025856
      05: +0000051712
      05: -0000077568
      05: +0000103679
      05: +0000129280
    
    ------------------------------------------------*/
    
     
    • Chuck Haatvedt

      Chuck Haatvedt - 2024-10-06

      Wild Eest,

      this was a bit of a tricky one to find, the issue is NOT big_endian / little_endian but rather the structure padding in C

      What is Structure Padding in C?

      Structure padding in C is the process that is handled by the CPU architecture. Structure Padding adds a certain number of empty bytes within a structure so that the data members are naturally aligned in memory. The alignment requirements are determined by the processor architecture rather than the language itself. Naturally, the alignment requirements change as per the data bus size or other architectural considerations of a certain CPU architecture.

      see this link...

      https://www.tutorialspoint.com/cprogramming/c_structure_padding_and_packing.htm

      ====> Note the attribute below

      #include <windows.h>                                                                               
      struct __attribute__((packed)) mystruct3 {
          char    beginstr[13];
          short   itemsize;
          int     intarr[5];
          char    endstr[13];
      } ;                                                                                                                 
      void cstructdemo(int itemamount, struct mystruct3* mystructptr) {
          mystructptr->itemsize = sizeof(int) ;
          //----------------------
          int iarr[] = {101, 202, -303, 404, 505} ;
          memcpy(mystructptr->intarr, iarr, sizeof(mystructptr->intarr));
          //----------------------
          char carrbeginstr[] = "*** begin ***";
          strncpy(mystructptr->beginstr, carrbeginstr, 13);
          //----------------------
          char carrendstr[] = "**** end ****";
          strncpy(mystructptr->endstr, carrendstr, 13);
          return;
      }
      
      F:\AA-minGW32-static>aaamain
      01: *** begin ***
      02: +00004
      04: **** end ****
      
        05: item 001 ==> +0000000101
      
        05: item 002 ==> +0000000202
      
        05: item 003 ==> -0000000303
      
        05: item 004 ==> +0000000404
      
        05: item 005 ==> +0000000505
      
           Chuck Haatvedt
      
       
      👍
      1
  • Anonymous

    Anonymous - 2024-10-06

    +01024 would indicate a big / little endian problem.
    use pic 9 (xx) comp-5 instead of "binary"

     
  • Ralph Linkletter

    I had to read the programmers guide to understand this bastardization of COBOL.
    I was wrong !

    I think that binary-int is perhaps big endian
    Try PIC S9(08) comp-5 for binary-int
    For "short" PIC S9(04) comp-5

    So these are GNU extensions ?
    I would presume they are little endian on X86 hardware.

    COBOL C
    BINARY-CHAR UNSIGNED unsigned char
    BINARY-CHAR [ SIGNED ] signed char
    BINARY-SHORT UNSIGNED unsigned short
    BINARY-SHORT [ SIGNED ] short
    BINARY-LONG UNSIGNED unsigned long
    BINARY-LONG [ SIGNED ] long
    BINARY-INT int
    BINARY-C-LONG [ SIGNED ] long
    BINARY-DOUBLE UNSIGNED unsigned long long
    BINARY-DOUBLE [ SIGNED ] long long
    BINARY-LONG-LONG long lon

     

    Last edit: Ralph Linkletter 2024-10-07
  • László Erdős

    László Erdős - 2024-10-06

    Hi,

    Try to add "SYNC" before "binary-short".
    (See in GnuCOBOL Programmer’s Guide: SYNCRONIZED)

    And write out the size in COBOL and also in C:

           display "size of c-out-struct-grp: " 
                   function length(c-out-struct-grp)
    
    printf("size of mystruct3: %d\n", sizeof(struct mystruct3));
    

    I have this result:

    size of c-out-struct-grp: 49
    size of mystruct3: 52
    01: *** begin ***
    02: +00004
    03: e ╩ Ð■  ö☺ ¨☺
    04: * end *
    05: +0000000101
    05: +0000000202
    05: -0000000303
    05: +0000000404
    05: +0000000505

    Maybe can somebody explain the size differences: 49 <-> 52.

    László

     
    🎉
    1

    Last edit: László Erdős 2024-10-07
  • Wild Eest

    Wild Eest - 2024-10-07

    Thank you everyone!
    The only thing that helped me was SYNC by @laszloerdos
    (attribute packed and changing BINARY types to COBOL equivalents - didn't).
    I wish it was easier to figure out, but I'm also happy that it worked eventually :)

    Code snippet below:

    /*-----------------------------------------------------            
    
          *------------------------------------^---------------------------*
           identification                      division .
           program-id. aaaMAIN.
           data                                division .
          *------------------------------------^---------------------------*
           working-storage                     section .
           01  item-amnt           binary-int value 5.
           01  i                               pic 9(3) value 0.
           01  c-out-struct-grp   .
               03  c-begin-str        pic x(13) value spaces.
          *    03  c-item-size-in-bytes     binary-short  value 0.
               03  c-item-size-in-bytes     PIC S9(04) comp-5 SYNC value 0.
               03  c-out-arr-grp  .
          *        05  arr-item      binary-int occurs 5  .
                   05  arr-item      PIC S9(08) comp-5   occurs  5.
               03  c-end-str          pic x(13) value spaces.
          *------------------------------------^---------------------------*
           procedure                           division .
    
               call "cstructdemo" using  by value  item-amnt
                                       by reference c-out-struct-grp .
    
               display   "01: " c-begin-str.
               display   "02: " c-item-size-in-bytes .
               display   "03: " c-out-arr-grp .
               display   "04: " c-end-str.
               perform varying i from 1 by 1   until i > 5
                   display "  05: " arr-item(i)
               end-perform .
    
               display "06: size of c-out-struct-grp: " 
                    function length(c-out-struct-grp) .
    
               goback .
               end program aaaMAIN.
          *------------------------------------^---------------------------*
    ---------------------------------------------------*/
    // ------------------ aaaSUB.c ---------------------------
    #include <windows.h>
    #include <stdio.h>
    
    struct __attribute__((packed)) mystruct3 {
        char    beginStr[13];
        short   itemSize;
        int     intArr[5];
        char    endStr[13];
    } ;
    
    void cstructdemo(int itemAmount, struct mystruct3* myStructPtr) {
    
        myStructPtr->itemSize = sizeof(int) ;
        //----------------------
        int iArr[] = {101, 202, -303, 404, 505} ;
        memcpy(myStructPtr->intArr, iArr, sizeof(myStructPtr->intArr));
        //----------------------
        char cArrBeginStr[] = "*** begin ***";
        strncpy(myStructPtr->beginStr, cArrBeginStr, 13);
        //----------------------
        char cArrEndStr[] = "**** end ****";
        strncpy(myStructPtr->endStr, cArrEndStr, 13);
        //----------------------
        printf("07: size of mystruct3: %d\n", sizeof(struct mystruct3));
        //----------------------
        return; 
    }
    
    /*----------------- OUTPUT ----------------------
    
    07: size of mystruct3: 49
    01: *** begin ***
    02: +0004
    03: e      ╤■  ö  ∙
    04: **** end ****
      05: +00000101
      05: +00000202
      05: -00000303
      05: +00000404
      05: +00000505
    06: size of c-out-struct-grp: 000000049
    
    -------------------------------------------------*/
    
     
  • Chuck Haatvedt

    Chuck Haatvedt - 2024-10-07

    the only change needed was the attribute((packed)) change in the aaasub.c program

     
    • Wild Eest

      Wild Eest - 2024-10-08

      Unfortunally for me - it wasn't enough. I have now went back to attribute((packed)) in C and BINARY-INT, BINARY-SHORT without SYNC in GnuCOBOL, and I still have the issue this way, including (due to?) difference in structure size (COBOL 48 bytes vs C 49 bytes) 🤷‍♂️

       
  • Ralph Linkletter

    The URL that Chuck included previously answers your "due to ? ) - does it not.
    https://www.tutorialspoint.com/cprogramming/c_structure_padding_and_packing.htm

    I wonder if your COBOL structure passed a redefines of the binary fields as character.
    That is:
    77 item-amnt binary-int.
    77 item-amnt-char redefines item-amnt pic x(04).
    move 5 to item-amnt
    COBOL would be indifferent to the type cast
    Would "C" insist on padding a char field?
    Is it that "C" would pad any data field of an odd length - even char ?

    The value contained in a four byte field is universal - irrespective of the description.

     
    👍
    1
  • Wild Eest

    Wild Eest - 2024-10-10

    I have finally managed to understand how 48 bytes structure transforms into 52 bytes (this is well aligned with x86_64 ABI documentation, specifically - c structure padding):

    struct mystruct3 {
        char    beginStr[13];
        short   itemSize;
        int     intArr[5];
        char    endStr[13];
    } ;
    ----------------------------------------------------
    sum = 0
    ----------------------------------------------------
    current: type char,      align 1, amount 13, size 13
    sum = sum + current = 13 (aligned)
    ----------------------------------------------------
    current: type short,     align 2, amount 1,  size 2   
    sum = sum + current = 15 (NOT aligned), 15->16 (aligned)
    ----------------------------------------------------
    current: type integer,   align 4, amount 5,  size 20
    sum = sum + current = 36 (aligned)
    ----------------------------------------------------
    current: type char,      align 1, amount 13, size 13
    sum = sum + current = 49 (aligned)
    ---------------------------------------------------
    total:   type mystruct3, align 4, amount 1, size 49 (NOT aligned)
    sum = 49->52 (aligned)
    ---------------------------------------------------
    

    So, what I've learned is:
    1. COBOL has nothing to do with this behavior
    2. the arch of the machine is important (Captain-Obvious moment)
    3. order of members inside c structure is important
    4. from COBOL perspective the "issue" can be easily solved by FILLERs (tested), but it will make the source code non-portable
    5. right way to share structure between COBOL and C is: __attribute__((packed)) + SYNC

     
    • Chuck Haatvedt

      Chuck Haatvedt - 2024-10-10

      Hello,

      from the testing which I did, the COBOL source code did not require any changes and the C code only needed to add attribute((packed)).

      I did not use SYNC anywhere in my test code.

               Chuck Haatvedt
      
       
      👍
      1
      • J McNamara

        J McNamara - 2024-10-11

        Chuck-

        I saw what you did with the code and I used AI to help understand it. Thank you for the cool tutorial on how to pack so that the code is portable across architectures.

        Have cool day,
        jim

        Sent with Proton Mail secure email.

        On Thursday, October 10th, 2024 at 7:27 PM, Chuck H. chaat@users.sourceforge.net wrote:

        Hello,

        from the testing which I did, the COBOL source code did not require any changes and the C code only needed to add attribute((packed)).

        I did not use SYNC anywhere in my test code.

        Chuck Haatvedt


        interfacing COBOL and C (return/receive custom struct *)


        Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/gnucobol/discussion/help/

        To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/

         
      • Wild Eest

        Wild Eest - 2024-10-13

        Hello, Chuck.
        You're only 50% right according to my testing :))
        It seems that older version of GnuCOBOL doesn't handle packed C-structure correctly.
        This problematic version is distributed with OpenCobolIDE 4.7.6 - not able to sync without SYNC keyword:

        cobc (GnuCOBOL) 2.0.0
        Copyright (C) 2016 Free Software Foundation, Inc.
        Built     Nov 13 2016 01:30:50
        C version "5.3.0"
        

        On the other hand this version (below) does not require SYNC keyword (like you've said earlier - only needs 'attribute packed' in the C-code):

        GnuCOBOL 3.2.0 
        Built Jul 28 2023 22:59:53
        C version (MinGW) "9.2.0"
        

        Thank you for your feedback.

         
        • J McNamara

          J McNamara - 2024-10-13

          Sent with Proton Mail secure email.

          Hi Wild Eest-

          thanks to you and Chuck, now you are now both right.

          It is interesting anomaly you discovered and must have been frustrating.

          Now you are both 100 % right which I rarely am.

          This is the coolest time to program now with the advent of AI.

          I am having a blast and benefiting from people like you and Chuck and the AI and I appreciate
          these cool tips.

          I wondered whether the AI would know about this. Probably not. It is expert advice.

          I'm riding on everybody's coattails at the moment.

          THANKS again,
          jim

          Sent with Proton Mail secure email.

          ------- Forwarded Message -------
          From: Wild Eest univac@users.sourceforge.net
          Date: On Sunday, October 13th, 2024 at 2:33 AM
          Subject: [gnucobol:discussion] Re: interfacing COBOL and C (return/receive custom struct *)
          To: [gnucobol:discussion] help@discussion.gnucobol.p.re.sourceforge.net

          Hello, Chuck.
          You're only 50% right according to my testing :))
          It seems that older version of GnuCOBOL doesn't handle packed C-structure correctly.
          This problematic version is distributed with OpenCobolIDE 4.7.6 - not able to sync without SYNC keyword:

          cobc (GnuCOBOL) 2.0.0
          Copyright (C) 2016 Free Software Foundation, Inc.
          Built Nov 13 2016 01:30:50
          C version "5.3.0"

          On the other hand this version (below) does not require SYNC keyword (like you've said earlier - only needs 'attribute packed' in the C-code):

          GnuCOBOL 3.2.0
          Built Jul 28 2023 22:59:53
          C version (MinGW) "9.2.0"

          Thank you for your feedback.


          interfacing COBOL and C (return/receive custom struct *)


          Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/gnucobol/discussion/help/

          To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/

           

Anonymous
Anonymous

Add attachments
Cancel





Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.