0

The following insertSNode function inserts item and returns updated pointer. Within the insertSnode function, each data from different struct is dereferenced accordingly.

PROBLEM: I get compiler errors on (LINE 1), (LINE 2), (LINE 3), (LINE 4) with the following error message:

"Member reference base type 'void' is not a structure or union."

QUESTION:

How do I get rid of compiler errors? If I can't, alternatively, do we have any better solution in writing functions as much identical as this situation? Let's assume there are too many struct types (i.e. Type_A, Type_B, etc), and it is extremely inefficient to create different functions with different type declarations.

*pListTypeA = (Type_A *) insertSnode(*pListTypeA, pPreTypeA, pTypeAItem, TYPEA);
*pListTypeB = (Type_B *) insertSnode(*pListTypeB, pPreTypeB, pTypeBItem, TYPEB);
*pListTypeC = (Type_C *) insertSnode(*pListTypeC, pPreTypeC, pTypeCItem, TYPEC);
// more assignments

insertSnode definition:

void* insertSnode(void* pList, void* pPre, char* item, const int type) {
    void *pNew;

    if (TYPEA == type) {
        pList = (Type_A*) pList;
        pPre =  (Type_A*) pPre;
        pNew =  (Type_A*) pNew;
    } else if (TYPEB == type) {
        pList = (Type_B*) pList;
        pPre =  (Type_B*) pPre;
        pNew =  (Type_B*) pNew;
    } else (TYPEC == type) {
        pList = (Type_C*) pList;
        pPre =  (Type_C*) pPre;
        pNew =  (Type_C*) pNew;
    }
    if (!(pNew = malloc(sizeof(*pList)))) {
        printf(ERR_NOT_ENOUGH_MEMORY);
        exit(EXIT_NOT_ENOUGH_MEMORY);
    }
    pNew->name = item;    // compiler error: (LINE 1)
    if (pPre == NULL) {
        pNew->link = pList;    // compiler error: (LINE 2)
        pList = pNew;
    } else {
        pNew->link = pPre->link;    // compiler error: (LINE 3)
        pPre->link = pNew;    // compiler error: (LINE 4)
    }
    return pList;
}

NOTE:

FYI: I was able to run this code with no if-statements for type-declarations and just one type (e.g. Type_A). So, we all know there are no external problems than the type-declarations.

J. Berman
  • 484
  • 1
  • 6
  • 13
  • Quite frankly I believe you are better off with a dedicated function per type - even if it is more verbose. The approach you are taking will make your `insertSNode` function to become larger and larger and more unmaintainable with time. Maybe you should instead reconsider having so many lists? – AndersK Feb 17 '13 at 03:56

3 Answers3

1

The problem here is that the below line

pList = (TYPE_A*) pList;

Still assigns to a void* pointer, so the cast is useless, and the rest of the code behaves as if it didnt happen.

I do not know a clean way to fix this neatly I'm sorry. I was going to suggest an intermediate struct but that wouldnt work since you want to modify the instances directly.

If this were C++, generics would have been the answer..

To get your current idea to work, you either need to make different variables for each type and cast to those, or cast each time you want to use the variables.

Or try to unify the type into one as Michael Pryor suggests.

Else you can try macros - see below example for implementation for one line of the above code.

#define OPERATEONTYPE(Type,TypeEnum)\
if(type == TypeEnum) {\
    ((Type*)pNew)->name = item;\
}


void* insertSnode(void* pList, void* pPre, char* item, const int type) {
         OPERATEONTYPE(Type_A,TYPEA)
    else OPERATEONTYPE(Type_B,TYPEB)
    else ....
}

