gSoap generated client-side structure initialization and use (using ANSI C bindings)
First of all, I searched and although there are a number of struct initialization solutions offered, I did not find anything directly answering this issue.
Also, this question is being posted simply to assist anyone else that has a similar question, as I have already worked out the solution and because of my newbie status
will post it immediatelyat least 8 hours
after posting this.
However, I am still very interested in comments and edits to the solution I will offer from those with better solutions, or with more experience in gSoap...
The Scenario:
I am fairly new to soap in general, and have been 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 within soapClient.c) are many times complex (nested) structures. Argument 4 specifically, because it it is the input structure, has to 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 for illustrative purposes)
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 */
};
The question is:
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?
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:
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, sizeof(char));
p_aaa->aaaStr2 = calloc(sizeStr, sizeof(char));
p_aaa->aaaStr3 = calloc(sizeStr, sizeof(char));
p_aaa->aaaStr4 = calloc(sizeStr, sizeof(char));
p_aaa->aaaStr5 = calloc(sizeStr, sizeof(char));
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, sizeof(char));
p_aaa->pBbb->row[i].cccStr2 = calloc(sizeStr, sizeof(char));
p_aaa->pBbb->row[i].cccStr3 = calloc(sizeStr, sizeof(char));
p_aaa->pBbb->row[i].cccStr4 = calloc(sizeStr, sizeof(char));
p_aaa->pBbb->row[i].cccStr5 = calloc(sizeStr, sizeof(char));
}
}
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);
}
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.
来源:https://stackoverflow.com/questions/5837772/gsoap-generated-client-side-structure-initialization-and-use