【问题标题】:C : send different structures for one function argumentC:为一个函数参数发送不同的结构
【发布时间】:2014-12-14 23:27:29
【问题描述】:

我有一个使用 OpenGL 绘制圆的函数,我想向它传递一个包含 x 和 y 坐标以及半径的结构。问题是同一个函数必须与 3 个不同的结构一起使用,它们都包含坐标、半径和绘图函数不使用的其他一些东西。

有没有办法让 3 个不同的结构只有一个参数(一次只发送一个)。

我希望我已经足够精确了。

PS : 函数必须是“抽象的”。

【问题讨论】:

    标签: c function structure abstract arguments


    【解决方案1】:

    是的,您可以使用这样的原型:

    void foo(char type, void *data);
    

    使用类型告诉函数将数据用作哪个结构,你很好。

    struct c *prepareStructC(void);
    //...
    struct c *toto = prepareStructC();
    foo('c', toto);
    //...
    void foo(char type, void *data)
    {
      int x, y;
      switch (type)
      {
        case 'c':
          x = ((struct c*)data)->x;
          y = ((struct c*)data)->y;
          break;
        //...
      }
      //...
    }
    

    第二个选项,如果你想避免开关/大小写,并且能够在之后添加更多的结构类型,而不演变 foo,你可以确保你的所有结构 开始 以必要的数据,总是以相同的顺序,使用相同的类型。这样您就可以从 C++ 中创建类似“接口”的东西,并使用该类型的抽象版本:

    struct abstract
    {
      int x;
      int y;
      int radius;
    }
    
    struct a
    {
      struct abstract abs;
      //... other data ...
    }
    struct b
    {
      struct abstract abs;
      //... other data ...
    }
    struct c
    {
      struct abstract abs;
      //... other data ...
    }
    
    //Two choices : either you have:
    void foo(void *data)
    {
      int x,y,r;
      x = ((struct abstract*)data)->x;
      y = ((struct abstract*)data)->y;
      r = ((struct abstract*)data)->radius;
      //...
    }
    
    //OR Smarter way:
    void foo2(struct abstract *data)
    {
      int x,y,r;
      x = data->x;
      y = data->y;
      r = data->radius;
    }
    //Calling foo2 with:
    struct a *sa = prepareStructA();
    struct b *sb = prepareStructB();
    struct c *sc = prepareStructC();
    foo2(sa->abs);
    foo2(sb->abs);
    foo2(sc->abs);
    

    第二种方法的第二部分让你更加灵活,将特定信息分解为一个子类型,使你可以将abs部分放在struct a/b/c中的任何位置,并且在a的原理上更好结构的单一用途(在结构内拥有坐标和半径其他东西并不总是最好的。)

    【讨论】:

    • +1 表示第二个选项,采用更智能的方式:这正是 OO 在 C 中实现的方式。
    • 谢谢大家,我打算在结构中包含一个结构。
    【解决方案2】:

    仅当函数接受 void* 作为参数,然后将 void* 转换为函数中正确的指针类型,然后再取消引用以访问其中的数据。

    typedef enum
    {
        type1, type2, type3
    } datatype;
    
    void foo(void* data, datatype type)
    {
        switch(type)
        {
            case type1:
            // do something with data by casting it to the proper type
            break;
    
            // ... other cases here
        }
    }
    

    但是,通过这样做,您将 a) 需要传递另一个参数,该参数表明原始类型在被传递之前被强制转换为 void*,并且 b) 您正在放弃类型系统,这几乎不是一个好主意,应该总是被避免。

    如果您采用这种方法,强烈建议在内部调用函数(采用 void*)之前创建三个采用正确类型的“包装器”函数。将永远不要直接调用void* 函数设为严格约定;只调用包装器。通过这种方式,您仍然可以使用类型系统来帮助解决编译错误。

    void bar1(someStruct* data)
    {
        foo((void*) data, type1);
    }
    void bar2(someOtherStruct* data)
    {
        foo((void*) data, type2);
    }
    void bar3(yetAnotherStruct* data)
    {
        foo((void*) data, type3);
    }
    

    【讨论】:

    • 你说得对,枚举比普通的 "char" 好,我的第一个方法更脏^^"
    • 同意,但你提出了一个很好的观点,即使用相同的数据“签名”开始单独的结构,允许使用类型系统和(理论上)随着时间的推移对主要功能的更改更少。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    相关资源
    最近更新 更多