Difference between revisions of "Calc/Add-In/CompleteAddIn"

From Apache OpenOffice Wiki
< Calc‎ | Add-In
Jump to: navigation, search
m (See also)
m (See also)
 
(25 intermediate revisions by 5 users not shown)
Line 20: Line 20:
 
==IDL File==
 
==IDL File==
 
We begin with a simple IDL file with four methods. Here is the corresponding IDL file :
 
We begin with a simple IDL file with four methods. Here is the corresponding IDL file :
<source lang="idl">
+
<syntaxhighlight lang="idl">
 
// IDL
 
// IDL
 
#include <com/sun/star/uno/XInterface.idl>
 
#include <com/sun/star/uno/XInterface.idl>
Line 44: Line 44:
 
};
 
};
 
};
 
};
</source>
+
</syntaxhighlight>
  
 
Four methods named methodOne, methodTwo, methodThree and methodFour can be seen in this IDL file and then will be implemented.
 
Four methods named methodOne, methodTwo, methodThree and methodFour can be seen in this IDL file and then will be implemented.
  
 
We give the automatically generated C++ class with the cppumaker tool.
 
We give the automatically generated C++ class with the cppumaker tool.
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
class SAL_NO_VTABLE XSomething : public ::com::sun::star::uno::XInterface
 
class SAL_NO_VTABLE XSomething : public ::com::sun::star::uno::XInterface
 
{
 
{
Line 67: Line 67:
 
                               (::com::sun::star::uno::RuntimeException) = 0;
 
                               (::com::sun::star::uno::RuntimeException) = 0;
 
};
 
};
</source>
+
</syntaxhighlight>
  
 
where again the four methods are seen but in C++ language. Note before going further that parameters of the methods  are always marked as const but passed by reference (in C++ style with & operator). It's time to give the corresponding code.
 
where again the four methods are seen but in C++ language. Note before going further that parameters of the methods  are always marked as const but passed by reference (in C++ style with & operator). It's time to give the corresponding code.
Line 75: Line 75:
  
 
===The two first member functions===
 
===The two first member functions===
The two first methods are similar :
+
The two first methods are similar:
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
OUString MyService2Impl::methodOne( OUString const & str )
 
OUString MyService2Impl::methodOne( OUString const & str )
Line 90: Line 90:
 
     "called methodTwo() of MyService2 implementation: ") ) + m_arg + str;
 
     "called methodTwo() of MyService2 implementation: ") ) + m_arg + str;
 
}
 
}
</source>
+
</syntaxhighlight>
They only take a string (from a OOoCalc Cell) and add a message and put all the message+string in the result cell.
+
They only take a string (from an AOO Calc Cell) and add a message and put all the message+string in the result cell.
  
 
===The third member Function===
 
===The third member Function===
The third member function is more complicated : it returns a value calculed from a cell range (the sum).
+
The third member function is more complicated: it returns a value calculated from a cellrange (the sum).
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
sal_Int32 MyService2Impl::methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
 
sal_Int32 MyService2Impl::methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
Line 117: Line 117:
 
   return temp;
 
   return temp;
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
===The fourth member Function===
 
===The fourth member Function===
The goal of the fourth member function is to show how we can implement a matrix function : starting from a cell range and obtaining a cell range.
+
The goal of the fourth member function is to show how we can implement a matrix function: starting from a cellrange and obtaining a cellrange.
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//C++
 
//C++
 
//It's a matrix operation should be called like : {=METHODFOUR(A1:B4)}
 
//It's a matrix operation should be called like : {=METHODFOUR(A1:B4)}
Line 142: Line 142:
 
   return temp;
 
   return temp;
 
}
 
}
</source>
+
</syntaxhighlight>
What is done by this example is not great : only add four to every cells of the cell range and put the result in an other cell range.
+
What is done by this example is not great : only add four to every cell of the cellrange and put the result in another cellrange.
  
 
Every method are related to a C++ class named MyService2Impl. Let's now give the corresponding class definition.
 
Every method are related to a C++ class named MyService2Impl. Let's now give the corresponding class definition.
Line 149: Line 149:
 
===The C++ Class===
 
===The C++ Class===
 
We use an helper to implement our class and then we don't need to implement all the interfaces : no need to implement XInterface, XTypeProvider, XWeak for instance. We give now the corresponding C++ code.
 
We use an helper to implement our class and then we don't need to implement all the interfaces : no need to implement XInterface, XTypeProvider, XWeak for instance. We give now the corresponding C++ code.
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//C++
 
//C++
 
class MyService2Impl : public ::cppu::WeakImplHelper5<
 
class MyService2Impl : public ::cppu::WeakImplHelper5<
Line 201: Line 201:
 
     virtual lang::Locale SAL_CALL getLocale() throw( uno::RuntimeException );
 
     virtual lang::Locale SAL_CALL getLocale() throw( uno::RuntimeException );
 
};
 
};
</source>
+
</syntaxhighlight>
  
The code is not complete with the four methods as you can see in the class declaration. Only your own XSomething interface is already implemented (see [[CompleteAddIn#Implementing_in_C.2B.2B_the_corresponding_four_Methods|above]]). You can wonder how is it possible to find all the methods in the class ? As you will learn below, the IDL files corresponding to interfaces have to be read for that. The number of interfaces you have to implement is dependant of your goal. If you want to create a component you have to implement XInterface interface and add more code, if you want to make a scriptable component you still add more code (and more interfaces : XTypeprovider, XServiceInfo and XWeak) and if you want to create an AddIn you have to add more (XAddin, XServiceName). This is explained in Developper's Guide. We first begin with a typical addin interface : XAddin.
+
The code is not complete with the four methods as you can see in the class declaration. Only your own XSomething interface is already implemented (see [[CompleteAddIn#Implementing_in_C.2B.2B_the_corresponding_four_Methods|above]]). You can wonder how is it possible to find all the methods in the class ? As you will learn below, the IDL files corresponding to interfaces have to be read for that. The number of interfaces you have to implement is dependant of your goal. If you want to create a component you have to implement XInterface interface and add more code, if you want to make a scriptable component you still add more code (and more interfaces : XTypeprovider, XServiceInfo and XWeak) and if you want to create an AddIn you have to add more (XAddin, XServiceName). This is explained in Developer's Guide. We first begin with a typical addin interface : XAddin.
  
 
==The XAddIn Interface==
 
==The XAddIn Interface==
Every add-in have to implement the <idl>com.sun.star.sheet.XAddIn</idl> interface. As usual what you have to do is described by an IDL file (or you can read the previous C++ class definition) :
+
Every add-in has to implement the <idl>com.sun.star.sheet.XAddIn</idl> interface. As usual, what you have to do is described by an IDL file (or you can read the previous C++ class definition) :
<source lang="idl">
+
<syntaxhighlight lang="idl">
 
// IDL
 
// IDL
 
module com { module sun { module star { module sheet {
 
module com { module sun { module star { module sheet {
Line 225: Line 225:
 
};
 
};
 
}; }; }; };
 
}; }; }; };
</source>
+
</syntaxhighlight>
 
The corresponding C++ code is given now without explanation (for the moment)
 
The corresponding C++ code is given now without explanation (for the moment)
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
// XAddIn
 
// XAddIn
Line 299: Line 299:
 
   return getProgrammaticCategoryName( aProgrammaticName );
 
   return getProgrammaticCategoryName( aProgrammaticName );
 
}
 
}
</source>
+
</syntaxhighlight>
  
To understand what is this code related to, we provide a snapshot : read it carefully to see where the code works and provides the strings to print out in the autopilot frame :
+
To understand what this code is related to, we provide a snapshot : read it carefully to see where the code works and provides the strings to print out in the autopilot frame :
  
 
[[Image:Addin2.png]]
 
[[Image:Addin2.png]]
  
I have used the method one as you can see above the autopilot frame, and I am ready to use the method two. What you can read in the autopilot comes directly from the C++ code except the French word "requis" which is probably translated by "required" in an English OpenOffice version.
+
I have used method one as you can see above the autopilot frame, and I am ready to use method two. What you can read in the autopilot comes directly from the C++ code except the French word "requis" which is probably translated by "required" in an English OpenOffice version.
  
 
== The XServiceName Interface==
 
== The XServiceName Interface==
  
 
Again the corresponding IDL file indicates what we have to do :
 
Again the corresponding IDL file indicates what we have to do :
<source lang="idl">
+
<syntaxhighlight lang="idl">
 
// IDL
 
// IDL
 
module com { module sun { module star { module lang {
 
module com { module sun { module star { module lang {
Line 318: Line 318:
 
};
 
};
 
}; }; }; };
 
}; }; }; };
</source>
+
</syntaxhighlight>
 
