# Help creating a macro to forward operator overloads \[SOLVED I GUESS?\]

**URL:** https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925
**Category:** Haxe
**Tags:** macro
**Created:** [April 6, 2023, 7:00pm UTC](https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925 "2023-04-06T19:00:42Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![kvbc](https://community.haxe.org/user_avatar/community.haxe.org/kvbc/32/2153_2.png) [@kvbc](https://community.haxe.org/u/kvbc)
#### Post date: [April 6, 2023, 7:00pm UTC](https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925/1 "2023-04-06T19:00:42Z")

</div>

Consider the following code

```haxe
package;

@:transitive
abstract A(Int) from Int to Int {
    @:op(A + B)
    private function add (other: A): A {
        return (this: Int) + (other: Int);
    }
}

// here something like @:forwardOps(A) ?
abstract B(A) from A to A {
    
}

class Main {
	static function main() {
		var a: B = 3;
        var b: B = 6;
        var c: B = a + b; // error !!!
	}
}

```

How would I create a macro to automatically generate operator overloads for class `B` that call operator overloads of class `A`?  
For example, to achieve this effect without a macro I would have to copy & paste the following over my entire codebase

```haxe
abstract B(A) from A to A {
    @:op(A + B)
    private function add (other: B): B
        return (this: A) + (other: A);
}

```

The above code works, but this all just comes down to type casting.  
So I’m wondering… is there a way to automate this process with a macro?

---

<div class="post-metadata">

### Author: ![kvbc](https://community.haxe.org/user_avatar/community.haxe.org/kvbc/32/2153_2.png) [@kvbc](https://community.haxe.org/u/kvbc)
#### Post date: [April 7, 2023, 11:35am UTC](https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925/2 "2023-04-07T11:35:53Z")

</div>

Ok i did it

```haxe
// ForwardOpsMacro.hx

package;

import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.Expr;

class ForwardOpsMacro {
    macro static public function build (localTypePath: String, baseTypePath: String): Array<Field> {
        function assertAbstract (type: Type): Void {
            if (!type.match(TAbstract(_,_)))
                Context.error('type $type expected to be an Abstract', Context.currentPos());
        }

        function getAbstractRef (type: Type): Ref<AbstractType> {
            assertAbstract(type);
            return switch (type) {
                case TAbstract(ref, _):
                    ref;
                case _: throw "Error"; // should be an abstract type by now
            }
        }

        var localComplexType: ComplexType = {
            var localType: Type = Context.getType(localTypePath);
            assertAbstract(localType);
            Context.toComplexType(localType);
        }

        var baseType: Type = Context.getType(baseTypePath);
        var baseComplexType: ComplexType = {
            assertAbstract(baseType);
            Context.toComplexType(baseType);
        }
        var baseAbstractRef: Ref<AbstractType> = getAbstractRef(baseType);

        var fields = Context.getBuildFields();

        for (staticField in baseAbstractRef.get().impl.get().statics.get()) {
            if (!staticField.meta.has(":op"))
                continue;

            var newArgs = new Array<FunctionArg>();

            var type: Type = staticField.type;
            // Extract the actual type if the type is TLazy
            type = switch(type) {
                case TLazy(_ => _() => actualType):
                    actualType;
                case _: type;
            }

            var args = switch (type) {
                case TFun(__args, _):
                    __args;
                case _: throw "Error"; // static fields with @:op should always be functions
            }

            // skip the first argument - this
            for (i in 1 ... args.length) {
                var arg = args[i];
                var isBaseType: Bool = (getAbstractRef(arg.t).toString() == baseAbstractRef.toString());

                var newArgType: ComplexType = if (isBaseType) {
                    localComplexType;
                } else {
                    Context.toComplexType(arg.t);
                }

                newArgs.push({
                    name: arg.name,
                    opt: arg.opt,
                    type: newArgType
                    // value?
                    // meta?
                });
            }

            var name = staticField.name;
            var newFunc: Function = {
                args: newArgs,
                ret: localComplexType,
                expr: macro return this.$name(other)
                // params?
            }

            fields.push({
                name: staticField.name,
                doc: staticField.doc,
                access: [staticField.isPublic ? APublic : APrivate], // inherit access modifier
                kind: FFun(newFunc),
                pos: staticField.pos,
                meta: staticField.meta.get()
            });
        }

        return fields;
    }
}

```

Usage:

```haxe
// Main.hx
package;

@:transitive
abstract A(Int) from Int to Int {
    @:op(A + B)
    public function add (other: A): A {
        return (this: Int) + (other: Int);
    }
    
    @:op(A - B)
    public function sub (other: A): A {
        return (this: Int) - (other: Int);
    }
}

@:transitive
@:build(ForwardOpsMacro.build("B", "A"))
abstract B(A) from A to A {}

@:build(ForwardOpsMacro.build("C", "B"))
abstract C(B) from B to B {}

class Main {
	static function main() {
        var a: C = 68;
        var b: C = 1;
        var c: C = a + b;
        trace(c);
	}
}

```

---

<div class="post-metadata">

### Author: ![kvbc](https://community.haxe.org/user_avatar/community.haxe.org/kvbc/32/2153_2.png) [@kvbc](https://community.haxe.org/u/kvbc)
#### Post date: [April 8, 2023, 6:00pm UTC](https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925/3 "2023-04-08T18:00:39Z")

</div>

Well, as it turns out, it’s not that simple.  
The build order is unspecified, so… that’s a problem.  
But I’ll be updating the macro here [GitHub - kvbc/haxe-macros: my haxe macros library](https://github.com/kvbc/haxe-macros/)

---

<div class="post-metadata">

### Author: ![player\_03](https://community.haxe.org/user_avatar/community.haxe.org/player_03/32/821_2.png) [@player\_03](https://community.haxe.org/u/player_03)
#### Post date: [April 13, 2023, 3:27am UTC](https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925/4 "2023-04-13T03:27:15Z")

</div>

Yeah, neither macro should rely on the other having run. If C happens to be built before B, drill down all the way to A and get the data from there.

By the way, you can simplify a bit. Haxe already knows how to add two integers, so all you need to do is tell it it’s allowed to do so.

```haxe
@:transitive
abstract A(Int) from Int to Int {
    @:op(A + B)
    private function add (other: A): A;
}

abstract B(A) from A to A {
    @:op(A + B)
    private function add (other: B): B;
}

```

---

<div class="post-metadata">

### Author: ![kvbc](https://community.haxe.org/user_avatar/community.haxe.org/kvbc/32/2153_2.png) [@kvbc](https://community.haxe.org/u/kvbc)
#### Post date: [April 23, 2023, 6:03pm UTC](https://community.haxe.org/t/help-creating-a-macro-to-forward-operator-overloads-solved-i-guess/3925/5 "2023-04-23T18:03:25Z")

</div>

Yup, “drilling” is exactly what I did in my updated version.

- If **B** has been built before **C** ===\> There’s no problem. **B** took from **A** before **C** could build and take from **B** , so **B** already has **A**. In the end **C** has both **B** and **A**
- Otherwise, if **C** has been built before **B** ===\> **C** takes from **B** , and since it knows **B** ’s underlying type ( **A** ) and **B** has not yet been built - it takes from **B** ’s underlying type ( **A** )

Examples:

- Build order: **A** , **B** , **C** (most desirable)
  - A \< Int
  - B \< A
  - C \< B

- Build order: **D** , **C** , **B** , **A** (insanity)
  - D \< C — _“drills” through its underlying types, since **C** has not yet been built_
    - D \< B
    - D \< A

  - C \< B
    - C \< A

  - B \< A
  - A \< Int

I’ve built-in the visualization of this whole process in the updated macro in the github repository I’ve posted right above

# [Try it out online](https://try.haxe.org/#48F16312)

Example compiler output for the build order of **C** , **B** , **A**

 ![obraz](https://community.haxe.org/uploads/default/original/2X/b/bf9f305d5247ede7b22ca81f777f3a2c0be0e004.png)
