How to prevent #include from loading same procedure twice?
Hello,
Some of my procedures are using the same basic functions, which are saved in separate procedures. So my procedures start with an #include-statement to load these basic procedures, to be sure they are available. However, if multiple procedures include the same basic procedure I get an error while compiling that the function name is already existing, because the same procedure is loaded multiple times.
I already tried
#include ":BasicProcedure"
Endif
but this seems to have no effect. I guess the "If" does not affect anything while compiling. The manual states options for #include, but none is suitable.
So how can I turn off the #include under certain conditions, or kill all except one of the instances when a collision between procedures appears?
Greeting,
Mathias
#include shouldn't load the same file twice. It's more likely that you have the same function name in two different files that are being loaded, or that you have two copies of the same file, one loaded manually and the other by #include, or something like that.
March 4, 2019 at 11:50 pm - Permalink
In reply to #include shouldn't load the… by tony
Thank you for this hint, you are completely right. When more than one procedure want to include the exact same procedure file, then #include only loads this file once. But when you have the following folder structure:
and both Procedure1.ipf and Procedure2.ipf contain
#include ":BasicProcedure"
then the error happens. Igor does not recognize that both files are the same (by name and checksum) and loads both.
So the solution is to have only one BasicProcedure.ipf in one folder, and let all relative paths point to this one file.
Then a new problem arises: The folder "shared Procedures" has subversions inside. Subfolders are tags, trunk, branch with different folder depths. So it is necessary to have BasicProcedure.ipf inside each folder. E.g. letting Procedure1.ipf in "my Procedures" point to Basicprocedure.ipf in "shared Procedures/trunk" will result in multiple loaded files when using "Procedure2.ipf" in "shared Procedures/tags/1.0".
Is there a way to solve this problem inside Igor? Or maybe I just need help to use subversion in a clean way.
March 5, 2019 at 01:26 am - Permalink
In reply to Thank you for this hint, you… by Mathias
you should be aware that
#include "procedure"
loads the first instance of procedure.ipf found the the User Procedures folder (doesn't seem to be affected by location of the procedure file containing the include statement).
Also that independent modules, named modules and static functions can help avoid conflicts.
Maybe if you avoid specifying the path that will do the trick? If you're sure that the files will always be identical you can probably get away with it.
Also, I strongly recommend storing procedure files in the User Procedures folder, rather than in Wavemetrics Procedures, as implied by your use of #include < > rather than #include " ".
BTW, I think that in the help for #include may be a bit misleading: 'If file spec is the name of or a partial path to the file, Igor interprets it as relative to the "User Procedures" folder'. It seems to be relative to the calling procedure file, not User Procedures.
March 5, 2019 at 02:50 am - Permalink
In reply to you should be aware that … by tony
First of all, I had a typo. I am using #include ":BasicProcedure" instead of the version with the <...>. Sorry for that.
And thank you for pointing towards modules and static functions. It seems like I have to put all procedures (including BasicProcedure.ipf) within "shared Procedures" into one regular module to avoid the name conflict. Furthermore I should put the version of BasicProcedure.ipf of the other folder into my User Procedure folder, so there is only one file at one position.
By doing so I should be able to solve the problem of name conflicts. Furthermore the procedures in "Shared Procedures" can be even better shared with coworkers, so that they just use the folder and have all procedures available, without name conflicts because they have used the same function name.
I will test it in the next days, if it works I will hopefully not forget to post the solution underneath for other people.
March 5, 2019 at 07:13 am - Permalink
In reply to First of all, I had a typo… by Mathias
In your example if you simply switch
#include ":BasicProcedure"
to#include "BasicProcedure"
the conflict should go away. No need for modules. Should be okay if all copies of BasicProcedure.ipf are identical.March 5, 2019 at 08:31 am - Permalink
I want to emphasize what Tony just said- that colon in #include ":BasicProcedure" is really important, and the likely source of the original problem. For all the gory details on the various formats for #include, execute this Igor command: DisplayHelpTopic "The Include Statement"
March 5, 2019 at 09:55 am - Permalink
When the two files are identical ...
One better approach is to put BasicProcedure in to the Igor Procedures folder. It will then load automatically every time Igor is opened.
The other better approach is to put BasicProcedure into its own "User Procedures: Common Procedures" folder. Then, when any procedure calls to #include "BasicProcedure", the file will only be loaded at the first call. All subsequent calls will be aware the procedure file is already loaded.
March 5, 2019 at 10:30 am - Permalink
In reply to When the TWO FILES are… by jjweimer
Regarding the remark with the colon: Just #include "BasicProcedure" looks only for procedures within the User Procedures folder. Removing the colon results in Igor not finding the local procedure.
And I will put BasicProcedure.ipf in the User Procedures folder, this folder is an aspect of Igor that I ignored for too long.
But the problem is that all procedures in "Shared Procedures" should be easily accessible by others and run out-of-the-box. So my coworkers can just download the folder, compile one procedure consisting of many #include ":...", and everything will run on its own. Not moving anything in the User Procedures folder.
To accomplish this goal for "Shared Procedures" without conflicts a module seems to be the best solution.
March 5, 2019 at 11:29 pm - Permalink
In reply to Regarding the remark with… by Mathias
I see several possible scenarios:
Depending on which of these best describes the problem the solution may differ. In general, developing outside of User Procedures may make sense, but your life will be much easier if you can persuade your users to store code in the User Procedures folder.
Don't forget that the help menu has a 'Show Igor Pro User Files' option.
March 6, 2019 at 01:42 am - Permalink
In reply to Regarding the remark with… by Mathias
The best solution is to manage this non-standard approach properly up-front at your end. Independent Modules are one way to go but they are really thought of as a means to solve a different set of problems than what you face.
In the end though, insisting that procedures must be stored in User Procedures can save you more development headaches even when it means you have to spend a bit more time to educate your co-workers.
BTW, when you are running on macOS, I believe that you can handle storing files external to User Procedures by putting a symlink (not an alias) of your external (Shared Procedures) folder directly in the User Procedures.
March 6, 2019 at 07:44 am - Permalink
@Mathias: Could you add a MWE so that we have something to play around with?
Also I'm not getting how modules should help here, naming the modules differently is an option but that is of course a maintenance nightmare.
March 6, 2019 at 09:36 am - Permalink
Oh and educating the users is the worst. Running out of the box is the standard nowadays.
March 6, 2019 at 09:39 am - Permalink
In reply to Oh and educating the users… by thomas_braun
I would argue educating management is a far more arduous task. ;)
Andy
March 6, 2019 at 10:01 am - Permalink
In reply to @Mathias: Could you add a… by thomas_braun
Here is a MWE that is working with Independent Modules. It worked once, as I am having some problems with Igor under Wine right now I cannot check anymore.
// Basic.ipf
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IndependentModule=Test1
Function TestBasic()
print "Clear"
End
// Test1.ipf
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IndependentModule=Test1
#include ":Basic"
Function MyFunctionA()
TestBasic()
End
// in folder "My Procedures":
// Basic.ipf
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IndependentModule=Test2
Function TestBasic()
print "Clear"
End
// Test2.ipf
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IndependentModule=Test2
#include ":Basic"
Function MyFunctionB()
TestBasic()
End
So this works as I want, the big downside is that you need to write
Test1#MyFunctionA()
So this means not only changing the header of every procedure but every call of a function. Yes, this is a big pain to change everything. But still, I think putting everything in "Shared Procedures" in one Independent Module would be a clean solution if you want it to work out of the box for everyone.
And in the last year there was only one coworker who wanted to use these procedures, he was even too lazy to understand SVN and how to download from a repository. In the end I gave him the files on a flash drive. He was not willing to do this work to get 200kb of working source code with GUIs. That is why I do not think putting the files in the User Procedures folder will work.
March 7, 2019 at 12:00 am - Permalink
I might consider this structure ...
Shared Procedures
- Common
-- Basic.ipf
- Test1 Folder
- Test2 Folder
- ...
- TestN Folder
The Basic.ipf procedure is only stored in ONE PLACE. In your TestN procedures, you should then need only to #include "::Common:Basic.ipf" (this should be the right syntax based on going up and then down again).
Otherwise, SVN can be a bit of a pain to appreciate. Even I get befuddled by remembering where I put what at times. When you are doing this distribution only internally, the easier option might be to use a "more transparent" (for those who are less inclined to get in to deep technology) cloud distribution source such as Dropbox or GDrive or even simply sftp. And the idea then might be to say ... Download this ZIP archive from here, put it in the User Procedures folder here, unZIP it there, and viola!
March 7, 2019 at 06:32 am - Permalink
Using a name like
is not a good idea as that can collide with other peoples code.
I've attached a MWE using an approach with file name prefixes and normal includes. This works out of the box. If you go that route you have to be sure that the common functions work with all your different projects. Ensuring that can be done with e.g. some automated testing, I'm using https://github.com/byte-physics/igor-unit-testing-framework (disclaimer: I wrote most of it) for that.
March 7, 2019 at 02:01 pm - Permalink
In reply to I might consider this… by jjweimer
Of course this would be a clean solution, but the problem is that the folder depth varies as written in the second comment.
And regarding the MWE of thomas_braun: "Basic.ipf" is a name chosen for clarity in this forum. The real name is "ShirleyBKG.ipf", which is widely used in our group. And then the main problem arises when someone wants to use the procedures in "Shared Procedures" out of the box. That ShirleyBKG.ipf was already loaded from another directory and that there is a collision.
Educating everyone to avoid this collision by putting ShirleyBKG.ipf in everyones' User Procedures folder as suggested in the third and seventh comment would not work. When there is a compilation error many colleagues simply give up.
March 7, 2019 at 11:45 pm - Permalink
@Mathias: I think I'm lost here. What is missing from the example MWE I provided?
March 8, 2019 at 01:46 am - Permalink
In reply to @Mathias: I think I'm lost… by thomas_braun
The folder structure is more difficult than just two folders. It is basically:
And in every folder (that does not have subfolders inside) there is a copy of Basic.ipf. So the problem is, having one version e.g. in Shared Procedures means that when I copy the files from trunk to tag/1.2, the relative path is not working anymore because the files are now 1 layer further away.
So the only clean solution where there is only one Basic.ipf would be the User Procedures folder, but that would mean that I would have to educate my coworkers. No longer working out-of-the-box.
A more tedious attempt (that I will try) is to put all procedures within Shared Procedures into one independent module. But I do not have the time to try this right now, as it means to change every function call from "function(...)" to "modulename#function(...)".
I hope this sums up the current point of the argumentation.
March 8, 2019 at 05:09 am - Permalink
I'm not sure that this will solve your problem, but I would recommend checking out either trunk or one of the specific tags instead of checking out the top level folder of the repository. You can use the SVN switch command to switch your local Shared Procedures directory to point to svn://xyz/repository/tags/1.0/ or svn://xyz/repository/trunk/. That should reduce the number of Basic.ipf copies within your hierarchy.
March 8, 2019 at 07:52 am - Permalink
aclight is totally right. You don't want to include muliple versions from your version control system.
March 8, 2019 at 12:49 pm - Permalink