BindEvent() And other

This forum is meant for questions about the Visual FoxPro Language support in X#.

User avatar
xinjie
Posts: 108
Joined: Wed May 20, 2020 10:05 am
Location: China
Contact:

BindEvent() And other

Post by xinjie »

robert wrote:xinjie,

Maybe you can send me a private email to we can discuss this ?

Robert
I had send an email to you.
简单的东西重复做,你能成为专家;重复的东西用心做,你能成为赢家!
User avatar
xinjie
Posts: 108
Joined: Wed May 20, 2020 10:05 am
Location: China
Contact:

BindEvent() And other

Post by xinjie »

@FoxProMatt

Thank's !
简单的东西重复做,你能成为专家;重复的东西用心做,你能成为赢家!
mainhatten
Posts: 200
Joined: Wed Oct 09, 2019 6:51 pm

BindEvent() And other

Post by mainhatten »

robert wrote: The BindEvent function is a runtime function that allows you to bind a function (method) to an event.
In X# we do that at compile time, just like in C# and VB. For example for a Form object you can bind an event with the += syntax:

Code: Select all

oForm:Loaded += Form_Loaded
The Form_Loaded method must have the right parameters (the compiler will check this)

And to remove an event handler you write the same code with the "-=" syntax,
In theory you can also do this in code (and we could therefore implement the BindEvents() function) but that is simply not the right solution.
Hi Robert,

I realize this is an old thread, but in vfp (due to the fact vfp always call the same method with different parameters filled or given) there is for me a huge benefit in BindEvent():
I can retrofit existing code with a homegrown profiler with minimal effort/source changes, if I need to I can "wrap" method calls even externally after objects have been loaded by reading a table with specific object instance names, object classes or decendants of object classes.

For this the vfp Bindevent NOT having to conform to the method signature of the wrapped method is ideal.
Perhaps the same effect might be realized in x# code depending on reflection, but even then result will be marred (in part) by same named methods calling each other with different (perhaps default) parameters.

So yes, I realize vfp BindEvent does not map to Dotnet delegates well - but the ability to wrap any named method either from object.init() or even very late by recursive dynamic calls walking the object tree with amembers() helping is a very powerful (and hackish) approach.

regards
thomas
User avatar
robert
Posts: 4567
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

BindEvent() And other

Post by robert »

Thomas,

We may be able to emulate this behavior, but to properly do so we need an example that shows what you describe.
Are you saying that you want to have one method as "handler" for all events, with different parameters depending on the event that was called ?
We can probably do that by creating a method "on the fly" that then does a Clipper Calling convention call to your code.
Can you provide us with an example ?

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
mainhatten
Posts: 200
Joined: Wed Oct 09, 2019 6:51 pm

BindEvent() And other

Post by mainhatten »

Hi Robert,
robert wrote:We may be able to emulate this behavior, but to properly do so we need an example that shows what you describe.
Are you saying that you want to have one method as "handler" for all events, with different parameters depending on the event that was called ?
We can probably do that by creating a method "on the fly" that then does a Clipper Calling convention call to your code.
Can you provide us with an example ?
Whipped up a VERY basic sample of the approach to "retrofit" a timing log to specific object.method() calls, as this is more "against Dotnet thinking" compared to having a special method linking specific delegates in the init of interesting classes.
This approach might be used on the cursoradapter class of the largest tables, as larger tables are more suspect of causing delays. The second approach - hooking in Init of base class (perhaps with property switch) would be used to log_time all cursoradapter.method calls suspect of causing delays for all tables, for instance across different machines or on different customer data sets.

Compared to coverage profiler this will give more realistic times, but I have specific C-logger .Fll functionality to check in more detail / with even less overhead / Heisenbug changes introduced by measuring, as well as specific "tableaction-wrappers" allowing me to see problematic/slow code parts.

Code tries to reduce to the max, but it DOES have cleanup (not needed in most situations), but the objects wrapped normally are have their own lists of what and where to clear - unwrap is more a hint than sober implementation and logging would be piped either into txt or dbf....
Fire up vfp, run and grin
;-)

Code: Select all

* BindEvent Logger
clea

