简体   繁体   中英

Accessing structure members without using dot operator

I don't even know, whether what I'm asking is something stupid or not. I am not asking you to write any code for me, but an idea to do something in a better way.

I have a struct with a large number of items like this:

typedef struct _myStruct
{
    int int1;
    char char1;
    int int2;
    :
    :
    int int50;
}myStruct;

I have another enumeration which has a single entry for each item in myStruct.

enum
{
   eINT1,
   eCHAR1,
   eINT2,
   :
   :
   eINT50
} PARAMETER_ID;

I want to write a function for each data type [say one for int, one for char, one for string etc], which return the value of a member of myStruct , when the PARAMETER_ID is given as input.

For example I need a int GetInt(PARAMETER_ID) function which return the value of int1 when eINT1 is passed as an argument. Similarly I am going to have char GetCharacter(PARAMETER_ID) , float GetFloat(PARAMETER_ID) etc.

The number of items in the struct can be large. So using a switch-case for each item will not be a viable option.

Only other option I can think of is using the address of the structure variable and offsetof() function to calculate the address of the parameter and then by memcpy ing the required bytes into a variable. In that case I need to keep the offset of each parameter somewhere, but that is not a problem.

I am looking for alternate options to do this. Any help will be greatly appreciated.

Thank you.

A large switch is a good viable option.

You might also play preprocessor tricks.

You could have a mystruct.def file containing

 INTFIELD(int1)
 CHARFIELD(char1)
 INTFIELD(int2)

etc... Then you would include it several times; to declare the structure:

 struct _myStruct {
 #define INTFIELD(F) int F;
 #define CHARFIELD(F) char F;
 #include "mystruct.def"
 #undef INTFIELD
 #undef CHARFIELD
 };

To declare the enumeration (using e_int1 instead of eINT1 )

 enum field_en {
 #define INTFIELD(F) e_##F,
 #define CHARFIELD(F) e_##F,
 #include "mystruct.def"
 #undef INTFIELD
 #undef CHARFIELD
 };

To implement the accessor,

 int get_int(struct _myStruct*s, enum field_en f)
 {
    switch (f) {
 #define INTFIELD(F) case e_##F: return s->F;
 #define CHARFIELD(F) /*nothing*/
 #include "mystruct.def"
 #undef INTFIELD
 #undef CHARFIELD
    default: return 0;
 }}

I don't claim this is better or more readable code, but that kind of programming style does appear in some C or C++ programs (eg GCC internals with its gcc/tree.def )

If you code is a very large code base, and you are ready to spend days of work (eg because you have a lot of such struct and don't want to play such tricks) you might consider making a GCC extension with MELT (a high-level domain specific language to extend GCC) to help you; you probably can make a MELT extension to generate the accessor functions for you.

You could also convince your boss to generate both the struct , the enum and the accessor functions from an ad-hoc descriptive file (using awk , python or whatever). GCC does such tricks for its options file, eg gcc/common.opt

At last, if the header containing the _myStruct is so sacred that you are not allowed to touch it, and if it is very cleanly formatted, you might make an ad-hoc (eg awk ) script to get that declaration and process it.

NB a good compiler optimizes dense switch statements as indexed jumps which take constant time, even for hundred of cases.

#include <stddef.h>
#include <stdio.h>

struct S
{
 int  int1;
 char char1;
 int  int2;
 char char2;
 long long1;
} myStruct = {12345, 'A', 321, 'B', -1L};

enum
{
 eINT1  = offsetof(struct S, int1),
 eCHAR1 = offsetof(struct S, char1),
 eINT2  = offsetof(struct S, int2),
 eCHAR2 = offsetof(struct S, char2),
 eLONG1 = offsetof(struct S, long1),
} PARAMETER_ID;

char GetChar(int para_id)
{
  return *((char*)((char *)&myStruct + para_id));
}

int GetInt(int para_id)
{
  return *((int*)((char *)&myStruct + para_id));
}

long GetLong(int para_id)
{
  return *((long*)((char *)&myStruct + para_id));
}

void main(void)
{
  printf("offsetof int1  = %d\n", eINT1);
  printf("offsetof char1 = %d\n", eCHAR1);
  printf("offsetof int2  = %d\n", eINT2);
  printf("offsetof char2 = %d\n", eCHAR2);
  printf("offsetof long1 = %d\n", eLONG1);


  printf("int1  = %d\n",  GetInt (eINT1));
  printf("char1 = %c\n",  GetChar(eCHAR1));
  printf("int2  = %d\n",  GetInt (eINT2));
  printf("char2 = %c\n",  GetChar(eCHAR2));
  printf("long1 = %ld\n", GetLong(eLONG1));
}

You partially answer your own question, offsetof is meant to be used for this very purpose. You have to consider struct padding/alignment. I think you are looking for something similar to this:

#include <stddef.h>  // size_t, offsetof
#include <string.h>  // memcpy
#include <stdio.h>

typedef struct
{
  int  int1;
  char char1;
  int  int2;
  int  int50;
} myStruct;

typedef enum
{
  eINT1,
  eCHAR1,
  eINT2,
  eINT50,
  ITEMS_IN_STRUCT
} myEnum;


static const size_t MYSTRUCT_MEMBER_OFFSET [ITEMS_IN_STRUCT] =
{
  offsetof(myStruct, int1),
  offsetof(myStruct, char1),
  offsetof(myStruct, int2),
  offsetof(myStruct, int50),
};

static const myStruct MS;
static const size_t MYSTRUCT_MEMBER_SIZE [ITEMS_IN_STRUCT] = 
{
  sizeof(MS.int1),
  sizeof(MS.char1),
  sizeof(MS.int2),
  sizeof(MS.int50)
};



void myStruct_get_member (void* result, const myStruct* ms, myEnum id)
{
  memcpy (result, 
          (char*)ms + MYSTRUCT_MEMBER_OFFSET[id], 
          MYSTRUCT_MEMBER_SIZE[id]);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM