0

我正在编写一个 SCPI 解析器(作为一个库)。lib用户的设备或应用功能需要能够输出多种类型的多个结果(不是混合类型,即A类型的多个结果B类型的多个结果)。

我首先想到了一个由执行控件传递给设备函数的 void 指针数组(如果设备函数类似于unsigned int deviceFunction (double inArg1, bool inArg2, void ** outResults);),但是在这里查看一些 Q 似乎不建议使用 void ptrs,因为当然,当我的响应格式化程序查看结果数据时,它不知道要转换为哪种类型。

我研究了使用联合和结构,类似于https://stackoverflow.com/a/3852192/1292918但是我不确定当设备功能需要报告多个结果时这是否是最佳方式。

也许我刚刚让我的想法咆哮起来。是否有这样做的通用方式或习语?

(注意这是 C,不是C++)

4

4 回答 4

2

这是一个例子。根据您的喜好或需求,可以进行多种变化:

struct MyStruct
{
    enum MyNum { Integer, Float, String } WhatTypeIsHere;
    size_t HowMany;
    union
    {
        int Integer;
        float Float;
        const char *String;
    } u[MaximumNumberOfElement];
};

要设置,给定一些xstruct MyStruct

x.WhatTypeIsHere = Float;
x.HowMany = NumberOfObjectsThisTime;
for (i = 0; i < NumberOfObjectsThisTime; ++i)
    x.u[i].Float = some value;

Integer其他类型的设置类似。

读书:

switch (x.WhatTypeIsHere)
{
    case Float;
        for (i = 0; i < x.HowMany; ++i)
            Use x.u[i].Float for something…;
        break;
    case Integer:
        …
}

可以声明您的函数,以便调用者将 a 传递struct MyStruct给它,然后该函数将其填充,或者该函数定义一个局部struct MyStruct变量并按值返回它。(通常情况下,当你做后者时,在 C 的底层实现中发生的是:调用者为 a 分配空间struct MyStruct并将其地址传递给负责填充它的被调用函数。如果编译器优化得当,性能等效于第一个选项,即使源代码看起来像被调用的函数正在按值返回结构。)

关于结构和工会的组织方式有多种选择。您可以使用数组联合而不是联合数组,并且可以传递有关单独返回的类型的信息,而不是在enum结构内部传递,并且已知元素的数量可以单独返回或通过其他一些信息返回,比如数组里面的一个标记值。对于小型、简单的用途,这些变化可能没有太大区别,它们由您来选择。

于 2013-05-20T13:41:25.863 回答
2

根据 Eric 的评论,我想出了以下代码,它使用 anunion返回一个复杂的结构。在这种情况下,不会出现提到的“别名规则”错误。

struct Result {
  int type;
};

struct Data1
{
  int type;
  char* str;
  int val;
};

struct Data2 {
  int type;
  int arr[4];
};

union StructTestUnion
{
  struct Result res;
  struct Data1 data1;
  struct Data2 data2;
};

union StructTestUnion getRandomResult(void) {
  static int cnt = 0;
  union StructTestUnion resVal;
  switch (cnt) {
    case 0:
      resVal.data1.type = 1;
      resVal.data1.str = "struct data 1";
      resVal.data1.val = 123;
      break;
    case 1:
      resVal.data2.type = 2;
      resVal.data2.arr[0]=1;
      resVal.data2.arr[1]=2;
      resVal.data2.arr[2]=3;
      resVal.data2.arr[3]=4;
      break;
  }
  cnt++;
  if (cnt==2) cnt = 0;

  return resVal;
}

int main() {
  union StructTestUnion resVal;

  int a =0;
  for (a =0; a<4; a++) {
    resVal = getRandomResult();
    printf("%d: %d ", a, resVal.res.type);
    switch ( resVal.res.type ) {
      case 1:
        printf("str=[%s] val=[%d]\n", resVal.data1.str, resVal.data1.val);
        break;
      case 2:
        printf("arr=[%d,%d,%d,%d]\n",resVal.data2.arr[0], resVal.data2.arr[1],resVal.data2.arr[2], resVal.data2.arr[3]);
        break;
    }
  }
  return 0;
}
于 2013-05-20T13:18:20.140 回答
1

你仍然可以使用void双指针,但是你必须找到一种方法来告诉调用者它实际上是什么类型。例如,通过使用从函数返回它return,或者通过使用另一个引用类型参数,例如int *outResultsType.

然后让调用者正确使用数据。

于 2013-05-20T12:29:20.417 回答
0

您可以创建一个像这样的通用结构:

struct Generic {
  int type;
}

和几个带有数据的真实返回结构,但第一个字段未修改:

struct Data1 {
  int type; // == TYPE_DATA1 == 1
  char* aCharVal;
  double aDoubleVal;
}

struct Data2 {
  int type; // == TYPE_DATA2 == 2
  int* anArray;
  int arraySize;
}

这样您就可以返回结构,将其转换为struct Generic,检查类型并继续进行。

这种方法的一个问题是得到“取消引用类型双关指针将破坏严格别名规则”。

于 2013-05-20T12:45:25.487 回答