I'm writing a simple 2d vector object. It will have x and y components, and length, cross product, etc. methods. The thing is, I want to have the structure to have many possible types (char, int, float, double, etc.). I was wondering what would be the best choice, design wise, to interact with the object? Here's what I'm currently considering:
1. Have the user pass the vector object to specialized functions, like:
Vector2Dt_Dot(Vec2Dt* vector1, Vec2Dt* vector2);
where 't' is the type of the vector. However, the problem with this approach is that it disallows different types from interacting with each other, so I couldn't say calculate the dot product of a float vector2d and a double vector2d. A second approach, and what I'm leaning towards:
2. Have the user pass the vector object(s) as void pointers, along with their types, like:
Vector2D_Dot(void* vector1, unsigned vector1_type, void* vector2, unsigned vector2_type);
obviously this approach is more compact API-wise and also solves the above problem, but at the cost of a few extra parameters and type safety.
There may be other solutions that I'm not aware of, however these are the ones I'm currently considering. What do you feel is the best approach to this?
There's no intrinsic support for polymorphism in C, but there are design patterns, using function pointers, base 'class' (structure) casts, etc., that can provide a logical equivalent of dynamic dispatch.
Structures don't support inheritence and runtime polymorphism is achived using the concept virtual functions on base and derived class. Since, Structures don't support inheritence therefore it can't support run time polymorphism.
Polymorphism as a feature of object-oriented languages is not available in C. Neither are encapsulation and inheritance - the language does not have the corresponding features.
Contrary to what younger developers, or people coming from C believe at first, a struct can have constructors, methods (even virtual ones), public, private and protected members, use inheritance, be templated… just like a class .
What you can do is to use polymorphic objects. Define the structures like this:
#define INT_TYPE 0
#define DOUBLE_TYPE 1
//more type constants
typedef struct Vector2D {
int type;
} Vector2D;
typedef struct Vector2D_int {
Vector2D super;
int x, y;
} Vector2D_int;
typedef struct Vector2D_double {
Vector2D super;
double x, y;
} Vector2D_double;
//more typed vector structures
Then you can write your functions to accept Vector2D
pointers, inspect their respective type fields and cast them down to the appropriate typed variant to access the payload data.
double Vector2D_length(const Vector2D* vector) {
if(vector->type == TYPE_INT) {
const Vector2D_int* intVector = (Vector2D_int*)vector;
return sqrt(intVector->x * intVector->x + intVector->y * intVector->y);
}
if(vector->type == TYPE_DOUBLE) {
const Vector2D_double* doubleVector = (Vector2D_double*)vector;
return sqrt(doubleVector->x * doubleVector->x + doubleVector->y * doubleVector->y);
}
//other cases follow
}
This is polymorphism coded by hand. All you need to ensure, is, that the type
field is always set to the correct value (set once when a typed vector is created).
The advantage of this approach to your second idea, is that you do not have to pass around the type of the vectors in another variable which would make using your vectors tedious and error prone.
As an alternative, you can define your type
field to contain a pointer to a structure of function pointers. You would create one object of this function pointer structure per typed Vector type you define, and use it to lookup which function to use with the given vector. This approach would be very close to what C++ does under the hood.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With