3

我什至不知道,我问的是不是愚蠢的。我不是要您为我编写任何代码,而是要以更好的方式做某事的想法。

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

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

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

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

我想为每种数据类型编写一个函数[比如一个用于 int,一个用于 char,一个用于 string 等],myStructPARAMETER_ID作为输入给出时,它返回一个成员的值。

例如,我需要一个int GetInt(PARAMETER_ID)函数,它返回int1eINT1 作为参数传递时的值。同样,我将拥有char GetCharacter(PARAMETER_ID)float GetFloat(PARAMETER_ID)

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

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

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

谢谢你。

4

3 回答 3

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并且不想玩这样的技巧)你可以考虑使用MELT制作一个 GCC 扩展(一个高级域特定语言来扩展 GCC)来帮助你;您可能可以制作一个 MELT 扩展来为您生成访问器功能。

您还可以说服您的老板从一个特别的描述文件(使用,或其他)生成struct、 the和访问器函数。GCC 为其选项文件做了这样的技巧,例如enumawkpythongcc/common.opt

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

请注意,一个好的编译器会将密集switch语句优化为需要恒定时间的索引跳转,即使对于数百个案例也是如此。

于 2013-02-18T07:25:14.557 回答
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));
}
于 2013-02-18T09:01:53.387 回答
2

你部分回答了你自己的问题,offsetof就是为了这个目的。您必须考虑结构填充/对齐。我认为您正在寻找类似的东西:

#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]);
}
于 2013-02-18T07:59:47.923 回答