This interface is simple : one method :
 
This interface is simple : one method :
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//C++
 
//C++
 
// XServiceName
 
// XServiceName
Line 328: Line 328:
 
   return OUString::createFromAscii( "my_module.MyService2" );
 
   return OUString::createFromAscii( "my_module.MyService2" );
 
}
 
}
</source>
+
</syntaxhighlight>
 
See <idl>com.sun.star.lang.XServiceName</idl> for the complete documentation.
 
See <idl>com.sun.star.lang.XServiceName</idl> for the complete documentation.
  
Line 334: Line 334:
 
This <idl>com.sun.star.lang.XServiceInfo</idl> interface may be avoided. You have to implement it only if you want that your add-in is also scriptable : you can call it from OOoBasic for instance. It's time to present the corresponding IDL file :
 
This <idl>com.sun.star.lang.XServiceInfo</idl> interface may be avoided. You have to implement it only if you want that your add-in is also scriptable : you can call it from OOoBasic for instance. It's time to present the corresponding IDL file :
  
<source lang="idl">
+
<syntaxhighlight lang="idl">
 
// IDL
 
// IDL
 
module com {  module sun {  module star {  module lang {
 
module com {  module sun {  module star {  module lang {
Line 344: Line 344:
 
};
 
};
 
}; }; }; };  
 
}; }; }; };  
</source>
+
</syntaxhighlight>
  
 
Here is the corresponding C++ code :
 
Here is the corresponding C++ code :
  
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
// XServiceInfo implementation
 
// XServiceInfo implementation
Line 362: Line 362:
 
{
 
{
 
// this object only supports one service, so the test is simple
 
// this object only supports one service, so the test is simple
   if (serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ))
+
   return serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ) ||
    return true;
+
        serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") );
  if (serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") ))
+
    return true;
+
  else return false;
+
 
}
 
}
  
Line 380: Line 377:
 
   return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
 
   return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
==XInitialisation Interface==
 
==XInitialisation Interface==
This <idl>com.sun.star.lang.XInitialization</idl> interface could be removed without problem. I will remove it completely in the futur.
+
This <idl>com.sun.star.lang.XInitialization</idl> interface could be removed without problem. I will remove it completely in the future.
 
Here is the corresponding C++ code.
 
Here is the corresponding C++ code.
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
// XInitialization implemention
 
// XInitialization implemention
Line 406: Line 403:
 
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
==XLocalizable Interface==
 
==XLocalizable Interface==
  
This <idl>com.sun.star.lang.XLocalizable</idl> interface is particular : we have let it in the class definition and give now the corresponding code but because the code do nothing, we don't add it as sixth interface when using the helper.
+
This <idl>com.sun.star.lang.XLocalizable</idl> interface is particular : we have let it in the class definition and give now the corresponding code, but because the code does nothing, we don't add it as sixth interface when using the helper.
  
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
// XLocalizable
 
// XLocalizable
Line 425: Line 422:
 
//    return aFuncLoc;
 
//    return aFuncLoc;
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
==Making a registerable component==
 
==Making a registerable component==
An add-in has to be registered. In fact if you want to use external code, you have to work with a registry. If you use your external code with OOoBasic you call it a component and if you use it with OOoCalc you call it an add-in. But both are very similar. In my knowledge the only exception to this registry rule is OOoBasic (under Windows) which can call directly an external DLL.
+
An add-in has to be registered. In fact if you want to use external code, you have to work with a registry. If you use your external code with OoBasic you call it a component and if you use it with AOo Calc you call it an add-in. But both are very similar. In my knowledge the only exception to this registry rule is OoBasic (under Windows) which can call directly an external DLL.
  
 
To supplement the above code we have to add :
 
To supplement the above code we have to add :
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
// C++
 
// C++
 
/* shared lib exports implemented without helpers in service_impl1.cxx */
 
/* shared lib exports implemented without helpers in service_impl1.cxx */
Line 468: Line 465:
 
}
 
}
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
== The complete Code ==
 
== The complete Code ==
 
It's time to give  the C++ complete code :
 
It's time to give  the C++ complete code :
  
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//Listing 9 My first Add-In
 
//Listing 9 My first Add-In
 
//C++
 
//C++
Line 744: Line 741:
 
     // this object only supports one service, so the test is simple
 
     // this object only supports one service, so the test is simple
 
     // modified *********
 
     // modified *********
     if (serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ))
+
     return serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ) ||
      return true;
+
          serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") );
    if (serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") ))
+
      return true;
+
    else return false;
+
 
}
 
}
 
Sequence< OUString > MyService2Impl::getSupportedServiceNames()
 
Sequence< OUString > MyService2Impl::getSupportedServiceNames()
Line 813: Line 807:
 
}
 
}
 
}
 
}
</source>
+
</syntaxhighlight>
 
We have learnt now how things works and particularly how to manage sequence of sequence parameters or returned value.
 
We have learnt now how things works and particularly how to manage sequence of sequence parameters or returned value.
  
{{Documentation/Note|This code is to set in <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent path because there is no add-in exemple in the SDK. You have to slightly modify the corresponding makefile remove a source file to check this example.}}
+
{{Note|This code is to set in <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent path because there is no add-in example in the SDK. You have to slightly modify the corresponding makefile remove a source file to check this example.}}
  
 
=Utilities=
 
=Utilities=
In order to provide utilities to interface OOo to other external libraries, we will provide in this section any utilities to transform classical variables into UNO variables.
+
In order to provide utilities to interface AOO to other external libraries, we will provide in this section any utilities to transform classical variables into UNO variables.
  
 
==Transforming sequence of sequence into  array==
 
==Transforming sequence of sequence into  array==
  
If you plan to extend OOoCalc in any way, you will probably use an external library, for instance a C library like GSL. You will notice that the variables are in general arrays of double and not sequence.  
+
If you plan to extend AOOCalc in any way, you will probably use an external library, for instance a C library like GSL. You will notice that the variables are in general arrays of double and not sequence.  
 
Have a look to the case of Listing below where you see an imaginary method « foo ». The point is we use a sequence of sequence as parameter and we want to transform it into 2D array.
 
Have a look to the case of Listing below where you see an imaginary method « foo ». The point is we use a sequence of sequence as parameter and we want to transform it into 2D array.
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//C+
 
//C+
 
Sequence< Sequence< double > > MyService2Impl::foo(
 
Sequence< Sequence< double > > MyService2Impl::foo(
Line 846: Line 840:
 
// Perhaps the compiler use a constructor in this case ?
 
// Perhaps the compiler use a constructor in this case ?
 
// I will have a look as sooner as possible
 
// I will have a look as sooner as possible
</source>
+
</syntaxhighlight>
 
At the end of this program we have a 2D array named « table » and we show how to construct it. (I have tried a way using getArray() but without success).
 
At the end of this program we have a 2D array named « table » and we show how to construct it. (I have tried a way using getArray() but without success).
  
Line 854: Line 848:
  
 
Imagine your calculus provides a 1D array named « z » and you want to construct a sequence of sequence before returning it. Here is what you could do :
 
Imagine your calculus provides a 1D array named « z » and you want to construct a sequence of sequence before returning it. Here is what you could do :
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//C+
 
//C+
 
...
 
...
Line 862: Line 856:
 
solList2D[0]=solList1D;
 
solList2D[0]=solList1D;
 
return solList2D;
 
return solList2D;
</source>
+
</syntaxhighlight>
  
 
A similar problem encountered later has to be solved differently : I start from an 1D array but this array contains, real part and imaginary part « The n-1 roots are returned in the packed complex array z of length 2(n-1), alternating real and imaginary parts » I want to show the result in Calc but with two rows (real part and imaginary part) with as many lines as necessary. Here is the way :
 
A similar problem encountered later has to be solved differently : I start from an 1D array but this array contains, real part and imaginary part « The n-1 roots are returned in the packed complex array z of length 2(n-1), alternating real and imaginary parts » I want to show the result in Calc but with two rows (real part and imaginary part) with as many lines as necessary. Here is the way :
  
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//C+
 
//C+
 
...
 
...
Line 879: Line 873:
 
}
 
}
 
return solList2D;
 
return solList2D;
</source>
+
</syntaxhighlight>
  
