I have these structures which hold data for circle and rectangle as their name suggests.
typedef struct _circledata{
FCOORD center;
float radius;
BOOL filled;
COLORREF color;
FCOORD *points;
}shapeCircle;
typedef struct _rectdata{
FCOORD start;
FCOORD end;
FCOORD points;
float length,breadth;
BOOL filled;
COLORREF color;
}shapeRect;
typedef enum {Circle, Rect, Triangle} objlist;
Now I have this code working to check if the mouse pointer is hovering in the area of the circle or rectangle. The isMouseHovering() function has the arguments ; pointer -> whether it is of circle or of rectangle , enum -> shape enum number
//In WinMain I initialize two circles
shapeCircle newCirc[2];
//gave them values
for(int i=0;i<2;i ){
if(isMouseHovering( //have to send pointer to struct and enum value ){
//I tried like this
//if(isMouseHovering((void*)newCirc i,Circle))
newCirc[i].color=red;
newCirc[i].radius=55;
//Do stuffs
}else{
//Do something else
}
In isMouseHovering() I have this type of code,
BOOL isMouseHovering(void *obj,int objnum){ //I did like this and tried to typecast the pointer
int val=0;
switch(objnum){ //diverting the checking code based on shape type
case Circle:
return checkCircleHover( (shapeCircle*) obj ); //sending the pointer of struct
break;
case Rect:
/* * * * */
break;
case Triangle:
/* * * * */
break;
default:
/* * * * */
return -1;
}
return 0;
}
So you could see what exactly I be trying to achieve. I need to send any structure pointer to just one function which then will check the shape and transfer it to the right function. I know my method is wrong as it does not work. So is there any workaround for such methods.
Thank you.
Sammy1410
CodePudding user response:
There is nothing in particular wrong with your isMouseHovering()
function. To the extent that any of the code you have actually presented has or implies an error, it is in the commented-out call to that function:
//if(isMouseHovering((void*)newCirc i,Circle))
Like all unary operators, typecast operators have higher precedence than
, so the expression (void*)newCirc i
is equivalent to ((void*)newCirc) i
. But you cannot perform pointer arithmetic on pointers to void
. If your compiler accepted that anyway then it most likely performed the arithmetic as if the pointer were a char *
, which would be a disaster in this case. If you are not getting a warning for that code then turn up your compiler's warning level.
Part of the issue is that you are performing unneeded casts. Object pointers are converted automatically to and from type void *
across assignments and as function arguments, so although you could write the above call as ...
// ok
isMouseHovering((void*)(newCirc i), Circle)
... it would be better to omit the cast altogether:
// better
isMouseHovering(newCirc i, Circle)
Similarly, in isMouseHovering()
, you do not need to cast the void *
to the specific pointer type accepted by the shape-specific functions. I would omit that cast too, on the basis that casts other than for arithmetic purposes have bad code smell. However, that one at least has some documentary value.
With that said, I think you're likely to find the need to specify the object type on every call to be inconvenient at best. If you're always going to be working in terms of pointers to specific shape types, then you can replace your isMouseHovering()
function with a generic-selection macro:
#define isMouseHovering(s) _Generic((s), \
shapeCircle*: checkCircleHover(s), \
shapeRect*: checkRectHover(s), \
shapeTriangle*: checkTriangleHover(s),
default: -1)
That will produce function calls appropriate for the type of the pointer without requiring a discriminator value, but of course, it will not work as desired if the pointer type does not (correctly) convey the type of the pointed-to object, such as if you have converted to type void *
.
On the third hand, I anticipate that you may discover a need to write functions that accept mixeded shapes, or where you want to pass shape pointers through multiple levels of function call before you get to the point where you discriminate based on the specific shape type. You have two reasonably good options for this:
Create a wrapper structure that contains the shape type code and a pointer to a specific shape. This is described more fully in @zakk's answer, and I don't feel a need to add anything to that. OR
Create a tagged union type that can represent any of your shapes. That would involve modifying your shape structures slightly to accommodate, by adding a type code as the first member. Example:
typedef enum {Circle, Rect, Triangle} objlist; typedef struct _circledata{ objlist type; FCOORD center; float radius; // ... } shapeCircle; typedef struct _rectdata{ objlist type; // ... } shapeRect; typedef struct _triangledata{ objlist type; // ... } shapeTriangle; typedef union { struct { objlist type; }; shapeCircle asCircle; shapeRect asRect; shapeTriangle asTriangle; } anyShape;
Then you work primarily with objects of type
anyShape
and / or with pointers to that type, which, as the name implies, can represent any shape. When you need to know specifically what kind of shape a particular instance represents, the information is right there in the object, no matter what shape it represents at the time.
CodePudding user response:
You can define an enum
for all your shapes:
enum ShapeType {SHAPE_CIRCLE, SHAPE_RECTANGLE, SHAPE_TRIANGLE}; // You can add more
And then, define a generic struct that has the type as well as a pointer to your shape:
struct shape {
enum ShapeType shape_type;
void *object;
};
Now, there's no need to provide a second argument to isMouseHovering()
:
BOOL isMouseHovering(void *obj)
{
struct shape *shape = obj;
switch(shape->shape_type) {
case SHAPE_CIRCLE:
return checkCircleHover((shapeCircle*)shape->object);
break;
case SHAPE_RECTANGLE:
/* * * * */
break;
case SHAPE_TRIANGLE:
/* * * * */
break;
default:
/* * * * */
return -1;
}
return 0;
}
And your checkCircleHover()
will accept a shapeCircle*
, because if you call it, you are sure you are passing a circle to it:
BOOL checkCircleHover(shapeCircle *circle)
{
// ...
}