views:

724

answers:

7

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!

+5  A: 

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 what type of object it is as this is dangerous.

Naveen
Or assign to a local variable of the appropriate type: eg struct foo * f = element; (no cast required)
William Pursell
Thanks for your response. However this wouldn't work if I don't know what the MyType is beforehand right?
mr.flow3r
You have to know ... There is no way out
Naveen
There's one way out: knowing the offset of Count. If offset is 0, you can do `*((int *) element)++;` and so on.
Gonzalo
+5  A: 

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.

Charles Bailey
your explanation clarified a lot of things. Thanks!!
mr.flow3r
+4  A: 

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);
rerun
+1  A: 

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)

Dasuraga
C++'s function style casts aren't valid C and, in general, casting a pointer type (`void*`) to an object isn't likely to work.
Charles Bailey
A: 

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++;.

Cade Roux
I guess this won't work: "inherit from the same virtual bass class X" - the question is about C, not C++.
viraptor
Unless the tags are misleading, this is C, not C++.
LnxPrgr3
I will edit to make clear that the last sentence is a C++ design idiom.
Cade Roux
+2  A: 

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.

viraptor
Cade Roux
viraptor
+1  A: 

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.

Tommy McGuire