ryyker
ryyker

Reputation: 23218

gSoap generated client-side structure initialization and use

gSoap generated client-side structure initialization and use (using ANSI C bindings)

After reading through gSoap examples and documentation I was not able to find anything directly answering this issue. I have since sorted it out. This post/answer pair lays out the problem and my solution.

Problem description:
I am using gSoap generated client source code to build ANSI C bindings to access web services. Arguments 4 & 5 of the "soap_call__" functions provided as application interfaces (defined in soapClient.c) are often generated as complex (nested) structures. Because struct ns3__send (4th argument) is the input structure, it must be declared, initialized, allocated and freed within the calling application.

for example, given the following gSoap generated prototype:

SOAP_FMAC5 int SOAP_FMAC6 soap_call___ns1__SendFile((struct soap *soap, const char *soap_endpoint, const char *soap_action, struct ns3__send *mtdf, struct recv *response)

with the following structure definition (looking only at argument 4) defined in soapStub.h

NOTE: I have shortened the names and reduced the number of members from original contents of the structures to simplify.

struct ns3__send
{
    char *wsStDate; /* optional element of type xsd:date */
    int *wsStDuration;  /* optional element of type xsd:int */
    int *wsStFailures;  /* optional element of type xsd:int */
    char *wsStFileName; /* optional element of type xsd:string */   
        struct ns3__Param *details; /* optional element of type ns3:Param */
};

struct ns3__Param
{
    int __sizeRow;  /* sequence of elements <wsStdDetailsRow> */
    struct ns3__Row *row;   /* optional element of type ns3:xxmtdfws_wsStdDetailsRow */
};

struct ns3__Row
{
    int *wsStdSeq;  /* optional element of type xsd:int */
    char *wsStdStep;    /* optional element of type xsd:string */
    char *wsStdTestDesc;    /* optional element of type xsd:string */
    char *wsStdLowLim;  /* optional element of type xsd:string */
};

Question:
How are the members and pointers within this complex (nested) input structure properly initialized, memory allocated, values assigned and memory freed such that they are useable within a calling application?

Upvotes: 1

Views: 3177

Answers (2)

ryyker
ryyker

Reputation: 23218

Note: This was originally posted to address structures generated by gSoap utilities specifically, but it has general applicability to any nested struct with pointers...

The following describes an ANSI C method for initializing, allocating, assigning and freeing members and pointers within a nested structure construct where the number of fields in the nested section is unknown until run-time.

To explain the shape of structs requiring this treatment, The data schema is comprised of a known number of header fields, each field having one value per row. The last field (row) serves as a delimiter ******** between the header and data sections. The data section contains an unknown number of data records, but each record contains a known (and constant) number of comma delimited fields:

Example data:
enter image description here

The two files shown below are fully commented. Together, they will compile and build with any ANSI C compiler:

InitComplexStructs.h:

    //The struct names typical in gSoap generated code
    //are longer and more complicated.  

    //for example, a typical client soap_call___ns...()
    //function prototype may look like this:
    //SOAP_FMAC5 int SOAP_FMAC6 soap_call___ns1__SendLEDF(struct soap *soap, const char *soap_endpoint, const char *soap_action, struct _ns3__ledf_send *ns3__xxmtsvclws, struct _ns3__ledf_recv *ns3__xxmtsvclwsResponse)
    //where areguments 4 & 5 are respectively:
    // arg 4: struct _ns3__ledf_send *ns3__xxmtsvclws
    // arg 5: struct _ns3__ledf_recv *ns3__xxmtsvclwsResponse
    //
    //for this project we will assume arg 4 represents a complex (nested)
    //set of data structures, and for illustration purposes, shorten the
    //name to aaa:

    // struct aaa contains members to accomodate a fixed number of strings
    // as well as a pointer to struct bbb
    struct aaa  {
        char *aaaStr1;
        char *aaaStr2;
        char *aaaStr3;
        char *aaaStr4;
        char *aaaStr5;
        struct bbb *pBbb;
    };

    // struct bbb is used to set array order size
    // (or the number of copies necessary of struct ccc)
    // it contains the array size (index value) member "numRows"
    // and a pointer to a struct, which will work like a pointer
    // to array of struct ccc
    struct bbb  {   
        int numRows;
        struct ccc *row;
    };

    // struct ccc contains members to accomodate a variable number of 
    // sets of strings, number of sets determined by the array row[] 
    // initialized to array size "numRows" in struct bbb
    // (see initComplexStructs.c for how this is done)
    struct ccc  {
        char *cccStr1;
        char *cccStr2;
        char *cccStr3;
        char *cccStr4;
        char *cccStr5;
    };

InitComplexStructs.c

    ///////////////////////////////////////////////////////////
    ///// Using nested data structures ////////////////////////
    ///////////////////////////////////////////////////////////
    //
    //  client-side gSoap generated code will often use nested  
    //  data structures to accomodate complex data types 
    //  used in the 4th and 5th arguments of the client
    //  soap_call__ns...() functions.  
    //
    //  This program illustrates how to work with these
    //  structures by a calling application in the 
    //  following way :
    //
    //    - Initialization of structs
    //    - Allocation of structs and members
    //    - Assignment of values to members
    //    - Freeing of allocated memory
    //
    ///////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////

    #include <ansi_c.h>
    #include "InitComplexStructs.h"

    struct aaa _aaa, *p_aaa;
    struct bbb _bbb, *p_bbb;
    struct ccc _row, *p_row;

    void InitializeStructs(void);
    void AllocateStructs(void);
    void AssignStructs(void);
    void FreeStructs(void);

    char typicalStr[]={"aaaStr 1"};
    size_t sizeStr = sizeof(typicalStr);

    void main (void)
    {
        InitializeStructs();
        AllocateStructs();
        AssignStructs();
        FreeStructs();
    }

    void InitializeStructs(void)
    {
        p_aaa = &_aaa;  
        p_bbb = &_bbb;  
        p_row = &_row;
    }
    void AllocateStructs(void)
    {
        int i;
        //allocate members of p_aaa 
        p_aaa->aaaStr1 = calloc(sizeStr, 1);
        p_aaa->aaaStr2 = calloc(sizeStr, 1);
        p_aaa->aaaStr3 = calloc(sizeStr, 1);
        p_aaa->aaaStr4 = calloc(sizeStr, 1);
        p_aaa->aaaStr5 = calloc(sizeStr, 1);
        p_aaa->pBbb    = malloc(    sizeof(*p_bbb));

        //Allocate member of next nested struct - pBbb
        //Note:  The order of array is determined
        //by the value assigned to "numRows"  
        //Note also: the value for numRows could be passed in by argument  
        //since the calling function has this information.
        //Just requires prototype mod from void to int argument.
        p_aaa->pBbb->numRows = 3;
        p_aaa->pBbb->row = calloc(p_aaa->pBbb->numRows,sizeof(*p_row));

        //Allocate the innermost struct ccc accessed through *row
        for(i=0;i<p_aaa->pBbb->numRows;i++)
        {
            p_aaa->pBbb->row[i].cccStr1 = calloc(sizeStr, 1);
            p_aaa->pBbb->row[i].cccStr2 = calloc(sizeStr, 1);
            p_aaa->pBbb->row[i].cccStr3 = calloc(sizeStr, 1);
            p_aaa->pBbb->row[i].cccStr4 = calloc(sizeStr, 1);
            p_aaa->pBbb->row[i].cccStr5 = calloc(sizeStr, 1);
        }
    }

    void AssignStructs(void)
    {
        int i;
        strcpy(p_aaa->aaaStr1, "aaaStr 1"); 
        strcpy(p_aaa->aaaStr1, "aaaStr 2"); 
        strcpy(p_aaa->aaaStr1, "aaaStr 3"); 
        strcpy(p_aaa->aaaStr1, "aaaStr 4"); 
        strcpy(p_aaa->aaaStr1, "aaaStr 5");

        for(i=0;i<p_aaa->pBbb->numRows;i++)
        {
            strcpy(p_aaa->pBbb->row[i].cccStr1, "bbbStr 1");
            strcpy(p_aaa->pBbb->row[i].cccStr2, "bbbStr 2");
            strcpy(p_aaa->pBbb->row[i].cccStr3, "bbbStr 3");
            strcpy(p_aaa->pBbb->row[i].cccStr4, "bbbStr 4");
            strcpy(p_aaa->pBbb->row[i].cccStr5, "bbbStr 5");
        }
    }

    void FreeStructs(void)
    {
        int i;
        for(i=0;i<p_aaa->pBbb->numRows;i++)
        {
            free(p_aaa->pBbb->row[i].cccStr1);
            free(p_aaa->pBbb->row[i].cccStr2);
            free(p_aaa->pBbb->row[i].cccStr3);
            free(p_aaa->pBbb->row[i].cccStr4);
            free(p_aaa->pBbb->row[i].cccStr5);
        }
        free(p_aaa->pBbb->row);
        free(p_aaa->pBbb);

        free(p_aaa->aaaStr1);
        free(p_aaa->aaaStr2);
        free(p_aaa->aaaStr3);
        free(p_aaa->aaaStr4);
        free(p_aaa->aaaStr5);
    }

Upvotes: 3

Vistian
Vistian

Reputation: 29

Are you aware that gSOAP allocates memory for your data structures for you so that you don't have to do this? You only have to allocate memory for any data in a Request structure, but never the a reply structure.

Upvotes: 0

Related Questions