Create automatically override function?

Hello,

I have function which the user needs to modify in order to get some functionality. In order to keep this as simple (for USER) as possible, I am hoping to have button, which will open main procedure window and type in 

override Function UserHookFunction(someparam)
       variable someparam
      //do something
end

I have the function UserHookFunction defined in the code, but need to give user chance to use modified version they want to use. I have no expectations of user high profiency, but the function is pretty simple and most can modify it. 

Normally I would open notebook and provide the template and instructions, but it would be nicer to give them the template, with the override keyword, in the main procedure window and open this all for them. 

Is this possible? I can get the function text with: 

ProcedureText(macroOrFunctionNameStr  [, linesOfContext  [, procedureWinTitleStr ]]) 

But then I run out of ideas. DoWIndow /F Procedure does not work and do not know how to paste there anything anyway. 

Help, please? 

Not wanting to greatly encourage this level of hackery, I am intrigued to wonder whether some combination of DisplayProcedure, PutScrapText and DoIgorMenu "Edit", "Paste" might suffice.

Plus Execute/P " COMPILEPROCEDURES".

Here is the code that achieves this unholy hackery (based on code by Tony):

Function InsertIntoMainProc()
    String currScrap = GetScrapText()   // copy current scrap text
    DisplayProcedure/W=Procedure
    DoIgorMenu "Edit" "Select All"
    DoIgorMenu "Edit" "Copy"
    PutScrapText InjectUserFunc(GetScrapText()) // modify procedure code
    DoIgorMenu "Edit" "Paste"
    PutScrapText currScrap              // put previous scrap text back
    Execute/P/Q/Z "COMPILEPROCEDURES "  // recompile all
    HideProcedures                      // hide all procedure windows
End

Function/S InjectUserFunc(String procText)
    String newCode = "\r\rFunction test()\r\tprint \"test\"\rEnd"
    return procText+newCode
End

Running InsertIntoMainProc() will secretly modify the code in the main procedure window via InjectUserFunc(). Do what you want here. Currently, it only writes a new function 'test()' in there. I could imagine you read the user-written function from a notebook, check for sanity and put it in the right place inside procText here. You have to do the error and sanity checking yourself. The procedure window will not compile without error if something is wrong. For added robustness I recommend to put above code into an independent module.

Thanks!!!!

That is a great hack with nice and tidy code. I completely understand the danger of this method... 

This is great use of DoIgorMenu. I rarely used up to now, that but clearly need to learn more ;-) 

Sounds like fun afternoon today... Once more, thanks a LOT. 

Just to add a thought. Do I understand your intent? You want to allow users to create their own analysis "plug-in" method editing it "on-the-fly" while the package is running?

(This sounds like a step into a danger zone for many reasons but if so ...)

I might offer an approach without the extensive hacking of inserting into the main procedure window.

Create a separate .ipf file that is called by #include in your main procedure. In that procedure file, include the blank template function with a return.

Function JIrun_UserAnalysisPlugIn(...)

    variable rtnv = 0

    // put your analysis function below
    // store the result in the rtnv variable

    return rtnv
end

In your main function, call this function with the assurance that it will return the value of rtnv.

When you want to allow users to edit this "on-the-fly", have your button or macro menu call open this specific procedure window for them to make the changes.

The advantage of editing the main procedure is that the plug-in remain resident only to that specific experiment. Perhaps this advantage can be recovered by doing the same thing to edit in a Notebook window instead of what will amount to a global #included procedure file.

There are many ways - many better ways, to be precise - how to do what I need correctly. The point is I need something dumb simple from user point of view. With the code from Stephan above I made this work in 15 minutes. Added check to prevent creating the override function second time and it is working fine. If override function does not exist, it gets created and displayed. If it exists, it gets displayed. Important benefit: general code does not change, this override function is specific to current Igor experiment. Nice hack!!

Great that it worked well for you. Again, to make it more robust you may want to entertain the idea of putting your controlling code into an independent module. This may be a hassle, but the big benefit is than independent modules stay complied and functional even if your users screw up big time and produce a function full of errors. Independent modules are also hidden, so that users cannot sniff around or screw with the code (this may be a drawback, depending on how open you are with your projects). Also, if your project is already big it will be a pain to implement this. Anyway, I leave this here:

DisplayHelpTopic "Independent Modules"

 

I don't think that a function override will work when the original function is in a different namespace, so it's unlikely that an independent module can be used. Is the return value important here?

I ran a quick test involving conditional compilation and managed to crash both Igor 8 and 9 in different ways, so be cautious!

Edit: This comment was written when Igor 9 was in an early stage of beta testing. Any bugs that caused a crash have long since been fixed.

here is what i tried:

in the procedure window

function test() // no need for override when original is in IM
    print GetIndependentModuleName()
    return 1
end

in second procedure window:

#pragma IndependentModule = imName

function doit()
    test()
end

function test()
    variable /G varReturn=nan
    NVAR varReturn=varReturn
    execute /Z "varReturn=test()"
    if(v_flag==0)
        return varReturn
    endif

    print GetIndependentModuleName()   
end

I thought this might work based on

DisplayHelpTopic "Limitations of Independent Modules"

but managed only to crash Igor :(

OK, I see that independent modules actually make things more unstable in some cases. ;) If you have an independent module going then you can just skip the Override. But this will not work for a hook function (this seems to be the goal here?) which needs to be always present in some form. If it is OK to not run the user function if it does not exist, e.g., by wrapping the user function call in an if-statement (check via Exists("UserHookFunction") == 6) inside the real hook function, then it might work. But, yeah, we don't know what Jan is really aiming for. If it is sufficient to  have some really simple (user-chosen) calculation etc. executed, then grabbing the input string and throwing it at an Execute command might be enough.

I think I have found the hack I was looking for :)

in procglobal:

function UserFunc(variable v)
   
    // override code goes here
    print GetIndependentModuleName()   
    return 1
end
#pragma IndependentModule = imName

function test(variable v1) 
    variable /G varReturn=nan
    NVAR varReturn=varReturn
    string cmd
    sprintf cmd, "varReturn=procglobal#userFunc(%g)", v1
    execute /Z cmd 
    if(v_flag==0)
        return varReturn
    endif
   
    // default code goes here
    print GetIndependentModuleName()
    return 2
end

 

Maybe what you should be using are funcRefs. The prototype would have the default code.

DisplayHelpTopic "Function References"