0

I did check the internet about it, but some uncertainty remains.

Struct Inheritance in C

Casting one struct pointer to other - C

http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

(What I read about libraries' binary compatibility was specific to C++)

There might be a solution to my problem but would require me to deeply modify my existing math & geometry library : Typesafe inheritance in C

Context

In C, the ability to cast a pointer to compatible types is handy, for example :

typedef struct  s_pnt2
{
    t_real  x;
    t_real  y;
}               t_pnt2;

typedef struct  s_pnt3
{
    t_real  x;
    t_real  y;
    t_real  z;
}               t_pnt3;

(Edited) Allows one to cast a 3D point into a 2D point (As R^2 is isomorphic to any hyperplane of R^3)

Furthermore :

typedef union   u_vec2
{
    t_pnt2  carthesian;
    t_real  matrix[2];
    struct
    {
        t_real  rep;
        t_real  imp;
    }       rectangular;
    struct
    {
        t_real  mod;
        t_real  arg;
    }       polar;
}               t_vec2;

typedef union   u_vec3
{
    t_pnt3  carthesian;
    t_real  matrix[3];
    struct
    {
        t_real  rho;
        t_real  theta;
        t_real  phi;
    }       spherical;
    struct
    {
        t_real  r;
        t_real  theta;
        t_real  z;
    }       cylindrical;
}               t_vec3;

Those unions allows one to explicitly express a point's coordinates in various systems, while the space used is no greater and most important, the compatibility remains.

While implementing functions converting a given vector from a system to another, I stumble upon the idea that storing the current system would be a good point. So I created an enum :

typedef enum    e_type
{
    CARTHESIAN,
    CYLINDRICAL,
    SPHERICAL,
    POLAR = CYLINDRICAL,
    RECTANGULAR = CARTHESIAN
}               t_type;

Problem

I must now store a variable of type t_type (int), and face a problem : either I put it at the end, and loose compatibility between dimensions : Edit :

+---+         +---+
| x | matches | x |
+---+         +---+
| y | matches | y |
+---+         +---+
| t | doesn't | z |
+---+         +---+
              | t |
              +---+

or put it at the beginning (Just like X11 with XEvent), and loose compatibility with matrices, and (Edit) the ability to declare a vector like :

t_vec3 vector = (t_vec3){{x, y, z}}

Would there be a better way ?

Even if I don't require it in this instance, I would be interested if there's a way to keep the binary compatibility.

More generally, any advice on good habits are welcomed !

Thanks for your time

Edit : @llja : Yes I could do something like

typedef struct  s_vec2t
{
    t_type  type;
    t_vec2  p;
}               t_vec2t

The downsides would be :

  1. To access some data I should do something like :

    t_vec2t point = (t_vec2t){CARTHESIAN, (t_vec2){{x, y}}}
    point.p.carthesian.x;
    
  2. Meaning I must modify all my functions using points so that they now use typed points ?

Community
  • 1
  • 1
  • Wrap your unions in a struct that has the type as the first member, union 2.? Did not totally get what you meant with losing compat with dimensions or matrices. – Ilja Everilä Mar 26 '16 at 22:03
  • 1
    "Allows one to cast a 2D point into a 3D point" ?? "and loose compatibility with matrices" ?? – Karoly Horvath Mar 26 '16 at 22:32
  • @KarolyHorvath : "Allows one to cast a 2D point into a 3D point" Yes, when manipulating pointers. –  Mar 26 '16 at 22:35
  • @Xxdzs ...if said "2D point" was actually a 3D point to begin with, or at least accessing "z" would cause UB. – Ilja Everilä Mar 26 '16 at 22:36
  • If the question is, "can I completely change the datatypes used in my library, and then continue to use my library exactly as I did before and pretend that I didn't make those changes?" then the answer is: "no". If you don't want to "modify all [your] functions using points so that they now use typed points", then why do you want to add a type in the first place, if you don't intend to modify your functions to do something with that information? – Crowman Mar 26 '16 at 22:37
  • @PaulGriffiths : Well most of my functions don't require this piece of information but some do.And my question is : can I avoid changing completely my datatype ? –  Mar 26 '16 at 23:16
  • @Ilja : Yes, sorry about that, I got confused while trying to explain. I actually meant from 3D to 2D, but 2D would be the "base type" –  Mar 26 '16 at 23:22

1 Answers1

0

Most of what you can do by inheritance, you can do with composition instead. In fact c++ compilers like clang++ internally use composition to realise inheritance, which you will see when debugging.

#include <stdio.h>

struct point_1d {
    double x;
};

void point_1d_print(struct point_1d const * const point) {
    printf("x=%lf", point->x);
}

struct point_2d {
    struct point_1d p;
    double y;
};

void point_2d_print(struct point_2d const * const point) {
    point_1d_print(&point->p);
    printf(", y=%lf", point->y);
}

struct point_3d {
    struct point_2d p;
    double z;
};

void point_3d_print(struct point_3d const * const point) {
    point_2d_print(&point->p);
    printf(", z=%lf", point->z);
}

void switch_xy(struct point_2d * point) {
    double const tmp = point->p.x;
    point->p.x = point->y;
    point->y = tmp;
}

int main(void) {
    struct point_3d point;
    // Direct access
    point.p.p.x = 1.5;
    point.p.y = 2.0;
    point.z = 3.0;
    // Pretend polymorphism
    switch_xy(&point.p);
    point_3d_print(&point);
    printf("\n");
    return 0;
}

Of course you can compose multiple structs to emulate multiple inheritance. Deriving a pointer to the parent (point_3d → point_2d → point_1d) is simple, there is no safe way to go back from a point_1d to a point_2d, though.

kamikaze
  • 1,529
  • 8
  • 18
  • Thanks for the quick answer, clear explanation and detailed example ! –  Mar 26 '16 at 23:27
  • OK you mean there is no other way –  Mar 26 '16 at 23:30
  • @Xxdzs You can do runtime polymorphism with function pointers, but unless your use case requires that, don't bother with the overhead. – kamikaze Mar 27 '16 at 19:08
  • OK thx I'll keep that in mind :) but seriously as I had matrices containing quaternions containing 3D vectors I must now use macros extensively :/ so there is quite an overhead already... –  Mar 31 '16 at 14:57