On Sat, 10 Apr 2021 10:51:18 -0700, John Speth <johnspeth@yahoo.com>
wrote:
>I could use some advice for a problem that I've had on my mind for
>years. I'm looking for a black box function that will examine a memory
>block of a known structure and output a list of IDs that point to any
>members that have changed. I have a strong feeling that this problem has
>been solved by many people before me but I've never seen any solution.
>I'm looking for an elegant solution that requires little to no source
>code changes when a structure member is changed, added, or removed.
>Please follow my problem description below to get an idea of what I'm
>looking for.
>
>I have a largish C structure containing data members of various types
>and sizes. The structure definition will probably have to change over
>the course of project development. Data packaged in the structure will
>arrive at some processing function. The processing function will always
>keep a copy of the previously processed structure so that a compare
>operation can be used to identify any structure members that have
>changed. There is such a large number of members that my quest is to
>automate as much as possible the ID and comparison operations. Not
>automating would require the programmer to redo the comparison which
>would be a giant if/elseif/else statement with customized comparison for
>each structure member. That would invite mistakes if not done accurately.
>
>Below is what I've hacked out so far using the C preprocessor. It
>quickly points to various problems that makes the problem seem like too
>much work for the preprocessor (like, for example, it won't work for
>c[3]). Maybe a code generation step using perl or python would work
>better. I thought I'd check with the experts before working on it further.
>
>#define MEMBER(t,v) t v; size_t size_ ## v; size_t id_ ## v
>
>typedef struct
>{
> MEMBER(int,i);
> MEMBER(char,c);
> MEMBER(float,f);
>} STRUCT;
>
>STRUCT s;
>
>s.size_i = sizeof(s.i); s.id_i = offsetof(STRUCT,i);
>s.size_c = sizeof(s.c); s.id_c = offsetof(STRUCT,c);
>s.size_f = sizeof(s.f); s.id_f = offsetof(STRUCT,f);
>
>printf("I: Size = %d, ID = %d\n",s.size_i,s.id_i);
>printf("C: Size = %d, ID = %d\n",s.size_c,s.id_c);
>printf("F: Size = %d, ID = %d\n",s.size_f,s.id_f);
>
>Thanks - JJS
While I agree with Don and Hans-Bernhard re: maintainability, I did
something similar in one of my projects.
In my case, I was translating messages between machine format (C
structs) and readable text format (to be sent via TCP or logged to a
file, depending).
I used a table-driven solution shown below. Note this is just one
example - there were hundreds of such message structures in the
application, and keeping everything consistent was a chore. The C
structs were used by many tasks, but only one needed to translate
them.
YMMV,
George
*****************************************
typedef struct
{
DWORD jobId;
float position; /* position for _first_ exposure
*/
int slices;
long H1exposureTime; /* ms total */
long H2exposureTime; /* ms total for all - this may be
a guesstimate */
int H2s; /* number of H2s */
char* magazineId;
RECTANGLE border; /* image border */
char* patientName;
char* printDate;
}
MSG_JOB_PARAMETERS;
*****************************************
typedef struct
{
BOOLEAN use;
char* text;
t_Parameter type;
int offset;
}
MESSAGE_FORMAT;
static MESSAGE_FORMAT JobParametersFormat[] =
{
{ 1, "[EXPJ]" , PARAM_NONE , 0 },
{ 1, "%JobId=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, jobId ) },
{ 1, "%StartPosition=" , PARAM_FLOAT , offsetof(
MSG_JOB_PARAMETERS, position ) },
{ 1, "%NumberOfSlabs=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, slices ) },
{ 1, "%TotalH1ExposeTime=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, H1exposureTime ) },
{ 1, "%NumberOfH2s=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, H2s ) },
{ 1, "%TotalH2ExposeTimeGuess=", PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, H2exposureTime ) },
{ 1, "%MagazineId=" , PARAM_STRING , offsetof(
MSG_JOB_PARAMETERS, magazineId ) },
{ 1, "%HotspotX1=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, border.left ) },
{ 1, "%HotspotY1=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, border.top ) },
{ 1, "%HotspotX2=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, border.right ) },
{ 1, "%HotspotY2=" , PARAM_INTEGER, offsetof(
MSG_JOB_PARAMETERS, border.bottom ) },
{ 1, "%PatientName=" , PARAM_STRING , offsetof(
MSG_JOB_PARAMETERS, patientName ) },
{ 1, "%PrintDateTime=" , PARAM_STRING , offsetof(
MSG_JOB_PARAMETERS, printDate ) },
{ 1, "[]" , PARAM_NONE , 0 },
{ 0, NULL , PARAM_NONE , 0 }
};
static BOOLEAN
LM_Encode( LM_ConnectionInfo* connection, BYTE* msg, MESSAGE_FORMAT*
format )
{
char buffer[64];
int retcode;
int index;
int length;
int i;
int* iptr;
float* fptr;
char** sptr;
retcode = LM_OK;
index = 0;
for ( i = 0; format->use == TRUE; ++i )
{
if ( i == 1)
{
length = sprintf( buffer, "%%ConnectionId=%d\r\n",
connection->socketId );
}
else
{
iptr = (int*) (msg + format->offset);
fptr = (float*) (msg + format->offset);
sptr = (char**) (msg + format->offset);
switch ( format->type )
{
case PARAM_NONE : length = sprintf( buffer, "%s\r\n"
, format->text ); break;
case PARAM_INTEGER: length = sprintf( buffer,
"%s%d\r\n", format->text, *iptr ); break;
case PARAM_FLOAT : length = sprintf( buffer,
"%s%f\r\n", format->text, *fptr ); break;
case PARAM_STRING : length = sprintf( buffer,
"%s%s\r\n", format->text, *sptr ? *sptr : "" ); break;
}
format++;
}
if ((index + length) < connection->sendLimit)
{
strcpy( &connection->sendBuffer[index], buffer );
index += length;
}
else
{
retcode = LM_BAD_MESSAGE_SIZE;
break;
}
}
return ( retcode );
}