#undef OPERATEONTYPE //should only be used/usable within that function anyway
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • Should I make different type-names for different types? I mean, should I declare `Type_A* paList;` `Type_B* pbList;` (...and so on...)? – J. Berman Feb 17 '13 at 03:11
  • 1
    This is the correct answer to why it fails. As to how you can fix it, maybe you could just have ONE type, and add a variable to your type to differentiate A/B/C, etc. – Michael Pryor Feb 17 '13 at 03:12
  • @JaneBerman I was going to suggest that, but if you do that you need to do that to the rest of the code as well, and it would be cleaner by far to just make many functions – Karthik T Feb 17 '13 at 03:13
  • @KarthikT Thank you for your answer/comment. I just want to clarify one thing here. In `C`, once you have declared the type, you cannot change its type? `void *pNew;` `pNew = (Type_A*) pNew;` does not work for this sense? – J. Berman Feb 17 '13 at 03:18
  • @MichaelPryor I'm afraid if I don't fully understand your comment... Could you explain to me in more detail? – J. Berman Feb 17 '13 at 03:18
  • @KarthikT @MichaelPryor Regarding your comments on "unify[ing] type", those `struct`s have non-shared field and it would make no sense to have the same `struct` name... So this is not my optimal way.. I guess I should make different functions (or variables)... – J. Berman Feb 17 '13 at 03:22
  • @JaneBerman Correct, you cannot change a type once you've declared it. – Michael Pryor Feb 17 '13 at 03:26
  • @JaneBerman: It might be helpful to use the common initial sequence rule. http://stackoverflow.com/questions/8702713/struct-pointer-compatibility – aschepler Feb 17 '13 at 03:56
  • @JaneBerman an idea occurred to me, If your treatment of each type is identical in this function, you can try to write a macro and use the type as a parameter. – Karthik T Feb 17 '13 at 08:56
  • @KarthikT Could you please explain in detail? – J. Berman Feb 17 '13 at 09:12
  • @JaneBerman pls see my edit, not pretty but atleast duplication is minimized. – Karthik T Feb 17 '13 at 09:20
  • @KarthikT Is it legal to have an `if` statement before initial declaration of the variables? My compiler does not recognize `Type` with the error message "Use of undefined identifier 'pNew'" – J. Berman Feb 17 '13 at 09:42
  • 2
    C99 allows it and C89 does not allow it: http://stackoverflow.com/questions/8474100/where-you-can-and-cannot-declare-new-variables-in-c – J. Berman Feb 17 '13 at 10:08
  • @Karthik Thanks a lot for the update! I think macro is one way to go! – J. Berman Feb 17 '13 at 10:09
1

If every "struct type" has some common values (name, next, prev, etc..), then perhaps you want to use a union? Store the common parts in a generic struct and then use a union to store the non-common parts.

Note @KarthikT's answer is correct, these really are syntax errors because pNew is always a void type, and has no "struct" associated with it, so pNew->name is a meaningless construct.

clemej
  • 2,553
  • 2
  • 19
  • 28
-2

As Karthik said you have to use generics in c++.
In c, there's one solution. Remove the first if...else if...else block in theinsertSnode().
Use it as follows.

void* insertSnode(void* pList, void* pPre, char* item, const int type) {
void *pNew;
 if (!(pNew = malloc(sizeof(*pList)))) {
    printf(ERR_NOT_ENOUGH_MEMORY);
    exit(EXIT_NOT_ENOUGH_MEMORY);
}
if (TYPEA == type) {
   if(pPre==NULL){
   (Type_A*)pNew->name=item;
   (Type_A*)pNew->link=plist;
   (Type_A*)pList=pNew;
 }else{
    (Type_A*)pNew->link=(Type_A*)pPre->link;
     (Type_A*)pPre->link;
  }
} else if (TYPEB == type) {
    if(pPre==NULL){
   (Type_B*)pNew->name=item;
   (Type_B*)pNew->link=plist;
   (Type_B*)pList=pNew;
 }else{
    (Type_B*)pNew->link=(Type_B*)pPre->link;
     (Type_B*)pPre->link;
  }
} else if(TYPEC == type) {
   if(pPre==NULL){
   (Type_C*)pNew->name=item;
   (Type_C*)pNew->link=plist;
   (Type_C*)pList=pNew;
 }else{
    (Type_C*)pNew->link=(Type_c*)pPre->link;
     (Type_C*)pPre->link;
  }
}


return pList;
}
Govind Balaji
  • 639
  • 6
  • 17