2013-05-20 35 views
0

我正在編碼SCPI解析器(作爲lib)。 lib用戶的設備或應用程序功能需要能夠以多種類型輸出多個結果(不是混合類型,即多種A型結果多種B型結果)。C函數多重結果多種類型

我首先想到了一個void指針數組,這個指針可以通過執行控制傳遞給設備函數(如果設備函數類似於unsigned int deviceFunction (double inArg1, bool inArg2, void ** outResults);),但是在這裏查看幾個Q,似乎不推薦使用void ptrs因爲當然,當我的響應格式化程序查看結果數據時,它不會知道要輸入的類型。

我看着使用工會和結構,類似於https://stackoverflow.com/a/3852192/1292918但是我不確定這是否是設備功能需要報告多個結果時的最佳方式。

也許我剛剛把我的想法咆哮起來。這樣做有沒有共同的方式或慣用法?

(請注意,這是C, C++)

+1

有多種方法可以解決這個問題。然而,更大的問題是關於要返回的對象的數量。如果它足夠小以至於可以始終使用最大數量的空間,那麼使用一個數組(聯合,使用另一個對象來指示類型,可能將它們打包在一個結構中)是合適的。如果它很大且不同,那麼動態分配空間可能會更好,然後存在調用者或被調用函數是否應提供空間以及哪個函數負責釋放空間的問題。 –

+0

@EricPostpischil這是針對嵌入式環境的,因此動態分配並不是一個真正的選擇 - 在編譯時應該知道內存分配。擁有固定大小的數組並不是一個問題,儘管大多數SCPI命令會返回有限數量的結果或者一個或兩個複合結果,因此可以使用最大數量的空間。感謝您的意見。 – Toby

回答

2

這裏是一個例子。許多變化是可能的,根據自己的喜好或需求:

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

要設置,給予一定的x這是一個struct 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: 
     … 
} 

你的函數可以聲明爲使主叫方傳遞一個struct MyStruct它,和函數填充它,或函數定義了一個本地struct MyStruct和值返回。 (通常,當你執行後者時,C的底層實現會發生什麼:調用者爲struct MyStruct分配空間並將其地址傳遞給調用函數,該函數負責填充它。如果編譯器正確優化,則性能等同於第一個選項,即使源代碼看起來像被調用的函數正在返回一個按值的結構。)

關於如何組織結構和聯合有多種選項。您可以使用數組聯合而不是聯合數組,並且您可以傳遞有關單獨返回的類型的信息,而不是在結構中的enum,並且可以單獨返回或通過其他一些元素信息,例如數組內的標記值。對於小而簡單的用途,這些變化可能沒有太大的區別,他們可以自己選擇。

1

你仍然可以使用void雙指針,但你必須找到一個方法來告訴來電者是什麼類型實際上是它。例如通過使用return從函數返回它,或者通過具有另一個參考類型參數,例如, int *outResultsType

然後讓它給調用者正確使用數據。

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,檢查類型並繼續。

這種方法的一個問題是得到「解引用類型的指針會破壞嚴格別名規則」。

+0

既然你知道這會打破別名規則,爲什麼不使用正確的語言特性來處理類型在運行時確定的對象,而不是轉換指針呢? –

+1

@EricPostpischil我還沒有用過這樣的聯盟。我沒有想到你可以做到這一點。感謝這個想法。 – Dariusz

2

基於Eric的評論,我提出了以下代碼,它使用union來返回一個複雜的結構。在這種情況下,不會出現提到的「別名規則」錯誤。

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

@EricPostpischil謝謝,我今天學到了一些新東西!:) – Dariusz

+0

爲什麼不能有一個'struct'包含一個「type」字段(例如'int')和一個'union',它們具有不同類型特定的返回數據如果需要的話可以是'struct')?似乎愚蠢的是在每個'struct'中複製'type',然後將它們推入'union'中。 –

+0

@NikBougalis如果你進一步傳遞相同的結構,也作爲聯合,你將不得不手動重新填充類型。不管你對數據做什麼,類型都是這樣。 – Dariusz