I have a struct that contain an attribute that can take many types,
I want ask about the most appropriate way to declare this attribute.
Example:
struct
{
void* pShape; //poiter to the shape that will be casted on *tCircle or *tRectangle
int type;//type of the shape
int h;
int l;
}tImage;
struct{
int size;
int r;
HANDLE calcul;
}tCircle
struct{
int size;
int x;
int y;
HANDLE calcul;
}tRectangle;
As you see here i'am using void* to declare a pointer to the shape and use type attribute to guess the type of the shape.
this is my function for the calcuation of the size of the shape in an image
int Image_Get_ShapeSize(tImage Im)
{
switch (Im.type)
{
case CIRCLE:
((tCircle*)(Im.shape))->calcul();
break;
case RECTANGLE:
((tRectangle*)(Im.shape))->calcul();
break;
default:
break;
}
}
what do you think about this is it a good method ?
I do not understand well why you need the tCircle and tRectangle structures because they have the same fields. I suggest you use only one definition and initialize the function pointer to a different and specific method.
struct shape {
int size;
int x;
int y;
void (*calcul) (struct shape * shape);
};
And then the specific functions:
void circle_calcul(struct shape * shape)
{
...
}
void rectangle_calcul(struct shape * shape)
{
...
}
And finally:
struct shape circle;
struct shape rectangle;
circle.calcul = circle_calcul;
rectangle.calcul = rectangle_calcul;
Related
If I have multiple structs which have a common method like this:
typedef struct s sphere;
typedef struct s{
point3 center;
double radius;
bool (*hit)(sphere, const ray*, double, double, hit_record*);
} sphere;
typedef struct b box;
typedef struct b{
point3 center;
double radius;
bool (*hit)(box, const ray*, double, double, hit_record*);
} box;
Is it possible for me to create some type of array such that it can store both of these structs and loop through it calling the method like this:
objects shapes[50]; // Where objects is something that can let shapes hold both structs
//Pretend shapes has a mixture of Boxes and Spheres
int number_of_shapes = 20;
for (int i = 0; i < number_of_shapes;i++){
if shapes[i].hit(shapes[i], r, t, t, red) { //random variables
;// do something
}
}
I just started learning c yesterday so I am not really sure what to do. I tried using void pointers but that also failed miserably.
Void pointers are the right way to do that, but it requires that your objects are persistent in memory, because an array of pointers won't retain them. I give you an example :
EDIT : I update the example to do the job using function pointers and simulate the behavior of methods.
#include <stdio.h>
#include <stdlib.h>
// Type your code here, or load an example.
enum { TypeSphere, TypeBox };
typedef struct s sphere;
typedef struct b box;
typedef char (*hit)(void *object, void *ray, double x, double y, void*hit_record);
// Starts both structure with a common function pointer type
typedef struct s{
hit method;
double center;
double radius;
} sphere;
typedef struct b{
hit method;
double center;
double radius;
} box;
// Implementation of a method for each type
char sphereMethod(void *s, void *ray, double x, double y, void *hit_record) {
printf("A sphere\n");
}
char boxMethod(void *b, void *ray, double x, double y, void *hit_record) {
printf("A box\n");
}
int main() {
void* objects[50];
// Allocates 25 objects of each type for the example
for(int i=0; i<50; i+=2) {
objects[i] = malloc(sizeof(sphere));
// Sets the implementation here
((sphere *)objects[i])->method = sphereMethod;
objects[i+1] = malloc(sizeof(box));
((box *)objects[i+1])->method = boxMethod;
}
// Loops through objects and calls the methods
for(int i=0; i<50; i++) {
hit method = *((hit *)objects[i]);
method(objects[i], NULL, 0, 0, NULL);
}
// Frees them
for(int i=0; i<50; i++) { free(objects[i]); }
}
Here is a rough cut of how you might achieve your objective...
enum { BOX, SPHERE } types;
typedef struct sphere {
point3 center;
double radius;
} sphere_t;
typedef struct box {
point3 center;
double len, wid, dpth;
double rotX, rotY, rotZ;
} box_t;
typedef object {
int type;
union {
sphere_t sphere;
box_t box;
};
} object_t;
int main() {
object_t objs[ 50 ];
/* ... */
for( int i = 0; i < numObjs; i++ )
switch( objs[ i ].type ) {
case BOX:
hndlr_Box( objs[i].box );
break;
case SPHERE:
hndlr_Sphere( objs[i].sphere );
break;
/* ... */
}
}
Looking back at that, it might be even better to use a linked list of objects instead of an array.
AND, each object instance in the array will consume as much memory as is required for the largest of the union. It may be better to use pointers to separately allocated blocks, each storing only its particular attributes.
If you create a sufficiently complicated enough object, you should be able to accomplish something pretty close to your goal. Below, I sketch out a solution that you should be able to build upon.
First, we define a Shape as having a ShapeInterface, which for now is just something that can be hit.
typedef struct HitRecord HitRecord;
typedef struct Ray Ray;
typedef struct Shape {
const struct ShapeInterface * const vtable;
} Shape;
struct ShapeInterface {
bool (*hit)(Shape *, const Ray *, double, double, HitRecord *);
void (*dump)(Shape *);
};
bool shape_hit (Shape *s, const Ray *r, double x, double y, HitRecord *hr) {
return s->vtable->hit(s, r, x, y, hr);
}
void shape_dump (Shape *s) {
s->vtable->dump(s);
}
Then, we define Sphere and Box to be kinds of Shape.
typedef struct Point3 {
double p[3];
} Point3;
typedef struct Sphere {
Shape base;
Point3 center;
double radius;
bool popped;
} Sphere;
typedef struct Box {
Shape base;
Point3 center;
double radius;
bool crushed;
} Box;
Now, define Objects to be a union of these different kinds of Shape. We note that all items in Objects have a common initial sequence, which is Shape.
typedef union Objects {
Shape shape;
Sphere sphere;
Box box;
} Objects;
When you have an array of Objects, you can loop through and call shape_hit on the shape member.
void process_hit_objects (
Objects shapes[],
int number_of_shapes,
const Ray *r, double x, double y, HitRecord *hr) {
for (int i = 0; i < number_of_shapes; ++i) {
if (shape_hit(&shapes[i].shape, r, x, y, hr)) {
/* ... do something ... */
shape_dump(&shapes[i].shape);
}
}
}
When creating a Sphere, it needs to initialize its base member with an appropriate implementations for the functions in its vtable.
static bool sphere_hit (
Shape *shape, const Ray *r, double x, double y, HitRecord *hr) {
Sphere *sphere = (void *)shape;
return sphere->popped;
}
static void sphere_dump (Shape *shape) {
Sphere *sphere = (void *)shape;
double *p = sphere->center.p;
printf("sphere: %p # <%f,%f,%f> |%f|\n",
(void *)sphere, p[0], p[1], p[2], sphere->radius);
}
void makeSphere (Sphere *s, Point3 center, double radius) {
static const struct ShapeInterface vtable = {
sphere_hit, sphere_dump
};
Shape base = { &vtable };
Sphere sphere = { base, center, radius, true };
memcpy(s, &sphere, sizeof(sphere));
}
Similarly for a Box.
static bool box_hit (
Shape *shape, const Ray *r, double x, double y, HitRecord *hr) {
Box *box = (void *)shape;
return box->crushed;
}
static void box_dump (Shape *shape) {
Box *box = (void *)shape;
double *p = box->center.p;
printf("box: %p # <%f,%f,%f> |%f|\n",
(void *)box, p[0], p[1], p[2], box->radius);
}
void makeBox (Box *b, Point3 center, double radius) {
static const struct ShapeInterface vtable = {
box_hit, box_dump
};
Shape base = { &vtable };
Box box = { base, center, radius, true };
memcpy(b, &box, sizeof(box));
}
I have read the book Understanding and Using C Pointers and try to compile the code below. But after compiling I got the warning: assignment from incompatible pointer type.
I have checked the code and find out the function pointer fptrSet and function ShapeSetX is incompatible because the first argument of fptrSet is void * and function ShapeSetX is Shape *.
How can I fix this?
Thanks!
typedef void (*fptrSet)(void*, int);
typedef int (*fptrGet)(void*);
typedef void (*fptrDisplay)();
typedef struct _vfunc
{
fptrSet setX;
fptrGet getX;
fptrSet setY;
fptrGet getY;
fptrDisplay display;
} vFunc;
typedef struct _shape
{
vFunc function;
int x;
int y;
} Shape;
void displayShape(){
printf("Shape\n");
}
void ShapeSetX(Shape *shape, int x){
shape->x = x;
}
void ShapeSetY(Shape *shape, int y){
shape->y = y;
}
int ShapeGetX(Shape *shape){
return shape->x;
}
int ShapeGetY(Shape *shape){
return shape->y;
}
Shape *newShape()
{
Shape *shape = (Shape *)malloc(sizeof(Shape));
shape->x = 10;
shape->y = 10;
shape->function.setX = ShapeSetX;
shape->function.getX = ShapeGetX;
shape->function.setY = ShapeSetY;
shape->function.getY = ShapeGetY;
shape->function.display = displayShape;
return shape;
}
You have to respect pointer definition: pointer need that first parameter is a pointer to void, so your function implementation should have first parameter as void:
void ShapeSetX(void *void_shape, int x){
Shape *shape = (Shape*) void_shape;
shape->x = x;
}
void ShapeSetY(void *void_shape, int y){
Shape *shape = (Shape*) void_shape;
shape->y = y;
}
int ShapeGetX(void *void_shape){
Shape *shape = (Shape*) void_shape;
return shape->x;
}
int ShapeGetY(void *void_shape){
Shape *shape = (Shape*) void_shape;
return shape->y;
}
I was going to say "Why don't you replace void with Shape then?", until I realised that Shape hadn't been defined yet - nor could you swap the two definitions, because Shape needs vFunc which needs the typedefs.
So, do this:
typedef struct _shape Shape; // Define _shape and Shape later
typedef void (*fptrSet)(Shape*, int);
typedef int (*fptrGet)(Shape*);
typedef void (*fptrDisplay)();
If your compiler doesn't like that, you may need to change it to:
typedef struct _shape; // Define _shape later
typedef void (*fptrSet)(struct _shape*, int);
typedef int (*fptrGet)(struct _shape*);
typedef void (*fptrDisplay)();
To explain more, I have two structures-'first' and 'second' having common variables 'jack' and 'jill'. I want to print jack via a pointer based on if-else condition.
I understand at the time of printing I have to typecast the void pointer. But whether the pointer points to struct a or b is decided on run time.
It is a basic C code. How to overcome this?
Code
#include <stdio.h>
int main(void)
{
typedef struct one
{
int jack;
float jill;
}a;
typedef struct two
{
int jack;
float jill;
char something;
int something1;
}b;
a first;
b second;
void *z;
if(1)
{
a* z;
z = &first;
printf("First one");
}
else
{
b* z;
z = &second;
printf("Second one");
}
printf("%d\n", z->jack);
return 0;
}
Error
prog.c:36:17: warning: dereferencing 'void *' pointer printf("%d\n", z->jack); prog.c:36:17: error: request for member 'jack' in something not a structure or union
You get a compiler warning since the compiler does not understand z->jack since z is a void * (note that the declarations a* z and b* z are not valid outside the scope of the if and else block).
To overcome this you can use a function printJack as shown in the following listing:
#include <stdio.h>
typedef struct one
{
int jack;
float jill;
}a;
typedef struct two
{
int jack;
float jill;
char something;
int something1;
}b;
void printJack(void *pStruct, int type)
{
switch (type)
{
case 1:
printf("jack: %d\n", ((a *)pStruct)->jack);
break;
default:
printf("jack: %d\n", ((b *)pStruct)->jack);
break;
}
}
/*
** main
*/
int main(void)
{
a first;
b second;
void *z;
first.jack = 5;
second.jack = 4892;
printJack(&first, 1);
printJack(&second, 0);
z = &first;
printJack(z, 1);
return (0);
}
I've written code like this often and experienced a lot of trouble with it. Not at the time of implementing, since you are knowing what you are typing at that moment but let's say a few years later if you need to extend your code. You will miss a few places where you cast from void * to a * or b * and you'll spend a lot of time debugging what's going on...
Now I'm writing things like this in the following way:
#include <stdio.h>
typedef struct header
{
int jack;
float jill;
} h;
typedef struct one
{
struct header header;
/* what ever you like */
}a;
typedef struct two
{
struct header header;
char something;
int something1;
/* and even more... */
}b;
void printJack(void *pStruct)
{
printf("jack: %d\n", ((struct header *)pStruct)->jack);
}
/*
** main
*/
int main(void)
{
a first;
b second;
void *z;
first.header.jack = 5;
second.header.jack = 4892;
printJack(&first);
printJack(&second);
v = &first;
printJack(v);
return (0);
}
As you've noticed I have declared a new struct header which covers the the common parts of struct one and struct two. Instead of casting the void * to either a * or b * a "common" cast to struct header * (or h *) is done.
By doing so you can easily extend the "common attribtues" of the structs or you can implement further structs using this header and function printJack still will work. Additionally there is no need for attribute type anymore making is easier to call printJack. You can even change the type of jack without needing to change it in various places within your code.
But remember that struct header needs to be the first element of the structs you use this mechanism. Otherwise you will end up with a few surprises since you are using memory which does not contain the data of the struct header...
I try to write a function, that finds void pointers in a data structure. The function has to cast the void* to any kind of struct.
Let's say I write a struct, which I store in my data sturcture in form of a void pointer. Then I calls the function, which prints information of all stored data elements.
To do that the function has to know to which type it should cast.
So my question is: Is it possible to give the function the informations it needs in form of a parameter somehow?
example code:
typedef struct{
int a, b
} teststruct;
void DSOut(datastructure* ds, datatypeinfo dt){
//...
//search for data in ds
//...
//if data is found cast it to the type defined in dt
//and print out the a and b fields
}
int main(){
datastructure* ds = DSCreate(4, 3); //can hold any type of data,
//but should hold just one at a time
//4 and 3 are just example parameters
teststruct ts;
ts.a = 4;
ts.b = 10;
teststruct ts2;
ts2.a = 6;
ts2.b = 12;
//Add the teststructs to the data-structure
DSAdd(2, 2, ts); //the numbers are just for example
DSAdd(4, 1, ts2);
datatypeinfo dt = teststruct; //stores the type teststruct for DSOut
DSOut(ds, dt); //function, that prints information of all added teststructs
return 0;
}
in this example DSOut(x,y) should print the following:
- on position 2, 2 is an element which holds following data: 4, 10.
- on position 4, 1 is an element which holds following data: 6, 12.
Do you think this is possible ?
Types cannot be passed as parameters in C, so the short answer to your question is "no, it cannot be done", at least not in the general case. You could pass something that would allow you to identify one of a limited set of types, and then hard-code how to handle each of those types (I'm thinking of a big switch statement). Since you don't specify what datatypeinfo looks like, it isn't clear how general you expect it to be.
I can think of adding a type identifier field to your struct and check it's value to decide how to print it, and initialize the structs with functions to take care of the type field
enum Types {
Point3D,
Point2D
};
struct Base {
enum Types type;
};
struct Point3D {
enum Types type;
int x;
int y;
int z;
};
struct Point2D {
enum Types type;
int u;
int v;
};
void print(void *data)
{
switch (((struct Base *)data)->type)
{
case Point2D:
{
struct Point2D *point;
point = (struct Point2D *)data;
printf("2D: %d, %d\n", point->u, point->v);
}
break;
case Point3D:
{
struct Point3D *point;
point = (struct Point3D *)data;
printf("3D: %d, %d, %d\n", point->x, point->y, point->z);
}
break;
}
}
void initialized2dPoint(struct Point2D *const point, int u, int v)
{
if (point == NULL)
return;
point->type = Point2D;
point->u = u;
point->v = v;
}
void initialized3dPoint(struct Point3D *const point, int x, int y, int z)
{
if (point == NULL)
return;
point->type = Point3D;
point->x = x;
point->y = y;
point->z = z;
}
int main(void)
{
struct Point2D point2d;
struct Point3D point3d;
initialized2dPoint(&point2d, 1, 2);
initialized3dPoint(&point3d, 3, 4, 5);
print(&point2d);
print(&point3d);
return 0;
}
am building a simple program dealing with structs. The logic is simple enough but for some reason I can't quite figure out how to pass the struct to a function. I declared the struct in my main(). I got the impression from searching the site that the only way to do this is to create a header file and declare that way. Is this true?
main() {
struct Rect {
double x;
double y;
char color;
double width;
double height;
};
struct Rect a, b, *rec;
and this is where I am trying to pass it:
int chk_overlap(struct Rect *r1, struct Rect *r2) {
if(((r1->x + r1->width) >= r2->x) && ((r1->y) >= (r1->y - r2->height))){
return 1;
} else {
return 0;
}
}
This is just one iteration of my attempt, when I pass it like this, I get a dereferencing to incomplete pointer error. I have also tried declaring it as
typedef struct Rect {
double x;
double y;
char color;
double width;
double height;
} Rect;
Rect a, b, *rec;
passing it as
int chk_overlap(Rect *r1, Rect *r2) {
EDIT: This is where I am actually using the function
int check = 0;
check = check + chk_overlap(&a, &b);
You should declare the structure before the main.
struct Rect {
double x;
double y;
char color;
double width;
double height;
}
/* Place here the function definitions */
int main (int argc, char *argv[]) {
struct Rect a, b, *rec;
...
}
/* place here the function code */
Since you're declaring it inside the main() it's not seen outside, so functions don't recognise it.
Other than that, the way you're calling the function (function(&a, &b)) looks correct.