访问结构成员而不使用点运算符

时间:2013-02-18 07:12:07

标签: c

我甚至都不知道,我所问的是不是愚蠢与否。我不是要求你为我编写任何代码,而是要以更好的方式做一些事情。

我有一个包含大量项目的结构:

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

我有另一个枚举,它对myStruct中的每个项目都有一个条目。

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

我想为每种数据类型编写一个函数[比如一个用于int,一个用于char,一个用于字符串等],当myStruct时,它返回PARAMETER_ID成员的值作为输入。

例如,我需要一个int GetInt(PARAMETER_ID)函数,当eINT1作为参数传递时,该函数返回int1的值。同样地,我将有char GetCharacter(PARAMETER_ID)float GetFloat(PARAMETER_ID)等。

结构中的项目数可能很大。因此,为每个项目使用开关盒将不是一个可行的选择。

我能想到的其他选项是使用结构变量的地址和offsetof()函数来计算参数的地址,然后通过memcpy将所需的字节变成变量。在这种情况下,我需要在每个地方保留每个参数的偏移量,但这不是问题。

我正在寻找替代方案来做到这一点。任何帮助将不胜感激。

谢谢。

3 个答案:

答案 0 :(得分:5)

switch是一个很好的可行选择。

你也可以玩预处理器技巧。

您可以拥有包含

mystruct.def文件
 INTFIELD(int1)
 CHARFIELD(char1)
 INTFIELD(int2)
等等......然后你会多次包括它;声明结构:

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

声明枚举(使用e_int1代替eINT1

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

要实现访问者,

 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;
 }}

我并不认为这是更好或更易读的代码,但这种编程风格确实出现在某些C或C ++程序中(例如GCC内部及其gcc/tree.def

如果您的代码是一个非常大的代码库,并且您已准备好花上几天的时间(例如,因为您有很多这样的struct并且不想玩这样的技巧)您可能会考虑制作GCC扩展程序MELT(扩展GCC的高级域专用语言)可以帮助您;您可能可以进行MELT扩展以为您生成访问器功能。

您还可以说服您的老板从特定描述性文件中生成structenum和访问者函数(使用awkpython或随你)。 GCC为其选项文件执行此类操作,例如: gcc/common.opt

最后,如果包含_myStruct的标题是如此神圣,以至于您不允许触摸它,并且如果格式非常干净,您可以进行临时(例如awk )脚本来获取声明并处理它。

NB一个好的编译器将密集switch语句优化为索引跳转,这需要花费一些时间,即使是数百个情况。

答案 1 :(得分:3)

#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));
}

答案 2 :(得分:1)

您部分回答了自己的问题,offsetof旨在用于此目的。你必须考虑struct padding / alignment。我想你正在寻找类似的东西:

#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]);
}