Determining actual args an Excel UDF was called with - c++

I'm adding a user defined function to Excel with varargs-based signature in C++:
LPXLOPER MyFunction(...);
When Excel calls MyFunction, it passes it 30 arguments regardless of how many the user entered in the sheet. The extraneous ones are blank strings.
MyFunction, however, is designed to accept empty string arguments. As a result, I cannot tell valid empty strings apart from the extraneous ones sent by Excel.
A solution could be to obtain the contents of the actual cell where the user entered the function. However, I can't find a way of doing that from within the implementation of my function.
Could someone please suggest a way out?

I've found a simple way to get hold of the caller cell.
Use excelApp->get_Caller();

Related

Check if a list is defined and avoid the "UNDEFINED" error

In the original TI-BASIC (for TI-83/84+) is there a way to check if a list has been defined?
Currently calling dim(⌊LIST) will return an error if the list is not defined.
Is there a way to handle that error gracefully?
Possible Workaround:
The only hacky way I can think of doing so is to redefine the list with more items than you're expecting such as 99→dim(⌊LIST) and check if the first few values are not zero. But that seems wasteful and slow.
Any suggestions?
Unfortunately there doesn't seem to be a clean, simple function for checking if a list already exists, but thanks to harold who posted this as a comment, there is a workaround:
The SetUpEditor command.
This is typically used for specifying which lists are displayed in the list editor, but the command has the side-effect of creating a zero-length list if it does not exist yet.
So here's some sample code, with comments:
"Create two empty lists if they do not exist yet
SetUpEditor FOO,BAR
"Check the size of FOO
dim(∟FOO)→X
"Clean up by returning the list editor back to
"its default state (∟1-∟6)
SetUpEditor
If you have control during the lists's creation, then another workaround would be to use another list with only one element (or a list of another fixed size, or a letter variable) as a flag that indicates this main list exists.
Something like this, for lists LMAIN and LFLAG:
1->dim(LFLAG
If 5784923472≠LFLAG(1
Then
"assume LMAIN does not exist because flag hasn't been set
"insert optional other code here
0->dim(LMAIN
5784923472->LFLAG(1
End

In Google Sheets, how do I search for a value in lists in multiple sheets, and return a result if there is at least one match?

I have a sheet with a list of company names in column A - some of these company names are present within sheets of different users, who have a tickbox within a column beside these companies.
I want to have a column in my sheet which would tell me whether this company is ticked within one of the user's sheets and if so, put the user's name in the cell next to it. If not, it can remain blank. The likelihood at this time that the tickbox is TRUE for the same company in two different sheets is negligible.
In the sample sheet below, I've used the following formula in cell B2:
=ifs(vlookup(A2,Sheet2!$A$2:$B$11,2,false)=TRUE,"Sam",vlookup(A2,Sheet3!$A$2:$B$11,2,false)=TRUE,"Nick",vlookup(A2,Sheet4!$A$2:$B$11,2,false)=TRUE,"Mike")
It works for the first two conditions, but then it cannot seem to work with the third logical pair. Note that the sample sheet below uses the vlookup across tabs, the actual sheet will be using importrange, but I don't think it should make a difference.
What could be wrong?
Sample sheet here
A More General Problem
To make this more general so others find it useful (which is the whole point of StackOverflow), the general problem can be rephrased as
"How do I search for a value in lists in multiple sheets, and return a
result if there is at least one match?"
Generally, when using VLOOKUP(), QUERY(), or other matching functions, you have to account for any errors through non-matches. Those "move" outwards into the outer functions and can eventually be the reported result, unless explicitly handled. Sometimes, this is less obvious when you sometimes get answers but that's because the outer functions have ignored or not evaluated the matching function.
Therefore, always consider what happens if the matching function returns a N/A and explicitly handle it.
Your Example
In your case, IFS() is simply raising an error whenever VLOOKUP() does not match. However, since IFS() returns the first condition that matches, from left to right in the formula, it doesn't always get around to evaluating one of the non-matching VLOOKUP()s which is why you see it works sometimes.
So, you should explicitly handle the errors with e.g. IFERROR()
My approach was to avoid lookup functions, and just filter the list by those that had filled checkboxes, and then count the occurrence of interest ("Company 1", "Company 2", ...) using COUNTIF(). If the counted total is 1, we have a match, so grab that entry as an element in an array. Otherwise, leave an empty value.
At this point, you could drop the empty elements, and take the first non-empty element (or return a blank), but I opted to list out all of the names.
To get rid of the blanks, I use QUERY() and then JOIN() to make a list of each name. In the case, where nothing matched, and my array was empty, I simply wrap everything in one IFERROR().
=IFERROR(JOIN(", ",QUERY(TRANSPOSE({IF(COUNTIF(FILTER(Sam!A:A,Sam!B:B=TRUE),A2)=1,"Sam",""),IF(COUNTIF(FILTER(Nick!A:A,Nick!B:B=TRUE),A2)=1,"Nick",""),IF(COUNTIF(FILTER(Mike!A:A,Mike!B:B=TRUE),A2)=1,"Mike","")}),"SELECT Col1 WHERE Col1 IS NOT NULL",0)),"")
I found it convenient for your example to rename "Sheet2" as "Sam", "Sheet3" as "Nick", "Sheet4" as "Mike", which is what I think was your original meaning.
The formula can be easily modified to show just the first matching result.
use:
=ARRAYFORMULA(IFNA(VLOOKUP(A2:A, QUERY({
QUERY(Sheet2!A2:B, "select A,B,'Sam'");
QUERY(Sheet3!A2:B, "select A,B,'Nick'");
QUERY(Sheet4!A2:B, "select A,B,'Mike'")},
"where Col2=TRUE"), 3, )))

Speed up creating a list of PL/SQL function/procedure dependencies

We are creating a list of functions and procedures used when executing a restricted version of a piece of software in PL/SQL. Our process is slow though, and we want to find a faster way of finding function or procedure calls made within a given function or procedure's code. The words function and procedure are used interchangeably throughout this post...
Our current process:
1) Get the function source code of given function in table using regexp_substr:
l_function_source := REGEXP_SUBSTR(FUNCTION|PROCEDURE)(\s)+'|| i_function_name ||'(\s|\()+(.)+END(\s)+'|| i_function_name||'(\s)*(;)'
2) For each word (slowness!) of the function source we look for 'foreign' function calls (e.g. package_name.function_name(param1,param2..)), remove them, and then scan for local function calls (e.g. function_name(param1,param2..)). Each time we find a new function/procedure, we add it to a table of function names.
Ideally I'd like to get a list of dependencies for each function, much like you do a table. E.g.:
SELECT referenced_name
FROM USER_DEPENDENCIES
WHERE TYPE IN ('FUNCTION','PROCEDURE')
AND NAME = i_function_name ;
This way we could loop through the list, querying each function/procedure name and adding their dependents to the list without having to rely on regexps and looping through each word. Sadly this doesn't seem to exist. Any suggestions or comments would be highly appreciated.
Fe

AX2012: Capture SysInfoAction on form initialization

Situation:
I am opening a form via a SysInfoAction (SysInfoAction_FormrunQuery to be exact) from an error message (info log) as follow (Compressed a bit):
query.addDataSource(tableNum(WDPErrorLogView)).addRange(fieldNum(WDPErrorLogView, ErrorOutboundMessage)).value(SysQuery::value(resultRecId));
error("#WDP1299", '', SysInfoAction_FormrunQuery::newFormnameQuery(formStr(WDPErrorLog), query));
Problem:
The form I am calling here, has multiple tabs, since it can be opened with queries from various tables. I would like to capture this calling record to not only filter on the record passed (This already happens thanks to the SysInfoAction), but also make the appropriate tab page active.
I thought that element.args().record() would contain this calling record, but to my surprise it does not. Any ideas how to could get hold of this record... or, at least its table id?
This is easy. Just add a parmMethod for an object/caller to \Classes\SysInfoAction_FormrunQuery. Copy the static method newFormnameQuery and call it newFormnameQueryWithCaller for example and then add an extra argument, then set that argument in your parm method with the same style as newFormnameQuery.
Then in your \Classes\SysInfoAction_FormrunQuery\run, where it sets up the args record, just add args.caller(...) and set your caller object if it exists.

How to check the validity of the dateformat (ex, %d/%m/%Y-%H:%M:%S)?

I am using boost optionsparser to parse the command-line arguments passed by the user.
Now the program had an option for the user to specify his/her choice of dateformat.
like,
program -d %d/%m/%Y-%H:%M:%S , program -d %d/%m/%Y and so on.
The problem I am facing is, *How do I check the validity of the format string passed by the user? *
The only way I can think of now is passing the format string on to the date class and using the exception handling there.
However, if there is another way to check the validity at the time of parsing the options then I wouldn't need to pass around the data and do stuff unncessarily since I do some calculation before actually using the format to generate the date string.
I wouldn't need to pass around the data and do stuff unncessarily since I do some calculation before actually using the format to generate the date string.
Instead of jumping through hoops to calculate something to pass to the date class to validate the format string, why don't you just ask the date class to format today's date for you and see if it generates an exception or not?
If you try to parse it yourself you're just writing code that's duplicating what the date class does, but that also has the chance of missing some detail. You may annoy your user by disallowing something that should be allowed (I've had this happen with applications that tell me my perfectly valid email isn't), and you'll have to handle the exception from the date class anyway in case you don't cover all the bases that it covers when parsing the format