XLL+ Class Library (6.3)

Understanding the vector code

The new function will have been inserted into your code. The interesting code in the common function is marked below:

CopyC++
CXlOper* HISTVOL_Impl(CXlOper& xloResult, const CXlOper* Prices_op)
{
    // Input buffers 
    std::vector<double> Prices;                                   
    // Validate and translate inputs             
    XlReadVector(*Prices_op, Prices, L"Prices", XLA_TRUNC_ONEMPTY|
        XLA_TRUNC_ONBLANK);                                       
    // End of generated code 
//}}XLP_SRC 
    // TODO - set the value of xloResult, or return another value 
    //          using CXlOper::RetXXX() or throw a CXlRuntimeException. 
    return xloResult.Ret();
}

As you can see, the Wizard has generated more code this time. Let us examine the new features.

CXlOper type argument

The argument type has been declared as const CXlOper*. In the last example, NORMSINV2, we used CXlOper to return data to Excel. Here, Excel is using CXlOper to pass data to our add-in function.

Because CXlOper is a generic type, we cannot know in advance what it will contain. The user may pass a single value instead of a series, or a string value, or they may omit the value altogether. Whatever the user specifies, Excel will pass it straight on to our function. It is the responsibility of the add-in function to ensure that the argument contains the expected type of data. The Wizard generates code to handle this.

STL vector

An STL vector of doubles, Prices, has been declared. This will be used as a convenient variable-size buffer for the data passed from Excel in the CXlOper Prices_op.

Note: The best thing about using an STL vector is that we don't have to worry about memory cleanup. As soon as Prices goes out of scope (i.e. at the end of the function), all its memory will be released. We do not have to remember to use free() or delete.

Input checking

The function is going to have do some processing of input to a more useful form. In particular, the data in Prices_op has to be extracted from the CXlOper and put into an array of doubles.

CopyC++
std::vector<double> Prices;
XlReadVector(*Prices_op, Prices, L"Prices");

The template method XlReadVector() is used to do the data extraction. If the extraction fails for any reason, then an exception of type CXlRuntimeException will be thrown, containing details of why the conversion failed. For example, if the third cell in Prices contains the text "Cheese", the exception will contain the following message:

Expected number for Prices[3]
Note: The message does not need to be in English. Depending on the current Windows language, the message may be localized. See International support for details.

Early exit

If the extraction process has failed, and an exception is thrown, the function will immediately stop, and the exception will be caught and handled by the wrapper function, HISTVOL_4 or HISTVOL_12.

CopyC++
extern "C" __declspec(dllexport)
LPXLOPER12 HISTVOL_12(LPXLOPER12 Prices)
{
    XLL_FIX_STATE;
    CXlOper xloResult, Prices__port(Prices);
    try {
        xloResult.HandleResult(HISTVOL_Impl(xloResult, &Prices__port));
    }
    catch(const CXlRuntimeException& ex) {
        CXllApp::Instance()->DisplayException(xloResult, ex);
    }
    return xloResult.Ret12();
}

The exception is handed to the application-level method, CXllApp::DisplayException(), which will deal with it in a standard way. (You can override DisplayException() in your add-in if you wish.) The standard behaviour is to display a text string, e.g.:

#ERROR: Expected number for Prices[3]

If it succeeds, then we can be confident that the vector Prices is now populated with an array of numeric values, and we can safely call our implementation function, CalcHistVol().

Truncating the input

The call to XlReadVector(...) also does some other useful work. If the user has left some of the cells at the end of the input range empty, XlReadVector() will truncate them, rather than putting null values into Price.

Adding some useful code

Add code to the function to call our stand-alone function, as shown below:

CopyC++
CXlOper* HISTVOL_Impl(CXlOper& xloResult, const CXlOper* Prices_op)
{
    // Input buffers 
    std::vector<double> Prices;
    // Validate and translate inputs
    XlReadVector(*Prices_op, Prices, L"Prices", XLA_TRUNC_ONEMPTY|
        XLA_TRUNC_ONBLANK);
    // End of generated code 
//}}XLP_SRC 
    try                                        
    {                                          
        xloResult = CalcHistVol(Prices, 250.0);
    }                                          
    catch(const char*)                         
    {                                          
        xloResult = xlerrNum;                  
    }                                          
    return xloResult.Ret();
}

Depending on whether CalcHistVol() succeeds, we will either set CXlOper to contain the result of the calculation, or set it to contain the error code xlerrNum, indicating that the inputs were out of range is some way.

Our add-in function is now complete and we're ready to build and test it.

Next: Input error handling >>