Sometimes we have to use Standard Template Library because the library we want to interface is in C++. Eric Ehlers has interfaced a C++ library (QuantLib project) and then provide any interesting code [http://www.quantlibaddin.org here]. See particularly the file calcutils.cpp. I have reproduct part of the code [[SDKCppLanguage#More_Abstractions_with_STL|here]] but with using template.
+
Sometimes we have to use Standard Template Library because the library we want to interface is in C++. Eric Ehlers has interfaced a C++ library (QuantLib project) and then provide any interesting code [https://www.quantlibaddin.org here]. See particularly the file calcutils.cpp. I have reproduct part of the code [[SDKCppLanguage#More_Abstractions_with_STL|here]] but with using template.
 +
 
 +
==Managing simultaneously Sequence of Sequence and Sequence==
 +
 
 +
It's possible to encounter any buildin functions which can cope with cell range and with a list of values. Because the former is a Sequence of Sequence and the latter a simple Sequence, the question is how to manage such a data structure in an addin function ?
 +
 
 +
Christophe Devalland was facing this problem when interfacing the [http://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/casref_en/casref_en.html XCas] library (See [[CompleteAddIn#Notes|the note below]]) and solves it with help of Niklas Nebel in this way :
 +
<syntaxhighlight lang="cpp">
 +
Any CASImpl::cadd(const Sequence< Any > &aValList )
 +
    throw (RuntimeException)
 +
{    sal_Int32 n1;
 +
    sal_Int32 nE1 = aValList.getLength();
 +
    Sequence< Sequence< Any > > aArraySeq;
 +
    gen e;
 +
    giac::context * contextptr=0;
 +
    e=0;
 +
    for( n1 = 0 ; n1 < nE1 ; n1++ )
 +
    {
 +
        try
 +
        {
 +
            if ( aValList[ n1 ] >>= aArraySeq )
 +
            {
 +
                // traiter la zone de cellules contenue dans aArraySeq
 +
                //
 +
                Somme_Array(e,aArraySeq);
 +
            }
 +
            else
 +
            {
 +
                // aValList[n1] ne contient qu'une valeur
 +
                e=e+Cellule2Gen(aValList[ n1 ]);
 +
            }
 +
        }
 +
        catch (std::runtime_error & erreur)
 +
        {
 +
            return makeAny(OUString::createFromAscii("Erreur : ")+OUString::createFromAscii(erreur.what()));
 +
        }
 +
    }
 +
    e=giac::simplify(e,contextptr);
 +
    return Gen2any(e);
 +
}
 +
void Somme_Array(gen & e, Sequence< Sequence< Any > > & aArraySeq)
 +
{
 +
    // somme le contenu de la zone de cellules désignée par aArraySeq
 +
    sal_Int32        n1, n2;
 +
    sal_Int32        nE1 = aArraySeq.getLength();
 +
    sal_Int32        nE2;
 +
    for( n1 = 0 ; n1 < nE1 ; n1++ )
 +
    {
 +
        const Sequence< Any >    rList = aArraySeq[ n1 ];
 +
        nE2 = rList.getLength();
 +
        const Any*    pList = rList.getConstArray();
 +
        for( n2 = 0 ; n2 < nE2 ; n2++ )
 +
        {
 +
            // version modifiée de Cellule2Gen pour gérer les cellules vides
 +
            OUString type_name=pList[n2].getValueTypeName();
 +
            if (type_name==OUString::createFromAscii("double"))
 +
            {
 +
                double valeur;
 +
                sal_Int32 partie_entiere;
 +
                pList[n2] >>= valeur;
 +
                partie_entiere=(sal_Int32)(valeur);
 +
                if (partie_entiere==valeur)
 +
                {
 +
                    // retrouver le type int perdu par OOo
 +
                    e+=(int)partie_entiere;     
 +
                }
 +
                else
 +
                {
 +
                    // non entier
 +
                    // chaine=OUString::valueOf(valeur);
 +
                    e+=valeur;
 +
                }
 +
            }
 +
            else
 +
            { 
 +
                if (type_name==OUString::createFromAscii("string"))
 +
                {
 +
                    OUString chaine;
 +
                    pList[n2] >>= chaine;
 +
                    OString aOString;
 +
                    if (chaine.getLength()!=0)
 +
                    {
 +
                        aOString = OUStringToOString (chaine,RTL_TEXTENCODING_UTF8);
 +
                        gen temp(string(aOString),0);
 +
                        e+=temp;
 +
                    }
 +
                    else
 +
                    {
 +
                    // ne rien faire, la cellule est vide
 +
                    // cela peut se produire quand on somme une zone de cellules dont certaines seront amenées
 +
                    // à se remplir plus tard.
 +
                    // aOString = OUStringToOString (OUString::createFromAscii("nulle"),RTL_TEXTENCODING_UTF8);
 +
                    }
 +
                }
 +
            }
 +
        }
 +
    }
 +
}
 +
</syntaxhighlight>
 +
This is a lot of code, but have a look to this snippet :
 +
<syntaxhighlight lang="cpp">
 +
try
 +
        {
 +
            if ( aValList[ n1 ] >>= aArraySeq )
 +
            {
 +
                // traiter la zone de cellules contenue dans aArraySeq
 +
                //
 +
                Somme_Array(e,aArraySeq);
 +
            }
 +
            else
 +
            {
 +
                // aValList[n1] ne contient qu'une valeur
 +
                e=e+Cellule2Gen(aValList[ n1 ]);
 +
            }
 +
        }
 +
</syntaxhighlight>
 +
and all the corresponding variables : here is the trick. Of course your problem is different, but start with this snippet.
  
 
=Interfacing the GSL (GNU Scientific Library)=
 
=Interfacing the GSL (GNU Scientific Library)=
  
Our goal is now to provide an example of intarfacing OOo with a scientific library. To give the possibility of accessing all the library is an enormous task that I cannot realize. I only want to show the way.
+
Our goal is now to provide an example of interfacing AOO with a scientific library. To give the possibility of accessing the whole library is an enormous task that I cannot realize. I only want to show the way.
  
 
==First step==
 
==First step==
Line 893: Line 1,003:
 
==Second step==
 
==Second step==
  
Find what gsl function do you want to wrapp and write the corresponding IDL file. For this example I am interested in finding the complex roots of a polynom. The corresponding function is « gsl_poly_complex_solve ».
+
Find what gsl function you want to wrap and write the corresponding IDL file. For this example, I am interested in finding the complex roots of a polynom. The corresponding function is « gsl_poly_complex_solve ».
  
 
The corresponding IDL file is (keeping all the old methods of the previous sections) :
 
The corresponding IDL file is (keeping all the old methods of the previous sections) :
  
<source lang="idl">
+
<syntaxhighlight lang="idl">
 
//Listing 14.14 My SDL interfacing Add-In : the IDL File
 
//Listing 14.14 My SDL interfacing Add-In : the IDL File
 
// IDL
 
// IDL
Line 918: Line 1,028:
 
};
 
};
 
};
 
};
</source>
+
</syntaxhighlight>
  
 
The name of the new method will be « poly_complex_solve »
 
The name of the new method will be « poly_complex_solve »
Line 947: Line 1,057:
  
 
Write the code and compile. The code given below only works when the polynomial coefficients are given in a line. In all the other cases it returns the same values as given in entry.
 
Write the code and compile. The code given below only works when the polynomial coefficients are given in a line. In all the other cases it returns the same values as given in entry.
<source lang="cpp">
+
<syntaxhighlight lang="cpp">
 
//Listing 14.16 New Code
 
//Listing 14.16 New Code
 
// C++
 
// C++
Line 991: Line 1,101:
 
   }
 
   }
 
}
 
}
</source>
+
</syntaxhighlight>
 
Here is the result :
 
Here is the result :
 
; Résults  
 
; Résults  
Line 1,014: Line 1,124:
  
 
where you see horizontally the polynomial coefficients and vertically the five roots with real part (left) and imaginary part (right).
 
where you see horizontally the polynomial coefficients and vertically the five roots with real part (left) and imaginary part (right).
 +
 
=Notes=
 
