Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Diamond inheritance only for type checking

I am working on an implementation of geometric algebra's. Here is a minimized example of what I have:

template<int p,int q,int r>
struct geometric_algebra{
    static const int d=p+q+r, e=1<<d;
    struct multivector {
        multivector operator|(multivector w){/*...*/}
        multivector operator&(multivector w){/*...*/}
        /*...*/
    };

    template<int g> struct graded_vector : multivector {
        template<int h>
        graded_vector<g+h> operator|(graded_vector<h>w) {
            return {multivector::operator|(w)};
        }
        template<int h>
        graded_vector<g+h-d> operator&(graded_vector<h>w) {
            return {multivector::operator&(w)};
        }
        /*...*/
    };
};

As you can see the graded_vector specializes a multivector, but does not reimplement anything. The primary reason for its existence is to have more strict type checking. A graded vector corresponds to a certain dimension of geometric object. And I want to use the type checker as a sanity check on these objects

using pga = geometric_algebra<3,0,1>
using point = pga::graded_vector<1>;
using line = pga::graded_vector<2>;
using plane = pga::graded_vector<3>;
plane a = /*...*/
line l = /*...*/
// the intersection of a plane and line is a point
point z = a & l;
line m = a & l; // this should give a compiler error

In conformal geometric algebra (geometric_algebra<4,1,0>) there is a separate distinction between round and flat objects. I also want this distinction to be type-checked. If I want to continue my current approach, I would need to add the following classes: flat_vector and flat_graded_vector. This introduces two problems:

  • diamond inheritance - flat_graded_vector inherits multivector twice via graded_vector and flat_vector.
  • Since the operations are overloaded in both graded_vector and flat_vector, it also needs to be overloaded in flat_graded_vector that implements the type logic of both.

Is there a better way to achieve this?

Using composition rather than inheritance (as in Diamond inheritance (C++)) does not seem to fit as a multivector is more than the product of whether it is graded and whether it is flat. I also don't think mix-ins work as that would require function overloading, which is not possible if the signature is different. Furthermore, graded<g,flat<mv>> is conceptually the same as flat<graded<g, mv>> but the type checker will not understand that.

MWE:

template<int p,int q,int r>
struct geometric_algebra{
    static const int d=p+q+r, e=1<<d;
    struct multivector {
        int data; //stand-in
        multivector operator|(multivector w){
            return {data | w.data}; //stand-in
        }
        multivector operator&(multivector w){
            return {data & w.data}; //stand-in
        }
        /*...*/
    };

    template<int g> struct graded_vector : multivector {
        template<int h>
        graded_vector<g+h> operator|(graded_vector<h>w) {
            return {multivector::operator|(w)};
        }
        template<int h>
        graded_vector<g+h-d> operator&(graded_vector<h>w) {
            return {multivector::operator&(w)};
        }
        /*...*/
    };
};

using pga = geometric_algebra<3,0,1>;
using multivector = pga::multivector;
using point = pga::graded_vector<1>;
using line = pga::graded_vector<2>;
using plane = pga::graded_vector<3>;
point make_pt(double x,double y,double z){
    return {1}; // stand-in
}

int main() {
    point u = make_pt(0,0,1);
    point v = make_pt(0,1,0);
    point w = make_pt(1,0,0);
    plane a = u | v | w;
    point x = make_pt(0,0,0);
    point y = make_pt(1,1,1);
    line l = x | y;
    point z = a & l;
}
like image 591
Jelmer Firet Avatar asked Oct 24 '25 15:10

Jelmer Firet


1 Answers

The crucial insight that solved the problem for me was that I did not need inheritance, I needed conversion.

Thank you to @Sebastian and @Jarod42 for inspiration

For reference, here is a MWE:

template<int p,int q,int r>
struct geometric_algebra{
    static const int d=p+q+r, e=1<<d;

    struct mvprop {
        bool graded = false;
        int grade = 0;
        bool flat = false;
        constexpr bool operator<=(const mvprop other) const {
            return (graded <= other.graded) && (flat <= other.flat);
        }
        constexpr mvprop operator|(mvprop other) const {
            return {graded && other.graded, grade + other.grade, flat || other.flat};
        }
        constexpr mvprop operator&(mvprop other) const {
            return {graded && other.graded, grade + other.grade - d, flat && other.flat};
        }
    };

    template<mvprop g>
    struct multivector {
        int data; //stand-in
        template<mvprop h>
        multivector<g|h> operator|(multivector<h> w){
            return {data | w.data}; //stand-in
        }
        template<mvprop h>
        multivector<g&h> operator&(multivector<h> w){
            return {data & w.data}; //stand-in
        }
        template<mvprop h> requires (h <= g)
        operator multivector<h>() {return {data};}
        /*...*/
    };
};

using cga = geometric_algebra<4,1,0>;
using mv = cga::multivector<{false, 0, false}>;
using flat = cga::multivector<{false, 0, true}>;
using rpoint = cga::multivector<{true, 1, false}>;
using dipole = cga::multivector<{true, 2, false}>;
using circle = cga::multivector<{true, 3, false}>;
using sphere = cga::multivector<{true, 4, false}>;
using rpoint_inf = cga::multivector<{true, 1, true}>;
using point = cga::multivector<{true, 2, true}>;
using line = cga::multivector<{true, 3, true}>;
using plane = cga::multivector<{true, 4, true}>;

rpoint make_rpt(double x,double y,double z, double r=0){
    return {1}; // stand-in
}
rpoint_inf make_inf() {
    return {1}; // stand-in
}

int main() {
    sphere s1 = make_rpt(0,0,1) | make_rpt(0,1,0) | make_rpt(1,0,0) | make_rpt(1,1,1);
    sphere s2 = make_rpt(1,0,1) | make_rpt(1,1,0) | make_rpt(2,0,0) | make_rpt(2,1,1);
    plane a = make_inf() | make_rpt(0,0,0) | make_rpt(1,0,0) | make_rpt(0,1,0);
    line l = make_inf() | make_rpt(0,0,0) | make_rpt(0,0,1);
    circle c = s1 & s2;
    point r = a & l;
    dipole d = c & a;
}
like image 93
Jelmer Firet Avatar answered Oct 26 '25 04:10

Jelmer Firet