Written by

Senior Cloud Architect at InterSystems
MOD
Question Eduard Lebedyuk · Dec 25, 2017

How can I redefine marco and recompile code for subclass

Let's say I have Macro.Parent class:

Include Parent
Class Macro.Parent
{

ClassMethod Test()
{
    write "Class: " _ $classname() _ $c(10,13) _ "Value: " _ $$$name
}

}

which references Parent.inc macro name:

#define name "Parent"

Now, I want in my subclass Macro.Child to have Test method with the same code, but to redefine value of name macro.

Currently I have Child.inc with macro name:

#define name "Child"

And Macro.Child class:

Class Macro.Child Extends Macro.Parent
{

ClassMethod Test() [ CodeMode = objectgenerator ]
{
    set parent = ##class(%Dictionary.MethodDefinition).IDKEYOpen(%class.Super, %method.Name)
    do %code.WriteLine(" #include Child")
    do %code.CopyFrom(parent.Implementation)
}

}

So the idea is to have a subclassed method generator that injects the new macro value. Here's how that works:

>do ##class(Macro.Parent).Test()
Class: Macro.Parent
Value: Parent
>do ##class(Macro.Child).Test()
Class: Macro.Child
Value: Child

But that approach has several drawbacks, such as:

  • Need to write a method for each method I want to redefine
  • Seems rather crude

Is there any better way to achieve that? I can't modify Macro.Parent class or Parent.inc macro.

Comments

Vitaliy Serdtsev · Dec 26, 2017

Macro.Parent.cls:

<FONT COLOR="#000080">Include </FONT><FONT COLOR="#ff0000">Parent

</FONT><FONT COLOR="#000080">Class Macro.Parent </FONT><FONT COLOR="#000000">{

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Test() {   </FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#008000">"Class: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$classname</FONT><FONT COLOR="#000000">() , ! , </FONT><FONT COLOR="#008000">"Value: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$$$name </FONT><FONT COLOR="#000000"> }

}</FONT>

Macro.Child.cls:
<FONT COLOR="#000080">Include </FONT><FONT COLOR="#ff0000">Child

</FONT><FONT COLOR="#000080">Class Macro.Child Extends Macro.Parent </FONT><FONT COLOR="#000000">{

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Test() {   </FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#008000">"Class: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$classname</FONT><FONT COLOR="#000000">() , ! , </FONT><FONT COLOR="#008000">"Value: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$$$name </FONT><FONT COLOR="#000000"> }

}</FONT>

Parent.inc:
<FONT COLOR="#0000ff">#ifndef </FONT><FONT COLOR="#000000">name
  </FONT><FONT COLOR="#0000ff">#define </FONT><FONT COLOR="#000000">name </FONT><FONT COLOR="#008000">"Parent"
</FONT><FONT COLOR="#0000ff">#endif name</FONT>
Child.inc:
<FONT COLOR="#0000ff">#define </FONT><FONT COLOR="#000000">name </FONT><FONT COLOR="#008000">"Child"</FONT>
Result:
>do ##class(Macro.Parent).Test()
Class: Macro.Parent
Value: Parent

do ##class(Macro.Child).Test() Class: Macro.Child Value: Child

0
Eduard Lebedyuk  Dec 26, 2017 to Vitaliy Serdtsev

Interesting,

Sorry, that I didn't specify it earlier, but I can't modify Parent.inc too.

0
Vitaliy Serdtsev  Dec 26, 2017 to Eduard Lebedyuk

Then this:

<FONT COLOR="#000080">Include </FONT><FONT COLOR="#ff0000">Child

</FONT><FONT COLOR="#000080">Class Macro.Child Extends Macro.Parent </FONT><FONT COLOR="#000000">{

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">first() {   </FONT><FONT COLOR="#0000ff">#include </FONT><FONT COLOR="#000000">Child }

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Test() [ </FONT><FONT COLOR="#000080">PlaceAfter </FONT><FONT COLOR="#000000">= first ] {   </FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#008000">"Class: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$classname</FONT><FONT COLOR="#000000">() , ! , </FONT><FONT COLOR="#008000">"Value: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$$$name </FONT><FONT COLOR="#000000">}

}</FONT>

or this:
<FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Test()
{
  </FONT><FONT COLOR="#0000ff">#include </FONT><FONT COLOR="#000000">Child
  </FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#008000">"Class: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$classname</FONT><FONT COLOR="#000000">() , ! , </FONT><FONT COLOR="#008000">"Value: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$$$name
</FONT><FONT COLOR="#000000">}</FONT>
0
Eduard Lebedyuk  Dec 26, 2017 to Vitaliy Serdtsev

Class include is not required (and does not seem to affect anything) and can be omitted

Include Child

Why that happens, as  methods generators are compiled before methods?

As for the second approach, I'd like to avoid duplicating code (in a real case I have about 20 methods 1-50 lines long).

0
Vitaliy Serdtsev  Dec 26, 2017 to Eduard Lebedyuk
Class include is not required (and does not seem to affect anything) and can be omitted
Yeah, I just forgot to delete that line.
0
Vitaliy Serdtsev  Dec 26, 2017 to Eduard Lebedyuk

Here's another way (without PlaceAfter):

<FONT COLOR="#000080">Class Macro.Child Extends Macro.Parent
</FONT><FONT COLOR="#000000">{

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">%inc() [ </FONT><FONT COLOR="#000080">Internal</FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#000080">Private </FONT><FONT COLOR="#000000">] {   </FONT><FONT COLOR="#0000ff">#include </FONT><FONT COLOR="#000000">Child }

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Test() {   </FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#008000">"Class: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$classname</FONT><FONT COLOR="#000000">() , ! , </FONT><FONT COLOR="#008000">"Value: " </FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#0000ff">$$$name </FONT><FONT COLOR="#000000">}

}</FONT>

0