=Notes=
{{Documentation/Caution|If you are installing an external addin under linux and encounter a problem verify the dynamic library dependance with ldd command : <code>ldd your_addin.so</code> to see if a dynamic library fails.}}
+
{{Warn|If you are installing an external addin under Linux and encounter a problem, verify the dynamic library dependency with ldd command : <code>ldd your_addin.so</code> to see if a dynamic library fails.}}
{{Documentation/Note|Instead of going further with interfacing the GSL, bear in mind there is an advanced project of interfacing the giac library (giac is a Computer Algebra System used in [http://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/casref_en/casref_en.html XCas]) which is already working. Here is the [http://cdeval.free.fr/test/Guide%20d%27utilisation%20de%20CmathOOoCAS.odt French Documentation] of the corresponding addin. You can download [http://cdeval.free.fr/CmathOOoUpdate/CmathOOoCAS.oxt the version 1.0] for Linux or M$ Windows (same file for both version).}}
+
{{Note|Instead of going further with interfacing the GSL, bear in mind there is an advanced project of interfacing the giac library (giac is a Computer Algebra System used in [http://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/casref_en/casref_en.html XCas]) which is already working. Here is the [http://cdeval.free.fr/spip.php?article132 French Documentation] of the corresponding addin. You can download [http://cdeval.free.fr/CmathOOoUpdate/CmathOOoCAS.oxt the version 1.0] for Linux or M$ Windows (same file for both versions).}}
  
 
=Home Page=
 
=Home Page=
Line 1,030: Line 1,141:
 
* [[Extensions_Packager|Extensions Packager]] (BasicAddonBuilder from [mailto:paolomantovani@openoffice.org Paolo Mantovani])
 
* [[Extensions_Packager|Extensions Packager]] (BasicAddonBuilder from [mailto:paolomantovani@openoffice.org Paolo Mantovani])
 
* [[Calc/Add-In/Project_Type|Java Calc Add-in]]
 
* [[Calc/Add-In/Project_Type|Java Calc Add-in]]
* [[API/Samples/Java/CalcAddin|An other Java Calc Add-in example]]
+
* [[API/Samples/Java/CalcAddin|Another Java Calc Add-in example]]
* [http://www.oooforum.org/forum/viewtopic.phtml?t=29552 Add-In in C++]
+
* [https://web.archive.org/web/20130119003550/http://www.oooforum.org/forum/viewtopic.phtml?t=29552 Add-In in C++]
* [http://sc.openoffice.org/addin_howto.html How to add-in in OpenOffice.org Calc]
+
* [https://www.openoffice.org/sc/addin_howto.html How to add-in in {{AOo}} Calc]
* [http://api.openoffice.org/docs/common/ref/com/sun/star/sheet/AddIn.html Service AddIn Documentation]
+
* [https://www.openoffice.org/api/docs/common/ref/com/sun/star/sheet/AddIn.html Documentation Service AddIn]
 
* [http://perso.wanadoo.fr/moutou/MyUNODoc_HTML/UNOCppAPI13.html Constructing Components]
 
* [http://perso.wanadoo.fr/moutou/MyUNODoc_HTML/UNOCppAPI13.html Constructing Components]
* Writing a Program to Control OpenOffice.org, by Franco Pingiori — [http://www.linuxjournal.com/article/8550 Part 1] and [http://www.linuxjournal.com/article/8608 Part 2], Linux Journal
+
* Writing a Program to Control OpenOffice.org, by Franco Pingiori — [https://www.linuxjournal.com/article/8550 Part 1] and [https://www.linuxjournal.com/article/8608 Part 2], Linux Journal
 
* [[SDKInstallation | How to install the SDK and compile the C++ examples]]
 
* [[SDKInstallation | How to install the SDK and compile the C++ examples]]
 
* [[SDKCppLanguage | The UNO C++ Language]]
 
* [[SDKCppLanguage | The UNO C++ Language]]
* QuantLibAddin exports the functionality of the QuantLib analytics library as Addins on a variety of platforms including OpenOffice.org Calc and can be found here : http://quantlib.org/quantlibaddin/
+
* QuantLibAddin exports the functionality of the QuantLib analytics library as Addins on a variety of platforms including {{AOo}} Calc and can be found here : https://quantlib.org/quantlibaddin/
  
 
[[Category:Calc|Add-Ins 10 Complete]]
 
[[Category:Calc|Add-Ins 10 Complete]]
[[Category:API|Calc]]
 
 
[[Category:Add-In|Calc]]
 
[[Category:Add-In|Calc]]
[[Category:Development]]
 
 
[[Category:Cpp]]
 
[[Category:Cpp]]
 
[[Category:Uno]]
 
[[Category:Uno]]
 
[[Category:Tutorial]]
 
[[Category:Tutorial]]

Latest revision as of 11:20, 21 August 2021

An other C++ add-in example

In this article under construction, a complete and simple add-in in C++ is presented.

Introduction

Before reading this article, please read Constructing Components and SimpleCalcAddIn. In SimpleCalcAddin you will learn most of important things like the tools involved in constructing an addin, the services involved and also the corresponding references to read in the devlopper's guide.

Let's recall here how different files are created and what are the corresponding tools involved ? A drawing is beter than a long discussion :

ComponentTools.png

As you can see idlc is used to transform a .IDL-suffix file into a .urd-suffix file, regmerge to transform the previous .urd-suffix file into a .rdb-suffix file and so on... (see the corresponding SimpleCalcAddin compilation script and also Compilation Chain of a Component. The action done by the red arrow is not a file transformation but what is usually called registery. Before going further let's note that starting from an idl file, we will generate an hpp file : this file automatically defines a C++ class that we will use in the cxx file.

You can go further with component and Java here.

We choose here an other way : starting from an example of SDK slightly modified : <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent

I mean I will use this example makefile only modified to take into account the fact I only use one file instead the two provided by the example. The makefile compiles the component and installs it.

IDL File

We begin with a simple IDL file with four methods. Here is the corresponding IDL file :

// IDL
#include <com/sun/star/uno/XInterface.idl>
#include <com/sun/star/lang/XInitialization.idl>
#include <com/sun/star/lang/XServiceName.idl>
#include <com/sun/star/lang/XLocalizable.idl>
#include <com/sun/star/sheet/XAddIn.idl>
module my_module
{
  interface XSomething : com::sun::star::uno::XInterface
{ // our four methods
  string methodOne( [in] string val );
  string methodTwo( [in] string val );
  long methodThree( [in] sequence< sequence< long > > aValList );
  sequence< sequence< long > > methodFour( [in] sequence< sequence< long > > aValList );
};
service MyService2
{
interface XSomething;
interface com::sun::star::lang::XInitialization;
interface com::sun::star::lang::XServiceName;
interface com::sun::star::sheet::XAddIn;
};
};

Four methods named methodOne, methodTwo, methodThree and methodFour can be seen in this IDL file and then will be implemented.

We give the automatically generated C++ class with the cppumaker tool.

class SAL_NO_VTABLE XSomething : public ::com::sun::star::uno::XInterface
{
public:
 
    // Methods
    virtual ::rtl::OUString SAL_CALL methodOne( const ::rtl::OUString& val ) throw
                     (::com::sun::star::uno::RuntimeException) = 0; 
    virtual ::rtl::OUString SAL_CALL methodTwo( const ::rtl::OUString& val ) throw 
                     (::com::sun::star::uno::RuntimeException) = 0;
    virtual sal_Int32 SAL_CALL methodThree( const ::com::sun::star::uno::Sequence< 
                     ::com::sun::star::uno::Sequence< sal_Int32 > >& aValList ) throw
                           (::com::sun::star::uno::RuntimeException) = 0;
    virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< 
                      sal_Int32 > > SAL_CALL methodFour( const ::com::sun::star::uno::Sequence<
                           ::com::sun::star::uno::Sequence< sal_Int32 > >& aValList ) throw 
                               (::com::sun::star::uno::RuntimeException) = 0;
};

where again the four methods are seen but in C++ language. Note before going further that parameters of the methods are always marked as const but passed by reference (in C++ style with & operator). It's time to give the corresponding code.

Implementing in C++ the corresponding four member Functions

We first give the C++ code of the four methods presented in the previous IDL file.

The two first member functions

The two first methods are similar:

// C++
OUString MyService2Impl::methodOne( OUString const & str )
throw (RuntimeException)
{
  return OUString( RTL_CONSTASCII_USTRINGPARAM(
    "called methodOne() of MyService2 implementation: ") ) + m_arg + str;
}
 
OUString MyService2Impl::methodTwo( OUString const & str )throw (RuntimeException)
{
  return OUString( RTL_CONSTASCII_USTRINGPARAM(
    "called methodTwo() of MyService2 implementation: ") ) + m_arg + str;
}

They only take a string (from an AOO Calc Cell) and add a message and put all the message+string in the result cell.

The third member Function

The third member function is more complicated: it returns a value calculated from a cellrange (the sum).

// C++
sal_Int32 MyService2Impl::methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
throw (RuntimeException)
{ 
  sal_Int32 n1 = 0;
  sal_Int32 n2 = 0;
  sal_Int32 nE1 = aValList.getLength();
  sal_Int32 nE2 = 0;
  sal_Int32 temp=0;
  for( n1 = 0 ; n1 < nE1 ; n1++ )
  {
    const Sequence< sal_Int32 > rList = aValList[ n1 ];
    nE2 = rList.getLength();
    const sal_Int32* pList = rList.getConstArray();
    for( n2 = 0 ; n2 < nE2 ; n2++ )
    {
      temp += pList[ n2 ];
    }
  }
  return temp;
}

The fourth member Function

The goal of the fourth member function is to show how we can implement a matrix function: starting from a cellrange and obtaining a cellrange.

//C++
//It's a matrix operation should be called like : {=METHODFOUR(A1:B4)}
Sequence< Sequence< sal_Int32 > > MyService2Impl::methodFour(const Sequence< Sequence< sal_Int32 > > &aValList )
throw (RuntimeException)
{ sal_Int32 n1, n2;
  sal_Int32 nE1 = aValList.getLength();
  sal_Int32 nE2;
  Sequence< Sequence< sal_Int32 > > temp = aValList;
  for( n1 = 0 ; n1 < nE1 ; n1++ )
  {
    Sequence< sal_Int32 > rList = temp[ n1 ];
    nE2 = rList.getLength();
    for( n2 = 0 ; n2 < nE2 ; n2++ )
    {
      rList[ n2 ] += 4;
    }
    temp[n1]=rList;
  }
  return temp;
}

What is done by this example is not great : only add four to every cell of the cellrange and put the result in another cellrange.

Every method are related to a C++ class named MyService2Impl. Let's now give the corresponding class definition.

The C++ Class

We use an helper to implement our class and then we don't need to implement all the interfaces : no need to implement XInterface, XTypeProvider, XWeak for instance. We give now the corresponding C++ code.

//C++
class MyService2Impl : public ::cppu::WeakImplHelper5<
      ::my_module::XSomething, lang::XServiceInfo, lang::XInitialization, lang::XServiceName,
	XAddIn>
{
    OUString m_arg;
public:
    // no need to implement XInterface, XTypeProvider, XWeak
 
    // XInitialization will be called upon createInstanceWithArguments[AndContext]()
    virtual void SAL_CALL initialize( Sequence< Any > const & args )
        throw (Exception);
    // XSomething
    virtual OUString SAL_CALL methodOne( OUString const & str )
        throw (RuntimeException);
    virtual OUString SAL_CALL methodTwo( OUString const & str )
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
        throw (RuntimeException);
    virtual Sequence< Sequence< sal_Int32 > > SAL_CALL methodFour(
				const Sequence< Sequence< sal_Int32 > > &aValList )
	throw (RuntimeException);
 
    // XServiceName
    virtual OUString SAL_CALL getServiceName() throw( uno::RuntimeException );
    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName()
        throw (RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName )
        throw (RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames()
        throw (RuntimeException);
    // XAddIn
    virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) 
                throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) 
                throw( uno::RuntimeException );
    virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) 
                throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getArgumentDescription(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getProgrammaticCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    // XLocalizable
    virtual void SAL_CALL setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException );
    virtual lang::Locale SAL_CALL getLocale() throw( uno::RuntimeException );
};

The code is not complete with the four methods as you can see in the class declaration. Only your own XSomething interface is already implemented (see above). You can wonder how is it possible to find all the methods in the class ? As you will learn below, the IDL files corresponding to interfaces have to be read for that. The number of interfaces you have to implement is dependant of your goal. If you want to create a component you have to implement XInterface interface and add more code, if you want to make a scriptable component you still add more code (and more interfaces : XTypeprovider, XServiceInfo and XWeak) and if you want to create an AddIn you have to add more (XAddin, XServiceName). This is explained in Developer's Guide. We first begin with a typical addin interface : XAddin.

The XAddIn Interface

Every add-in has to implement the com.sun.star.sheet.XAddIn interface. As usual, what you have to do is described by an IDL file (or you can read the previous C++ class definition) :

// IDL
module com { module sun { module star { module sheet {
interface XAddIn: com::sun::star::lang::XLocalizable
{
  string getProgrammaticFuntionName( [in] string aDisplayName );
  string getDisplayFunctionName( [in] string aProgrammaticName );
  string getFunctionDescription( [in] string aProgrammaticName );
  string getDisplayArgumentName(
  [in] string aProgrammaticFunctionName,
  [in] long nArgument );
  string getArgumentDescription(
  [in] string aProgrammaticFunctionName,
  [in] long nArgument );
  string getProgrammaticCategoryName( [in] string aProgrammaticFunctionName );
  string getDisplayCategoryName( [in] string aProgrammaticFunctionName );
};
}; }; }; };

The corresponding C++ code is given now without explanation (for the moment)

// C++
// XAddIn
OUString SAL_CALL MyService2Impl::getProgrammaticFuntionName( const OUString& aDisplayName ) throw(
                uno::RuntimeException )
{
// not used by calc
// (but should be implemented for other uses of the AddIn service)
  return OUString();
}
 
OUString SAL_CALL MyService2Impl::getDisplayFunctionName( const OUString& aProgrammaticName ) throw(
                uno::RuntimeException )
{ // a nested if implementation would be better
  OUString aProgName, aRet;
  aProgName = aProgrammaticName;
  if (aProgName.equalsAscii("methodOne")) aRet = OUString::createFromAscii("method1");
  if (aProgName.equalsAscii("methodTwo")) aRet = OUString::createFromAscii("method2");
  if (aProgName.equalsAscii("methodThree")) aRet = OUString::createFromAscii("method3");
  if (aProgName.equalsAscii("methodFour")) aRet = OUString::createFromAscii("method4");
  return aRet;
}
 
OUString SAL_CALL MyService2Impl::getFunctionDescription( const OUString& aProgrammaticName ) throw(
                uno::RuntimeException )
{ // a nested if implementation would be better
  OUString aRet;
  if (aProgrammaticName.equalsAscii("methodOne"))
    aRet = OUString::createFromAscii("methodOne() : 1st try");
  if (aProgrammaticName.equalsAscii("methodTwo"))
    aRet = OUString::createFromAscii("methodTwo() : 1st try");
  if (aProgrammaticName.equalsAscii("methodThree"))
    aRet = OUString::createFromAscii("methodThree() : 1st try");
  if (aProgrammaticName.equalsAscii("methodFour")) 
    aRet = OUString::createFromAscii("methodFour() : 1st try");
  return aRet;
}
 
OUString SAL_CALL MyService2Impl::getDisplayArgumentName(
const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{ // (requis) is added in French and probably (required) in English (see the snapshot below)
  OUString aRet;
  if (aProgrammaticName.equalsAscii("methodOne")||aProgrammaticName.equalsAscii("methodTwo"))
    aRet = OUString::createFromAscii("a string");
  if (aProgrammaticName.equalsAscii("methodThree")||aProgrammaticName.equalsAscii("methodFour"))
    aRet = OUString::createFromAscii("a Cell Range");
  return aRet;
}
 
OUString SAL_CALL MyService2Impl::getArgumentDescription(
const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{
  OUString aRet;
  if (aProgrammaticName.equalsAscii("methodOne")||aProgrammaticName.equalsAscii("methodTwo"))
    aRet = OUString::createFromAscii("method1/2:a string or a cell with a string is required");
  if (aProgrammaticName.equalsAscii("methodThree")||aProgrammaticName.equalsAscii("methodFour"))
    aRet = OUString::createFromAscii("method3/4:a cell range is required");
  return aRet;
}
 
OUString SAL_CALL MyService2Impl::getProgrammaticCategoryName(const OUString& aProgrammaticName ) throw(
                 uno::RuntimeException )
{
  OUString aRet( RTL_CONSTASCII_USTRINGPARAM("Add-In"));
  return aRet;
}
 
OUString SAL_CALL MyService2Impl::getDisplayCategoryName(const OUString& aProgrammaticName ) throw(
                  uno::RuntimeException )
{
  return getProgrammaticCategoryName( aProgrammaticName );
}

To understand what this code is related to, we provide a snapshot : read it carefully to see where the code works and provides the strings to print out in the autopilot frame :

Addin2.png

I have used method one as you can see above the autopilot frame, and I am ready to use method two. What you can read in the autopilot comes directly from the C++ code except the French word "requis" which is probably translated by "required" in an English OpenOffice version.

The XServiceName Interface

Again the corresponding IDL file indicates what we have to do :

// IDL
module com { module sun { module star { module lang {
interface XServiceName: com::sun::star::uno::XInterface
{
  string getServiceName();
};
}; }; }; };

This interface is simple : one method :

//C++
// XServiceName
OUString SAL_CALL MyService2Impl::getServiceName() throw( uno::RuntimeException )
{
// name of specific AddIn service
  return OUString::createFromAscii( "my_module.MyService2" );
}

See com.sun.star.lang.XServiceName for the complete documentation.

The XServiceInfo Interface

This com.sun.star.lang.XServiceInfo interface may be avoided. You have to implement it only if you want that your add-in is also scriptable : you can call it from OOoBasic for instance. It's time to present the corresponding IDL file :

// IDL
module com {  module sun {  module star {  module lang {
interface XServiceInfo: com::sun::star::uno::XInterface
{
	string getImplementationName();
	boolean supportsService( [in] string ServiceName );
	sequence<string> getSupportedServiceNames();
};
}; }; }; };

Here is the corresponding C++ code :

// C++
// XServiceInfo implementation
OUString MyService2Impl::getImplementationName()
throw (RuntimeException)
{
// unique implementation name
  return OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService2") );
}
 
sal_Bool MyService2Impl::supportsService( OUString const & serviceName )
throw (RuntimeException)
{
// this object only supports one service, so the test is simple
  return serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ) ||
         serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") );
}
 
Sequence< OUString > MyService2Impl::getSupportedServiceNames()
throw (RuntimeException)
{
  return getSupportedServiceNames_MyService2Impl();
}
 
Reference< XInterface > SAL_CALL create_MyService2Impl(Reference< XComponentContext > const & xContext)
SAL_THROW( () )
{
  return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
}

XInitialisation Interface

This com.sun.star.lang.XInitialization interface could be removed without problem. I will remove it completely in the future. Here is the corresponding C++ code.

// C++
// XInitialization implemention
void MyService2Impl::initialize( Sequence< Any > const & args )
    throw (Exception)
{
    if (1 != args.getLength())
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("give a string instanciating this component!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
    if (! (args[ 0 ] >>= m_arg))
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("no string given as argument!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
}

XLocalizable Interface

This com.sun.star.lang.XLocalizable interface is particular : we have let it in the class definition and give now the corresponding code, but because the code does nothing, we don't add it as sixth interface when using the helper.

// C++
// XLocalizable
void SAL_CALL MyService2Impl::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException )
{
//    aFuncLoc = eLocale;
//    InitData();     // change of locale invalidates resources!
}
 
lang::Locale SAL_CALL MyService2Impl::getLocale() throw( uno::RuntimeException )
{
//    return aFuncLoc;
}

Making a registerable component

An add-in has to be registered. In fact if you want to use external code, you have to work with a registry. If you use your external code with OoBasic you call it a component and if you use it with AOo Calc you call it an add-in. But both are very similar. In my knowledge the only exception to this registry rule is OoBasic (under Windows) which can call directly an external DLL.

To supplement the above code we have to add :

// C++
/* shared lib exports implemented without helpers in service_impl1.cxx */
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    {
        create_MyService2Impl, getImplementationName_MyService2Impl,
        getSupportedServiceNames_MyService2Impl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};
}
 
extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
    lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry )
{
    return ::cppu::component_writeInfoHelper(
        xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
    sal_Char const * implName, lang::XMultiServiceFactory * xMgr,
    registry::XRegistryKey * xRegistry )
{
    return ::cppu::component_getFactoryHelper(
        implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}

The complete Code

It's time to give the C++ complete code :

//Listing 9 My first Add-In
//C++
#include <cppuhelper/implbase5.hxx> // "5" implementing five interfaces
#include <cppuhelper/factory.hxx>
#include <cppuhelper/implementationentry.hxx>
 
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XServiceName.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/sheet/XAddIn.hpp>
#include <com/sun/star/lang/XLocalizable.hpp>
#include <my_module/XSomething.hpp>
 
 
using namespace ::rtl; // for OUString
using namespace ::com::sun::star; // for odk interfaces
using namespace ::com::sun::star::uno; // for basic types
using namespace ::com::sun::star::sheet;
 
 
namespace my_sc_impl
{
 
static Sequence< OUString > getSupportedServiceNames_MyService2Impl()
{
	static Sequence < OUString > *pNames = 0;
	if( ! pNames )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( !pNames )
		{
			static Sequence< OUString > seqNames(2);
			seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM("my_module.MyService2"));
                        seqNames.getArray()[1] = OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.sheet.AddIn"));
			pNames = &seqNames;
		}
	}
	return *pNames;
}
 
static OUString getImplementationName_MyService2Impl()
{
	static OUString *pImplName = 0;
	if( ! pImplName )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pImplName )
		{
			static OUString implName( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_implementation.MyService2") );
			pImplName = &implName;
		}
	}
	return *pImplName;
}
 
class MyService2Impl : public ::cppu::WeakImplHelper5<
      ::my_module::XSomething, lang::XServiceInfo, lang::XInitialization, lang::XServiceName,
	/*lang::XLocalizable, */XAddIn>
{
    OUString m_arg;
public:
    // focus on three given interfaces,
    // no need to implement XInterface, XTypeProvider, XWeak
 
    // XInitialization will be called upon createInstanceWithArguments[AndContext]()
    virtual void SAL_CALL initialize( Sequence< Any > const & args )
        throw (Exception);
    // XSomething
    virtual OUString SAL_CALL methodOne( OUString const & str )
        throw (RuntimeException);
    // **********************ADDED
    virtual OUString SAL_CALL methodTwo( OUString const & str )
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
        throw (RuntimeException);
    virtual Sequence< Sequence< sal_Int32 > > SAL_CALL methodFour(
				const Sequence< Sequence< sal_Int32 > > &aValList )
	throw (RuntimeException);
    // ********************** END ADDED
    // XServiceName
    virtual OUString SAL_CALL getServiceName() throw( uno::RuntimeException );
    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName()
        throw (RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName )
        throw (RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames()
        throw (RuntimeException);
    // XAddIn
    virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getArgumentDescription(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getProgrammaticCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    // XLocalizable
    virtual void SAL_CALL MyService2Impl::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException );
    virtual lang::Locale SAL_CALL MyService2Impl::getLocale() throw( uno::RuntimeException );
};
 
// XInitialization implemention
void MyService2Impl::initialize( Sequence< Any > const & args )
    throw (Exception)
{
    if (1 != args.getLength())
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("give a string instanciating this component!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
    if (! (args[ 0 ] >>= m_arg))
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("no string given as argument!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
}
 
// XServiceName
OUString SAL_CALL MyService2Impl::getServiceName() throw( uno::RuntimeException )
{
    // name of specific AddIn service
    return OUString::createFromAscii( "my_module.MyService2" );
}
// XSomething implementation
OUString MyService2Impl::methodOne( OUString const & str )
    throw (RuntimeException)
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM(
        "called methodOne() of MyService2 implementation: ") ) + m_arg + str;
}
// **********************ADDED 
 
OUString MyService2Impl::methodTwo( OUString const & str )
    throw (RuntimeException)
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM(
        "called methodTwo() of MyService2 implementation: ") ) + m_arg + str;
}
 
 
sal_Int32 MyService2Impl::methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
    throw (RuntimeException)
{ 	sal_Int32		n1, n2;
	sal_Int32		nE1 = aValList.getLength();
	sal_Int32		nE2;
	sal_Int32 temp=0;
	for( n1 = 0 ; n1 < nE1 ; n1++ )
	{
		const Sequence< sal_Int32 >	rList = aValList[ n1 ];
		nE2 = rList.getLength();
		const sal_Int32*	pList = rList.getConstArray();
		for( n2 = 0 ; n2 < nE2 ; n2++ )
		{
			temp += pList[ n2 ];
		}
	}
	return temp;
}
 
//It's a matrix operation  should be called like : {=METHODFOUR(A1:B4)}
Sequence< Sequence< sal_Int32 > > MyService2Impl::methodFour(
		const Sequence< Sequence< sal_Int32 > > &aValList )throw (RuntimeException)
{ 	sal_Int32		n1, n2;
	sal_Int32		nE1 = aValList.getLength();
	sal_Int32		nE2;
	Sequence< Sequence< sal_Int32 > > temp = aValList;
	for( n1 = 0 ; n1 < nE1 ; n1++ )
	{
		Sequence< sal_Int32 >	rList = temp[ n1 ];
		nE2 = rList.getLength();
		for( n2 = 0 ; n2 < nE2 ; n2++ )
		{
			rList[ n2 ] += 4;
		}
		temp[n1]=rList;
	}
	return temp;
}
 
// ********************** END ADDED 
 
// XAddIn
 
OUString SAL_CALL MyService2Impl::getProgrammaticFuntionName( const OUString& aDisplayName ) throw( uno::RuntimeException )
{
    //  not used by calc
    //  (but should be implemented for other uses of the AddIn service)
    return OUString();
}
 
OUString SAL_CALL MyService2Impl::getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    OUString aProgName, aRet;
    aProgName = aProgrammaticName;
    if (aProgName.equalsAscii("methodOne")) aRet = OUString::createFromAscii("method1");
    if (aProgName.equalsAscii("methodTwo")) aRet = OUString::createFromAscii("method2");
    if (aProgName.equalsAscii("methodThree")) aRet = OUString::createFromAscii("method3");
    if (aProgName.equalsAscii("methodFour")) aRet = OUString::createFromAscii("method4");
 
    return aRet;
}
 
OUString SAL_CALL MyService2Impl::getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    OUString aRet;
    if (aProgrammaticName.equalsAscii("methodOne")) 
	aRet = OUString::createFromAscii("methodOne() : 1st try");
    if (aProgrammaticName.equalsAscii("methodTwo")) 
	aRet = OUString::createFromAscii("methodTwo() : 1st try");
    if (aProgrammaticName.equalsAscii("methodThree")) 
	aRet = OUString::createFromAscii("methodThree() : 1st try");
    return aRet;
}
 
OUString SAL_CALL MyService2Impl::getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{   
    OUString aRet;
    if (aProgrammaticName.equalsAscii("methodOne")||aProgrammaticName.equalsAscii("methodTwo")) 
	aRet = OUString::createFromAscii("a string");
    if (aProgrammaticName.equalsAscii("methodThree")||aProgrammaticName.equalsAscii("methodFour")) 
	aRet = OUString::createFromAscii("a Cell Range");
    return aRet;
}
 
OUString SAL_CALL MyService2Impl::getArgumentDescription(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{
    OUString aRet;
    if (aProgrammaticName.equalsAscii("methodOne")||aProgrammaticName.equalsAscii("methodTwo")) 
	aRet = OUString::createFromAscii("method1/2:a string or a cell with a string is required");
    if (aProgrammaticName.equalsAscii("methodThree")||aProgrammaticName.equalsAscii("methodFour")) 
	aRet = OUString::createFromAscii("method3/4:a cell range is required");
    return aRet;
}
 
OUString SAL_CALL MyService2Impl::getProgrammaticCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    OUString aRet( RTL_CONSTASCII_USTRINGPARAM("Add-In"));
    return aRet;
}
 
OUString SAL_CALL MyService2Impl::getDisplayCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    return getProgrammaticCategoryName( aProgrammaticName );
}
 
// XServiceInfo implementation
OUString MyService2Impl::getImplementationName()
    throw (RuntimeException)
{
    // unique implementation name
    return OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService2") );
}
sal_Bool MyService2Impl::supportsService( OUString const & serviceName )
    throw (RuntimeException)
{
    // this object only supports one service, so the test is simple
    // modified *********
    return serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ) ||
           serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") );
}
Sequence< OUString > MyService2Impl::getSupportedServiceNames()
    throw (RuntimeException)
{
	return getSupportedServiceNames_MyService2Impl();
}
 
Reference< XInterface > SAL_CALL create_MyService2Impl(
    Reference< XComponentContext > const & xContext )
    SAL_THROW( () )
{
    return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
}
 
// XLocalizable
void SAL_CALL MyService2Impl::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException )
{
//    aFuncLoc = eLocale;
//    InitData();     // change of locale invalidates resources!
}
 
lang::Locale SAL_CALL MyService2Impl::getLocale() throw( uno::RuntimeException )
{
//    return aFuncLoc;
}
 
 
}
 
/* shared lib exports implemented without helpers in service_impl1.cxx */
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    {
        create_MyService2Impl, getImplementationName_MyService2Impl,
        getSupportedServiceNames_MyService2Impl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};
}
 
extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
    lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry )
{
    return ::cppu::component_writeInfoHelper(
        xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
    sal_Char const * implName, lang::XMultiServiceFactory * xMgr,
    registry::XRegistryKey * xRegistry )
{
    return ::cppu::component_getFactoryHelper(
        implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}

We have learnt now how things works and particularly how to manage sequence of sequence parameters or returned value.

Documentation note.png This code is to set in <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent path because there is no add-in example in the SDK. You have to slightly modify the corresponding makefile remove a source file to check this example.

Utilities

In order to provide utilities to interface AOO to other external libraries, we will provide in this section any utilities to transform classical variables into UNO variables.

Transforming sequence of sequence into array

If you plan to extend AOOCalc in any way, you will probably use an external library, for instance a C library like GSL. You will notice that the variables are in general arrays of double and not sequence. Have a look to the case of Listing below where you see an imaginary method « foo ». The point is we use a sequence of sequence as parameter and we want to transform it into 2D array.

//C+
Sequence< Sequence< double > > MyService2Impl::foo(
		const Sequence< Sequence< double > > &aValList )throw (RuntimeException)
{ 		sal_Int32		nE1 = aValList.getLength();
	sal_Int32		nE2;
 
	Sequence< double >	rList = aValList[ 0 ];
	nE2 = rList.getLength();
	double table[nE1][nE2];
	for( sal_Int32 n1 = 0 ; n1 < nE1; n1++ )
	{
		for (sal_Int32 n2=0;n2<nE2;n2++)
			table[n1][n2]=aValList[n1][n2];
	}
// we have a 2D array now
//!!!!!!!! This code is wrong : double table[nE1][nE2]; with nE1 and nE2 
//!!!!!!!! unknown at the compilation time. !!!!!!!!
// I have used this code with success but I don't know why ? 
// Perhaps the compiler use a constructor in this case ?
// I will have a look as sooner as possible

At the end of this program we have a 2D array named « table » and we show how to construct it. (I have tried a way using getArray() but without success).

As mentioned in the comments, this code has a poor quality in my knowledge. At the moment, I provide other code here

Transforming an Array into Sequence of Sequence

Imagine your calculus provides a 1D array named « z » and you want to construct a sequence of sequence before returning it. Here is what you could do :

//C+
...
// here a line is constructed with size (nE2-1)*2
	Sequence< double >	solList1D(z,(nE2-1)*2);
	Sequence< Sequence< double > > solList2D(1);
	solList2D[0]=solList1D;
	return solList2D;

A similar problem encountered later has to be solved differently : I start from an 1D array but this array contains, real part and imaginary part « The n-1 roots are returned in the packed complex array z of length 2(n-1), alternating real and imaginary parts » I want to show the result in Calc but with two rows (real part and imaginary part) with as many lines as necessary. Here is the way :

//C+
...
// two rows and nE2-1 lines starting from an array "z" with (nE2-1)*2 lements
	Sequence< double >	solList1D(2);
		Sequence< Sequence< double > > solList2D(nE2-1);
		for( int j=0;j<((nE2-1)<<1);j+=2){ // (nE2-1)<<1 is faster than (nE2-1)*2
			for( int i=0; i<2 ; i++){
				solList1D[i]=z[i+j];
			}
			solList2D[j>>1]=solList1D;//j >> 2 is faster than j/2
		}
		return solList2D;

Sometimes we have to use Standard Template Library because the library we want to interface is in C++. Eric Ehlers has interfaced a C++ library (QuantLib project) and then provide any interesting code here. See particularly the file calcutils.cpp. I have reproduct part of the code here but with using template.

Managing simultaneously Sequence of Sequence and Sequence

It's possible to encounter any buildin functions which can cope with cell range and with a list of values. Because the former is a Sequence of Sequence and the latter a simple Sequence, the question is how to manage such a data structure in an addin function ?

Christophe Devalland was facing this problem when interfacing the XCas library (See the note below) and solves it with help of Niklas Nebel in this way :

Any CASImpl::cadd(const Sequence< Any > &aValList )
    throw (RuntimeException)
{     sal_Int32 n1;
    sal_Int32 nE1 = aValList.getLength();
    Sequence< Sequence< Any > > aArraySeq;
    gen e;
    giac::context * contextptr=0;
    e=0;
    for( n1 = 0 ; n1 < nE1 ; n1++ )
    {
        try
        {
            if ( aValList[ n1 ] >>= aArraySeq )
            {
                // traiter la zone de cellules contenue dans aArraySeq
                //
                Somme_Array(e,aArraySeq);
            }
            else
            {
                // aValList[n1] ne contient qu'une valeur
                e=e+Cellule2Gen(aValList[ n1 ]);
            }
        }
        catch (std::runtime_error & erreur)
        {
            return makeAny(OUString::createFromAscii("Erreur : ")+OUString::createFromAscii(erreur.what()));
        }
    }
    e=giac::simplify(e,contextptr);
    return Gen2any(e);
}
void Somme_Array(gen & e, Sequence< Sequence< Any > > & aArraySeq)
{
    // somme le contenu de la zone de cellules désignée par aArraySeq
    sal_Int32        n1, n2;
    sal_Int32        nE1 = aArraySeq.getLength();
    sal_Int32        nE2;
    for( n1 = 0 ; n1 < nE1 ; n1++ )
    {
        const Sequence< Any >    rList = aArraySeq[ n1 ];
        nE2 = rList.getLength();
        const Any*    pList = rList.getConstArray();
        for( n2 = 0 ; n2 < nE2 ; n2++ )
        {
            // version modifiée de Cellule2Gen pour gérer les cellules vides
            OUString type_name=pList[n2].getValueTypeName();
            if (type_name==OUString::createFromAscii("double"))
            {
                double valeur;
                sal_Int32 partie_entiere;
                pList[n2] >>= valeur;
                partie_entiere=(sal_Int32)(valeur);
                if (partie_entiere==valeur)
                {
                    // retrouver le type int perdu par OOo
                    e+=(int)partie_entiere;       
                }
                else
                {
                    // non entier
                    // chaine=OUString::valueOf(valeur);
                    e+=valeur;
                }
            }
            else
            {   
                if (type_name==OUString::createFromAscii("string"))
                {
                    OUString chaine;
                    pList[n2] >>= chaine;
                    OString aOString;
                    if (chaine.getLength()!=0)
                    {
                        aOString = OUStringToOString (chaine,RTL_TEXTENCODING_UTF8);
                        gen temp(string(aOString),0);
                        e+=temp;
                    }
                    else
                    {
                    // ne rien faire, la cellule est vide
                    // cela peut se produire quand on somme une zone de cellules dont certaines seront amenées
                    // à se remplir plus tard.
                    // aOString = OUStringToOString (OUString::createFromAscii("nulle"),RTL_TEXTENCODING_UTF8);
                    }
                }
            }
        }
    }
}

This is a lot of code, but have a look to this snippet :

try
        {
            if ( aValList[ n1 ] >>= aArraySeq )
            {
                // traiter la zone de cellules contenue dans aArraySeq
                //
                Somme_Array(e,aArraySeq);
            }
            else
            {
                // aValList[n1] ne contient qu'une valeur
                e=e+Cellule2Gen(aValList[ n1 ]);
            }
        }

and all the corresponding variables : here is the trick. Of course your problem is different, but start with this snippet.

Interfacing the GSL (GNU Scientific Library)

Our goal is now to provide an example of interfacing AOO with a scientific library. To give the possibility of accessing the whole library is an enormous task that I cannot realize. I only want to show the way.

First step

The first step is to install the GSL. See the GSL documentation

Second step

Find what gsl function you want to wrap and write the corresponding IDL file. For this example, I am interested in finding the complex roots of a polynom. The corresponding function is « gsl_poly_complex_solve ».

The corresponding IDL file is (keeping all the old methods of the previous sections) :

//Listing 14.14 My SDL interfacing Add-In : the IDL File
// IDL
module my_module
{
interface XSomething : com::sun::star::uno::XInterface
{
  string methodOne( [in] string val );
  string methodTwo( [in] string val );
  long methodThree( [in] sequence< sequence< long > > aValList );
  sequence< sequence< long > > methodFour( [in] sequence< sequence< long > > aValList );
  sequence< sequence< double > > poly_complex_solve([in] sequence< sequence< double > > aValList );
};
service MyService2
{
  interface XSomething;
  interface com::sun::star::lang::XInitialization;
  interface com::sun::star::lang::XServiceName;
  interface com::sun::star::sheet::XAddIn;
};
};

The name of the new method will be « poly_complex_solve »

Third Step

You have to modify the makefile to link your addin with the GSL libraries :

#Listing 14.15 New MakeFile
....
ifeq "$(OS)" "WIN"
$(SHAREDLIB_OUT)/%.$(SHAREDLIB_EXT) : $(SLOFILES) $(OUT_COMP_GEN)/%.def
-$(MKDIR) $(subst /,$(PS),$(@D))
$(LINK) $(LIBRARY_LINK_FLAGS) /OUT:$@ /MAP:$(OUT_COMP_GEN)/$(subst $(SHAREDLIB_EXT),map,$(@F)) \
/DEF:$(OUT_COMP_GEN)/$(subst $(SHAREDLIB_EXT),def,$(@F)) $(SLOFILES) \
$(CPPUHELPERLIB) $(CPPULIB) $(SALLIB) $(STLPORTLIB) msvcrt.lib kernel32.lib
else
$(SHAREDLIB_OUT)/%.$(SHAREDLIB_EXT) : $(SLOFILES)
-$(MKDIR) $(subst /,$(PS),$(@D))
$(LINK) $(LIBRARY_LINK_FLAGS) $(LINK_LIBS) -o $@ $^\
$(CPPUHELPERLIB) $(CPPULIB) $(SALLIB) $(STLPORTLIB) $(STC++LIB) -lgsl -lgslcblas -lm
# $(CPPUHELPERLIB)
endif
....

Step Four

Write the code and compile. The code given below only works when the polynomial coefficients are given in a line. In all the other cases it returns the same values as given in entry.

//Listing 14.16 New Code
// C++
....
#include <gsl/gsl_poly.h>
....
Sequence< Sequence< double > > MyService2Impl::poly_complex_solve(
const Sequence< Sequence< double > > &aValList )throw (RuntimeException)
{
  sal_Int32 nE1 = aValList.getLength();
  sal_Int32 nE2;
  Sequence< double > rList = aValList[ 0 ];
  nE2 = rList.getLength();
  double table[nE1][nE2];
  for( sal_Int32 n1 = 0 ; n1 < nE1; n1++ )
  {
   for (sal_Int32 n2=0;n2<nE2;n2++)
     table[n1][n2]=aValList[n1][n2];
  }
// I have a 2D nE1xnE2 table here
  if (nE1==1){ // real coefficients horizontally disposed
    double z[(nE2-1)*2];
    gsl_poly_complex_workspace * w = gsl_poly_complex_workspace_alloc(nE2);
    gsl_poly_complex_solve(table[0],nE2,w,z);
    gsl_poly_complex_workspace_free(w);
    Sequence< double > solList1D(2);
    Sequence< Sequence< double > > solList2D(nE2-1);
    for( int j=0;j<((nE2-1)<<1);j+=2){
      for( int i=0; i<2 ; i++){
         solList1D[i]=z[i+j]; 
      }
      solList2D[j>>1]=solList1D;
    }
    return solList2D;
  } else
  if (nE2==1){ // real coefficients vertically disposed : doesn't work at the moment
    Sequence< Sequence< double > > temp = aValList; // return the coefficients
   return temp;
  } else {
// If here : error
    Sequence< Sequence< double > > temp = aValList; // return the coefficients
    return temp;
  }
}

Here is the result :

Résults
x^4 x^5
-1 0 0 0 0 1
-0.81 0.59
-0.81 -0.59
0.31 0.95
0.31 -0.95
1 0

where you see horizontally the polynomial coefficients and vertically the five roots with real part (left) and imaginary part (right).

Notes

Documentation caution.png If you are installing an external addin under Linux and encounter a problem, verify the dynamic library dependency with ldd command : ldd your_addin.so to see if a dynamic library fails.
Documentation note.png Instead of going further with interfacing the GSL, bear in mind there is an advanced project of interfacing the giac library (giac is a Computer Algebra System used in XCas) which is already working. Here is the French Documentation of the corresponding addin. You can download the version 1.0 for Linux or M$ Windows (same file for both versions).

Home Page

HomePageCpp.png Return to Document Home page

See also

This article is under construction and if you want to go further, for the moment have a look to the following references :

Personal tools