如何通过结构字段名称传递功能?

时间:2019-12-27 15:53:54

标签: c struct

我有许多处理一个结构的类似函数调用,但是每个调用都使用不同的结构域。

示例:

 typedef struct {
     int i1;
     int i2;
     int i3;
    } S;

获取结构字段的函数(最好避免使用它们):

int getFieldI1 (S *s){ return s->i1; }

int getFieldI2 (S *s){ return s->i2; }

int getFieldI3 (S *s){ return s->i3; }

函数我必须多次调用:

void doJob (int (*get_field_func)(S *)){
 //some code
 S s;
 int v = get_field_func(&s);
 //some code 
}

我这样调用doJob():

doJob(&getFieldI1);
doJob(&getFieldI2);
doJob(&getFieldI3);

我想这样做:

doJob(i1);
doJob(i2);
doJob(i3);

在C语言中有可能吗?

5 个答案:

答案 0 :(得分:2)

选项1-偏移量

您可以使用内存偏移量。

void doJob (int offset){
    //some code
    S s;
    int v = *(&s+offset*sizeof(int));
    //some code
}

您可以这样称呼它:

doJob(0);//i1
doJob(1);//i2
doJob(2);//i3

如注释中所指出,偏移量是不安全的。您可以为此创建支票:

if(offset>2||offset<0){
    //some kind of error
}

此外,仅当结构仅包含整数(或相同类型的元素,您需要对其进行调整)时才可以使用它(请参见注释)。

如果s1,s2和s3之前有元素,则需要添加元素的大小(作为填充,只需添加);

选项2-常量

另一个选项(没有提到的问题)是定义常量/宏:

您将像这样定义它们:

#define I1 &getFieldI1
#define I2 &getFieldI2
#define I3 &getFieldI3

并使用以下命令调用它:

doJob(I1);
doJob(I2);
doJob(I3);

答案 1 :(得分:0)

只需传递一个指向该字段的指针:

void doJob( int* fieldPointer )
{
    assert( fieldPointer != NULL );

    // Get the field value:
    int v = *fieldPointer;

    // Do something with the field value:
    v += 10;

    // Save the updated value back to the field:
    *fieldPointer = v;
}

用法:

S structInstance = ...

doJob( &structInstance.i1 );
doJob( &structInstance.i2 );
doJob( &structInstance.i3 );

答案 2 :(得分:0)

  

如何将结构字段名称传递给函数?

通常,您不能。用C编码的典型库不会将内部struct的字段显示到外部。换句话说,字段名称仅对于编译器是已知的,并且与当前translation unit相关,并且在运行时没有任何意义。

请考虑以下metaprogramming方法:编写元程序(使用C或某些脚本语言,例如GuileawkPython等)来生成C代码,并相应地建立您的构建。这可能意味着要编辑Makefile或配置build automation工具。

这是上个世纪以来的惯例。看看SWIGRPCGEN是一个著名的例子。

您可能会使用preprocessor的技巧,例如X-macros

答案 3 :(得分:0)

抱歉,我无法猜测i1 / i2 / i3的正确类型。所以我使用c ++中的auto关键字:

#include <stdio.h>

typedef struct {
     int i1;
     int i2;
     int i3;
    } S;

int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }

void doJob (int (*get_field_func)(S *)){
 //some code
 S s = {1,2,3};
 //S s;
  int v = get_field_func(&s);
 //some code
 printf("got: %d\n", v);
}

int main() {
  S s = {1,2,3};
  auto i1 = getFieldI1;
  auto i2 = getFieldI2;
  auto i3 = getFieldI3;

  doJob(i1);
  doJob(i2);
  doJob(i3);
}

然后

g++ 59503102.cxx -o 59503102 && ./59503102

按预期产生

got: 1
got: 2
got: 3

普通c版本

#include <stdio.h>

typedef struct {
     int i1;
     int i2;
     int i3;
    } S;

int getFieldI1 (S *s){ return s->i1; }
int getFieldI2 (S *s){ return s->i2; }
int getFieldI3 (S *s){ return s->i3; }

void doJob (int (*get_field_func)(S *)){
 //some code
 S s = {1,2,3};
 //S s;
  int v = get_field_func(&s);
 //some code
 printf("got: %d\n", v);
}

int main() {
  S s = {1,2,3};
  int (*i1)(S *) = getFieldI1;
  int (*i2)(S *) = getFieldI2;
  int (*i3)(S *) = getFieldI3;

  doJob(i1);
  doJob(i2);
  doJob(i3);
}

答案 4 :(得分:0)

不幸的是,C并不能完全满足您的需求。但是您可以通过一些代码更改来部分获胜。

我有一个解决方案。对于第一个,我提出了(简化!)实现,对于第二个,我仅提供了一个提示。请检查它们是否适合您。

您的示例结构:

typedef struct {
     int i1;
     int i2;
     int i3;
    } S;

我将定义一个代表特定字段的枚举:

typedef enum
{
  FIELD_ID_I1,
  FIELD_ID_I2,
  FIELD_ID_I3,

  FIELD_ID_MAX
} FieldId_e;

然后,我将在您的常规函数​​中添加一个字段参数,在内部管理要返回的正确字段。 必须在此处进行一些智能的错误管理,以防ID错误。为简便起见,我只返回-1。

int getField (S *s, FieldId id)
{
  int ret = -1; 

  switch(id)
  {
    case FIELD_ID_I1:
      ret = s->i1;
      break;

    case FIELD_ID_I2:
      ret = s->i2;
      break;

    case FIELD_ID_I3:
      ret = s->i3;
      break;
  }

  return ret;
}

您的doJob将成为

void doJob (int (*get_field_func)(S *, FieldId), FieldId id){
 //some code
 S s;
 int v = get_field_func(&s, id);
 //some code 
}

最后的通话将变成这个。但是可能(取决于您的情况)只有一个通用函数将可以省略函数指针,从而大大简化了界面。

doJob(&getField, FIELD_ID_I1);
doJob(&getField, FIELD_ID_I2);
doJob(&getField, FIELD_ID_I3);

只是对另一个需要使用指针的棘手解决方案的简短引用。

您知道offsetof宏吗? (Wikipedia EN)

  

它计算给定成员在a中的偏移量(以字节为单位)   struct或union类型,即size_t类型的表达式。 offsetof()   宏带有两个参数,第一个是结构名称,而   第二个是结构中成员的名称。

在这种情况下,您可能会遇到类似的情况

int getField (S *s, size_t offset);

doJob(&getField, offsetof(S, i1));