*--- tiny stuff injected in app code
private poLog
poLog = createobject("Logger_Base")

***************************************************************
*-- this would usually be table based, to switch several dozen timers on/off with single SQL on table
= Check_Instance("Worker_Fast", "Do_Job")
= Check_Instance("Worker_Base", "Do_Wrk, Do_Val, Do_Sav")
= Check_Instance("Worker_Fast", "Do_Wrk, Do_Job")
= Check_Instance("Worker_Slow", "Do_Wrk, Do_Val, Do_Sav, Do_Job")
= Check_Instance("Worker_Sloppy", "Do_Wrk, Do_Val, Do_Sav, Do_Job")

function Check_Instance(tcObj, tcWatchList)
	local loWork
	loWork = createobject(m.tcObj)
	= poLog.Wrap_List(m.loWork, m.tcWatchList)
	loWork.Do_Job()

***************************************************************
* logger classes have more capabilities only example of "static external code"
define class EvWrapper as custom
	function Wrap_List(toSource, tcMethodCSV, toHandler, tcDelegate))
		local laMethods[1], lnRun
		for lnRun = 1 to alines(laMethods, m.tcMethodCSV, 5, ",")
			= this.Wrap_Both(m.toSource, laMethods[m.lnRun], m.toHandler, m.tcDelegate)
		next

	function Wrap_Both(toSource, tcEvent, toHandler, tcDelegate)
		local loHandler, lcDelegate
		*-- make last 2 parameters optional via defaults
		loHandler = iif(vartype(m.toHandler)=="O", m.toHandler, this)
		lcDelegate = iif(vartype(m.toHandler)=="C", m.tcDelegate, "Log")
		= bindevent(m.toSource, m.tcEvent, m.loHandler, m.lcDelegate+"_Setup",0)
		= bindevent(m.toSource, m.tcEvent, m.loHandler, m.lcDelegate+"_TearDown",1)
		return

	function UnWrap(toSource)
		*-- ok, here it gets really ugly
		*-- in demo code trying to hint at better cleanup
		local laEv[1], lnRun
		for lnRun = 1 to aevents(laEv, m.toSource)
			= unbindevents(m.toSource, laEv[m.lnRun, 3], laEv[m.lnRun, 2], laEv[m.lnRun, 4])
		next
		return
enddefine


define class Logger_Base as EvWrapper
	*--- timing log does not need special info, dummy parameter, 
	*--- don't overdo, as vfp parameters are slooooow
	function Log_Setup(dum1, dum2, dum3)
		= this.Log_Do("Setup")
	function Log_TearDown(dum1, dum2, dum3)
		= this.Log_Do("TearDown")

	function Log_Do(tcFrom)
		local laEv[1]
		= aevents(laEv, 0)
		? str(seconds(), 12, 4) + " at " + m.tcFrom ;
			+ ">>>" + laEv[1].Class + "::" + laEv[2] 
		return

enddefine


*****************************************************
* here is the huge customer app where I am tasked to identify the slow parts
* I don't want to mess *inside* their code too much, as subclass methods will also have code 
* and not will depend on properties to be slow

define class Worker_Base as custom
	nVal = 1
	nSav = 1
	nWrk = 1

	function Simulate(tnVal)
		wait window program(program(-1)-1) timeout m.tnVal
		wait clea
		return

	function destroy()
		*-- ahemmm... good habits done ugly...
		= poLog.UnWrap(this)
		return dodefault()

	function Do_Job()
		= this.Do_wrk()
		= this.Do_Val(1)
		= this.Do_Sav("", 0)

	function Do_wrk()
		= this.Simulate(this.nWrk)
	function Do_Val(toObj)
		= this.Simulate(this.nVal)
	function Do_Sav(toObj, tcTable)
		= this.Simulate(this.nSav)

enddefine

define class Worker_Fast as Worker_Base
	nVal = 0.2
	nSav = 0.1
	nWrk = 0.4
enddefine

define class Worker_Slow as Worker_Base
	nVal = 2
	nSav = 2
	nWrk = 3
enddefine

define class Worker_Sloppy as Worker_Slow
	nVal = 0.2
	nSav = 3
	nWrk = 1
enddefine

Post Reply