Skip to main content
Known Participant
December 9, 2012
Answered

Callback function uses caller's variable scope instead of declared scope

  • December 9, 2012
  • 2 replies
  • 3127 views

hi there

Still on CF 9.0.1.x

I have in the same directory 2 CFCs, each implementing an object that communicates with each other. Each CFC has the variables scope used. They are part of an emulator for a physical device. There are more cfc involved but I reduced it to the minimum test case.

The parent is MCU which instantiates any number of TARGETS. A target communicates back to MCU with a callback function provided by the MCU. The callback function uses MCU's clock to log the incoming message.

---

component MCU {

  variables.mcu.clock = 0;

more of variables.mcu.*

  Init () {

// Create all targets, call each target’s registerMCUListener to let them know whom to call back.

variables.mcu.targets = CreateObject ("component", "target").Init ();

variables.mcu.targets.registerMCUListener (cbFromTarget);

  }

private numeric function cbFromTarget (struct msg) {

writedump (var=#variables#);  // <<<< This shows the variables scope of TARGET, expected MCU

variables.mcu.events.list[variables.mcu.events.ptr] = {

clock = variables.mcu.clock,  // CF chokes on this one because MCU is unknown

msg = arguments.msg

};

return 0;

}

}

---

---

component TARGET {

  variables.tg.clock = 0;

more of variables.tg.*

public void function registerMCUListener (any mculistener) {

/*

* register the mcu listener function to notify of any events

*/

variables.tg.mcu.listener = arguments.mculistener;

}

private void function ToMCU (struct msg) {

/*

* use the registered mcu listener function to notify of any events

*/

if (isCustomFunction (variables.tg.mcu.listener)) {

variables.tg.mcu.listener (msg);

}

}

}

---

Idea: The cbFunc registers the message in the MCU’s variables scope

But it does not. The marked variables.mcu.clock is throwing an error because mcu.clock does not exist in the variables scope.

Observation: CF is using the variables scope of TARGET instead of MCU.

I confirmed that by dumping the variables scope just before that line.

Now: Is this a bug? Looks as if this is an compiler issue of binding *too late* …

Because it's urgent (as usual) .. any workarounds? Be aware that even those two simple CFCs have to communicate in both directions MCU <-> TARGET .. and of course there are more CFCs involved ...

PS: In CF 10 I could use the keyword function as a data type declaration. I did that but to no avail. Same error.

Thanks

This topic has been closed for replies.
Correct answer Adam Cameron.

Idea: The cbFunc registers the message in the MCU’s variables scope

But it does not. The marked variables.mcu.clock is throwing an error because mcu.clock does not exist in the variables scope.

Observation: CF is using the variables scope of TARGET instead of MCU.

I confirmed that by dumping the variables scope just before that line.

  

Now: Is this a bug?

No, it's not a bug I'm afraid: it's expected behaviour.

If I have waded through your code correctly, you are wanting cbFunc to be implemented as a closure, ie: the external variables it references are bound at declaration-time: the function is declared in MCU, so you expect the references to the variables scope in cbFunc to refer to the variables scope of MCU. This is not how CF works.

Variable-binding in CF is done at runtime, and at runtime cbFunc is being called from within TARGET, so the variables-scope references in cbFunc are bound to TARGET's variables scope.

CF10 can use functions which are implemented as closures, but not via the syntax you're using here (I'll not repeat the docs: http://help.adobe.com/en_US/ColdFusion/10.0/Developing/WSe61e35da8d31851842acbba1353e848b35-8000.html).

--

Adam

2 replies

Adam Cameron.Correct answer
Inspiring
December 13, 2012

Idea: The cbFunc registers the message in the MCU’s variables scope

But it does not. The marked variables.mcu.clock is throwing an error because mcu.clock does not exist in the variables scope.

Observation: CF is using the variables scope of TARGET instead of MCU.

I confirmed that by dumping the variables scope just before that line.

  

Now: Is this a bug?

No, it's not a bug I'm afraid: it's expected behaviour.

If I have waded through your code correctly, you are wanting cbFunc to be implemented as a closure, ie: the external variables it references are bound at declaration-time: the function is declared in MCU, so you expect the references to the variables scope in cbFunc to refer to the variables scope of MCU. This is not how CF works.

Variable-binding in CF is done at runtime, and at runtime cbFunc is being called from within TARGET, so the variables-scope references in cbFunc are bound to TARGET's variables scope.

CF10 can use functions which are implemented as closures, but not via the syntax you're using here (I'll not repeat the docs: http://help.adobe.com/en_US/ColdFusion/10.0/Developing/WSe61e35da8d31851842acbba1353e848b35-8000.html).

--

Adam

tinu8805Author
Known Participant
December 13, 2012

hi adam

Thanks, that's what i figured out too.

just to be sure: using this. instead of variables. would not be a remedy i assume?

So, I could create another cfc as a queue manager so both MCU and TARGET use it to put/poll event messages ... this should no longer create problems, do you agree?

Thanks

Martin

Inspiring
December 13, 2012

Nah, it would not matter which variables scope you use: they're all bound at runtime, and givent he function is "within" the target CFC, it'll be that CFC's scope that is bound.

I think - if I get where you're suggesting - the idea of using an common intermediary CFC instance should work, yes.

The other thing you could possibly do is to pass a reference to MCU's variables scope along with the callback, and then pass that reference into the callback when you actually call it.  It depends on what yo're doing as to what's gonna be a better approach here.

I'm in a slight rush to get out of the office, and I have to say I'm scanned your code a few times now (hence the delay in responding) and I don't quite get what you're doing... not your fault, I'm not poring over the code very thoroughly, so am kinda just assuming what you're doing.

But based on my passing understanding, I'd probably use your queue suggestion. I think.

--

Adam

Inspiring
December 9, 2012

I don't think it's a bug.  You passed the cbFromTarget function to a function in the target cfc.  That means it is operating in the target cfc and reading the target cfc's variables scope.

I notice that the cbFromTarget function can take a structure as an argument but I don't see you passing it one.  Maybe that's the path to the workaround you need.

tinu8805Author
Known Participant
December 9, 2012

Yes, you don't see how I use the regsitered function in TARGET .. it goes like this:

---

toMCU ( { msgnum = msgTGHitCorrect,

                         target = variables.tg.id,

                         hand   = variables.tg.events.stack[evtptr].hand } );

---

My undestandig of the problem is: Since CF does many things quite dynamically, it evaluates the expression variables.xxx at runtime and not at compile time. If I suppose at runtime, it's clear that varaible.xx is the noe of TARGET .. but I was not aware that it is REALLY at runtime.

I considered compile time "dynamic" evaluation like this: CF tries to find unscoped variables first in this scoap, than in that scope finally in scop xyz .. but still at compile time. Hence the reason why I use fully scoped vairables.

Martin

BKBK
Community Expert
Community Expert
December 11, 2012

I would define component instance variables explicitly. For example, like this

component MCU {

variables.mcu = structNew();

...

etc., etc.

}

component TARGET{

variables.tg= structNew();

...

etc., etc.

}

I am assuming you have also initialized the targets array, using something like

Init () {

variables.mcu.targets = arrayNew(1);

  }

It is unclear from your code whether or not cbFromTarget has an updated value of mcu. You might have to do this using this.init() or some other means.

private numeric function cbFromTarget (struct msg) {

this.init();

...

etc., etc.

}

Finally, you get your function, cbFromTarget, to do 2 things at once. Namely to return 0(redundant) and to dump the variables struct. Improve the design by giving the function a returntype of struct, and changing the last line to return variables;.