문제

Hello there I am working on an assignment in C where I need to pass in an unknown type of parameter into a function.

For example suppose I have the following:

int changeCount(void* element)
{
    element.Count = element.Count++;

    return 1;

}

The reason why variable element is void is because there are 3 types of possibilities. All 3 however do have a member variable named "Count".

When I try to compile the actual code I wrote in Eclipese, I get the following error:

error: request for member ‘Count’ in something not a structure or union

I am guessing this is happening because the compiler doesn't know the type of "element" before hand. However I don't see why this isn't working.

Thanks for help!

도움이 되었습니까?

해결책

You need to cast the pointer to one of those 3 types and then use it. Something like:

MyType* p = (MyType*)element;
p->count++;

However, you need to be sure of the type of the object you are casting as casting an object to the wrong type can be dangerous.

다른 팁

First, you are passing in a pointer to void, which is a valid approach for unknown types, but you need to be passing in pointers to your different object types.

C isn't a dynamic language so symbolic type information is largely deleted before run time so when you say that your three types all have a member Count this doesn't help with your function design.

The only way you can access Count is by casting your void* parameter to the correct pointer type before derefencing with -> or (*element).Count (i.e. not just .).

Unless you are relying on your types having a compatible layout (which is likely to be implementation dependent) you will also need to pass something that helps your function determine the correct cast to perform. At this point you may be better off with three seperate functions and better type safety.

You have to cast it to the actual type but you may get unexpected results in the structs don't have the count property in the same place.

typedef struct _A 
{
    int count;
    int x;
}A;
int changeCount(void* element)
{
    ((A*)element)->Count++;

    return 1;

}

Also remember that if you pass a pointer to a struct that looks like the following you will increment the x field not the count.

typedef struct _B 
{
    int x;      
    int count;

}B;

    B st;
    B* pst = &st;
    changeCount(pst);

If the .Count element is of the same type for each of those 3 types, then you can also use a macro. Assuming it's int, you can do this:

#define changeCount(a) _changeCount((a), &(a)->Count)

int _changeCount(void* element, int *count)
{
  (*count)++;
  return 1;
}

This will work, because the a.Count address will be resolved when you call the function, not after (when you don't know the type anymore). I assume that you have the proper type when you call the function though. So Sometype x; changeCount(x); will work, but passing in something that already is a (void*) won't.

Also your original expression element.Count = element.Count++; is rather bizarre. If you want to increment, use element.Count++, or element.Count = element.Count + 1.

use a cast.

ClassName(element).count++

also your element.Count = element.Count++; line is redundant, you just need to do element.count++(which increments the value by one)

When compiling, C discards[1] most type information, leaving only offsets. So, your function would compile to something like this, in pseudocode:


changeCount:
  assign *(*(stack_ptr + offset_element) + offset_Count) + 1
    to *(stack_ptr + offset_element) + offset_Count;
  assign 1 to return_value;
  pop

The stack_ptr is the location of the stack frame that was created when you call changeCount, the offset_element is the location of the element argument, relative to the stack_ptr, but what is offset_Count? Keep in mind, all the compiler knows about your code is just what you have shown in your example; element is a generic pointer, not really a pointer to anything. You will have to tell the compiler what element is pointing to, by casting or assigning it to a variable[2]:


typedef struct { int Count; } counted_t;
int changeCount(void* element)
{
    counted_t* counted = element;
    counted.Count++;
    return 1;
}

This function will generate essentially the same (pseudo)code as above, but the compiler now knows what the offset of Count should be.

You mention that there are three possibilities for the type of what element is pointing to. There are a couple of ways of handling that: either a distinguished union or an "inherited" structure. For a distinguished union use, say, a structure with one element being an enum identifying which of the three possibilities and another element being a union of the three possible structures; this is roughly what the ML languages (OCaml, Haskell, etc.) call an algebraic data type or what a union is in Pascal. For "inheritance", you could use type definitions:


typedef struct { counted_t counted; int i; } counted_int_t;
typedef struct { counted_t counted; double d; } counted_double_t;
typedef struct { counted_t counted; char* s; } counted_charptr_t;

In this case, you could use the changeCount function above and pass in a pointer to a counted_int_t, counted_double_t, or counted_charptr_t.

What happens is that the compiler will lay out the three structures with the Count element in the "descendant" structures in the same place as long as the counted_t element is first. (At least, in every compiler I have ever used and in every bit of code I have seen. I think this made it into the C standard at some point, but it is a very normal idiom.)

[1] Except for debugging information, if you have told the compiler to emit it. Your program will not have access to that information, though, so it would not help in this case.

[2] The x++ (postincrement) operation increments the variable (well, lvalue) that it is applied to; the assignment in the original code is unnecessary.

This is exactly the problem:

I am guessing this is happening because the compiler doesn't know the type of "element" before hand. However I don't see why this isn't working.

Calling a method or member data by name is not generally a feature of staticly-typed languages.

Even a void * can only be reliably cast to a T *, where T is a type, when the pointer is actually a pointer to that type.

In C++, one possibility is to have all three types inherit from the same virtual bass class X which has a method Count (i.e. an interface ICountable). Then casting to X * and using p->Count. This would be idiomatic C++ - all the classes implement the same interface and thus they all support this method. This would be a language-supported method kind of analogous to relying on the same struct offsets trick shown in Tommy McGuire's answer, which makes the structs all similar by convention. If you were to alter the structs, or the compiler were to depart from the standard for laying out structs, you would be in hot water.

I can't help thinking that this is rather a toy problem, since the method is so simple, one would typically not wrap it in a function - one would simply call it inline: T t; t.Count++;.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top