XLL+ Class Library

Using data in a matrix object

Code generated by the wizard

Whens you return to DevStudio, you will see that the wizard will has inserted the following source code at the end of your C++ file.

// Function:    INTERP2D
// Purpose:     Interpolate in a two-dimensional array

//{{XLP_SRC(INTERP2D)
    // NOTE - the FunctionWizard will add and remove mapping code here.
    //    DO NOT EDIT what you see in these blocks of generated code!
IMPLEMENT_XLLFN2(INTERP2D, "RPPPBB", "INTERP2D", "X,Y,Z,InterpX"
    ",InterpY", "Statistical", "Interpolate in a two-dimensional"
    " array", "Ordered vector of X values\0Ordered vector of Y"
    " values\0Matrix of Z values\0X value at interpolation"
    " point\0Y value at interpolation point", "B()X Ordered"
    " vector of X values\0B()Y Ordered vector of Y values\0B()0("
    ")Z Matrix of Z values\0\0\0", 1)

extern "C" __declspec( dllexport )
LPXLOPER INTERP2D(const COper* X, const COper* Y, const COper* Z,
    double InterpX, double InterpY)
{
    CXlOper xloResult;
    BOOL bOk = TRUE;
    std::vector<double> vecX;
    bOk = bOk && X->ReadVector(vecX, "X", xloResult);
    std::vector<double> vecY;
    bOk = bOk && Y->ReadVector(vecY, "Y", xloResult);
    ple::mtx_ptrs<double> matZ;
    bOk = bOk && Z->ReadMatrix(matZ, "Z", xloResult, 0, 0, XLA_ARRAY_FLAGS_STD, 0, 0);
    if (!bOk)
        return xloResult.Ret();
//}}XLP_SRC

    // TODO - Set the value of xloResult
    return xloResult.Ret();
}

Most of this code is pretty familiar stuff. Let's examine the new code for handling the matrix.

    ple::mtx_ptrs<double> matZ;
    bOk = bOk && Z->ReadMatrix(matZ, "Z", xloResult, 0, 0, XLA_ARRAY_FLAGS_STD, 0, 0);

As you may remember, our implementation function, LinearInterp2D(), expects the matrix argument to be passed as an array of pointers to one-dimensional arrays of doubles, where each of the arrays of doubles represents one column of data.

The wizard has generated code that does a number of useful things:

  1. Declares a matrix variable, matZ, which will hold the data extracted from the COper. For reasons of efficiency, the matrix is of type mtx_ptrs<T>.
  2. Calls the ReadMatrix() method, which extracts the values from the COper Z, and populates matZ. If any of the data in Z is unacceptable, ReadMatrix() returns an error value.

So, at the end of the Wizard-generated code, we have exactly what we need to call LinearInterp2D():

Add your own code

The code we add to the wizard-generated skeleton needs to do three things:

  1. Check that Z is the same width as X and the same height as Y, and return a useful error message if not.
  2. Call LinearInterp2D().
  3. Return either an error code or the calculated result.

(Generally, this is all an add-in function does. And frequently, all the checking code is generated by the wizard, and does not need to be added.)

Add the code shown below, which implements all the required functionality.

extern "C" __declspec( dllexport )
LPXLOPER INTERP2D(const COper* X, const COper* Y, const COper* Z,
    double InterpX, double InterpY)
{
    ...
//}}XLP_SRC

    // Check that the sizes of the X and Y axes match Z                                           
    if (matZ.size(0) != vecY.size()) {                                                            
        xloResult.Format("#Error: expected %d rows in Z", vecY.size());                           
        return xloResult.Ret();                                                                   
    }                                                                                             
    if (matZ.size(1) != vecX.size()) {                                                            
        xloResult.Format("#Error: expected %d columns in Z", vecX.size());                        
        return xloResult.Ret();                                                                   
    }                                                                                             

    double dInterpZ;                                                                              
    // The line below can use STL notation if you prefer                                          
    // e.g. if (LinearInterp2D(vecX.size(), vecX.begin(), vecY.size(), vecY.begin(), matZ.begin(),
    //                         InterpX, InterpY, &dInterpZ))                                      
    if (LinearInterp2D(vecX.size(), &vecX[0], vecY.size(), &vecY[0], &matZ[0],                    
                       InterpX, InterpY, &dInterpZ))                                              
        xloResult = dInterpZ;                                                                     
    else                                                                                          
        xloResult = xlerrNum;                                                                     

    return xloResult.Ret();
}

We will revisit this example later, to look at a way to make the wizard write the dimension-checking code above.

The function is now complete, and ready for building and testing.

Next: Matrix layouts >>