Adding members to structures and API compatibility
thomas_braun
Hi,
I've encouraged users to write functions which have the same signature as
TestAnalysisFunction_V3.
Function TestAnalysisFunction_V3(panelTitle, s)
string panelTitle
STRUCT AnalysisFunction_V3& s
End
/// @brief The structure passed into `V3` and later analysis functions
Structure AnalysisFunction_V3
/// one of @ref EVENT_TYPE_ANALYSIS_FUNCTIONS
variable eventType
/// raw data wave for interacting with the DAC hardware (locked to prevent
/// changes using `SetWaveLock`). The exact wave format depends on the hardware.
/// @sa GetHardwareDataWave()
WAVE rawDACWave
/// active headstage index, `[0, NUM_HEADSTAGES[`
variable headStage
/// number of rows in `rawDACWave` which will be filled with data at the
/// end of DAQ. The total number of rows in `rawDACWave` might be higher
/// due to alignment requirements of the data acquisition hardware.
///
/// Always `NaN` for #PRE_DAQ_EVENT events.
variable lastValidRowIndex
/// number of rows in `rawDACWave` with already acquired data
variable lastKnownRowIndex
/// Potential *future* number of the sweep. Once the sweep is finished it will be
/// saved with this number. Use GetSweepWave() to query the sweep itself.
variable sweepNo
/// Number of sweeps in the currently acquired stimset of the passed headstage
variable sweepsInSet
/// Analysis function parameters set in the stimset's textual parameter
/// wave. Settable via WBP_AddAnalysisParameter().
string params
EndStructure
string panelTitle
STRUCT AnalysisFunction_V3& s
End
/// @brief The structure passed into `V3` and later analysis functions
Structure AnalysisFunction_V3
/// one of @ref EVENT_TYPE_ANALYSIS_FUNCTIONS
variable eventType
/// raw data wave for interacting with the DAC hardware (locked to prevent
/// changes using `SetWaveLock`). The exact wave format depends on the hardware.
/// @sa GetHardwareDataWave()
WAVE rawDACWave
/// active headstage index, `[0, NUM_HEADSTAGES[`
variable headStage
/// number of rows in `rawDACWave` which will be filled with data at the
/// end of DAQ. The total number of rows in `rawDACWave` might be higher
/// due to alignment requirements of the data acquisition hardware.
///
/// Always `NaN` for #PRE_DAQ_EVENT events.
variable lastValidRowIndex
/// number of rows in `rawDACWave` with already acquired data
variable lastKnownRowIndex
/// Potential *future* number of the sweep. Once the sweep is finished it will be
/// saved with this number. Use GetSweepWave() to query the sweep itself.
variable sweepNo
/// Number of sweeps in the currently acquired stimset of the passed headstage
variable sweepsInSet
/// Analysis function parameters set in the stimset's textual parameter
/// wave. Settable via WBP_AddAnalysisParameter().
string params
EndStructure
Now the obvious thing happened and I have to add another member to AnalysisFunction_V3.
Can that break something on the user side? I can assume that they are not passing the structure into an XOP.
AFAIK, structures are passed in and out without the inside routines knowing what the outside routines use or do not use. So, it should not fail when you put a new variable (e.g. mynewparam) in the structure but never call on it or use it outside the structure (e.g. via TAF.mynewparam). OTOH, when a function calls the OLD structure that does not have the mynewparam and then tries to use TAF.mynewparam, it should break if not at compile then certainly at run time.
At least, that is my understanding if not also supported by some (limited) mucking about in my own routines.
September 25, 2019 at 01:56 pm - Permalink
Thanks Jeffrey.
> it should break if not at compile then certainly at run time.
Well that would be a problem. How would it compile but fail at runtime? I can't think of any code which does that.
September 26, 2019 at 03:50 am - Permalink
Oh! You are right. Of course it would have to break at compile time first. Igor is not compiling line by line during runtime as with python.
Speaking of this, didn't Igor compile macros line by line at runtime some time ago?
September 26, 2019 at 06:00 am - Permalink
> Of course it would have to break at compile time first.
Good! This would be totally fine.
> Speaking of this, didn't Igor compile macros line by line at runtime some time ago?
This is still the case now AFAIK.
September 26, 2019 at 06:25 am - Permalink
In reply to > Of course it would have to… by thomas_braun
Then I might not be surprised when a call made within a macro to a structure variable TAF.mynewvariable would only fail at runtime.
To conclude, I am fairly certain in principle and by experience that a procedure will never fail to compile or fail to run when new parameters are added to a structure inside of it but the new parameters are never called/used outside of the structure. I've done this type of coding a number of times in my own procedures.
OTOH, I am also certain that calls to structures to try to access parameters that do not exist in the structure will fail at compile time if not at run time (in macros). @WaveMetrics could address the latter point better. Also, I can think of a simple brute force test. Make a structure with only one parameter. Call it from a function and try to access a second (non-existent) parameter. Change the function to a macro and repeat the test.
September 26, 2019 at 08:57 am - Permalink
I can't think of how this would break anything, as long as the function that accepts the structure doesn't expect the value of the new field to be initialized to a useful value. In other words, it can't rely on the caller setting the value if the caller might not know about the value.
It's often good practice to include a version field as the first field of a structure so that functions that use the structure can be written more intelligently (this often means so they don't crash). But with Igor code, you don't need to worry about a crash(*).
(*) "You" means the typical user, not necessarily thomas_braun, who should not take this statement as a challenge :)
September 26, 2019 at 11:31 am - Permalink
@jjweimer Yes, macros are interpreted, so you only get errors at runtime. But structures aren't allowed in macros, so they are guaranteed to fail to compile if code tries to use a non-existent structure member.
September 26, 2019 at 11:40 am - Permalink
So to conclude I would say:
Adding members to Igor Pro structures is API compatible if you only use these structures in Igor Pro functions.
Thanks all for your replies.
September 26, 2019 at 11:49 am - Permalink
In reply to So to conclude I would say: … by thomas_braun
The only issue is if you use StructPut and StructGet. Then you need to worry about versions. You may be able to handle this by including a version field in the structure.
September 26, 2019 at 12:15 pm - Permalink