How to import external libraries to CINT
Because of Cint limitation, special technique is needed to import
arbitrary C/C++ libraries into Cint. Assuming you already have basic
knowledge about cint/makecint, this document describes how to import
external libraries using makecint.
Rootcint is a version of makecint which is specialized for ROOT framework.
This document does not focus on use of rootcint. But basic technique
explained here will be applicable to rootcint too. Rootcint is different
from makecint in following aspects.
- Rootcint knows classes and environments of ROOT framework.
- provides object persistence by automatically generated Streamer()
############################################################################
# Assumptions
############################################################################
Here are assumptions in this document.
- A user have useful C/C++ library which he/she wants to use in the
interpreter.
- The library is provided by either source code, object file, static
library or shared library. Let's call it 'extlib.cxx' , 'extlib.o'
, 'extlib.a' or 'extlib.so'.
- The library provides header file. Let's call it 'extlib.h'.
- The user knows interface to the library he/she wants to use.
############################################################################
# First, you could try this...
############################################################################
If you the header file extlib.h is simple enough, you could simply feed
extlib.h to makecint.
If extlib.cxx is provided,
$ makecint -mk makefile -dl extlib.dll -H extlib.h -C++ extlib.cxx
$ make -f makefile
If extlib.o , extlib.a or extlib.so is provided.
$ makecint -mk makefile -dl extlib.dll -H extlib.h -l extlib.a
$ make -f makefile
If macro function is used in extlib.h, you will find problems.
You can try using preprocessor by adding '-p' option.
If extlib.cxx is provided,
$ makecint -mk makefile -dl extlib.dll -p -H extlib.h -C++ extlib.cxx
$ make -f makefile
If extlib.o , extlib.a or extlib.so is provided.
$ makecint -mk makefile -dl extlib.dll -p -H extlib.h -l extlib.a
$ make -f makefile
############################################################################
# Using preprocessor might help...
############################################################################
Unfortunately, above works only if you are very lucky. Many of the library
headers include something cint can not understand. Typical reasons are
- Parsing limitation of Cint
Cint is improved over the years. This case is decreasing, however,
still not perfect. You can request improvements to the author.
- Non-standard symbols are used in the library
For portability reason, Cint only supports what is defined in
ANSI C and ANSI/ISO C++, or at least subset of it. Anything out
of this standard may not be understood by Cint.
Please proceed to following sections if you find many errors. According
to your situation, you can select Workaround 1 or 2.
Workaround 1: Creating dummy header
- You can not or do not want to modify the original header file.
- Library is not under your control. It may be modified by other person
without notice.
- There are too many errors and it looks unrealistic to start from
the original header file.
Workaround 2: Modifying library header file
- You can modify the original header file.
- You are the originator of the library. You have 100% control of it.
- You find simple errors only.
############################################################################
# Workaround 1: Creating dummy header
############################################################################
In many cases, original header file includes things that cint can not
understand. This is not only because of cint limitation, but also by
compiler specific keywords and non-standard symbols. There is very
little hope to modify the original header. Besides, you want to keep
the original library unchanged for maintenance reason.
As a workaround, you need to create a dummy header which describes
interface of the external library. Let's name it 'extlibdmy.h' in this
document.
1. Dummy header overview
A dummy header must consists of 2 parts.
(1) Part where C/C++ compiler reads
(2) Part where cint/makecint compiler reads
These are segmented by '#ifdef __MAKECINT__'
////////////////////////////////////////////////////
// extlibdmy.h
////////////////////////////////////////////////////
#ifndef EXTLIB
#define EXTLIB
#ifndef __MAKECINT__
^
| // Include extlib's original header file here.
(1) | // This part is only read by C++ compiler.
| #include "extlib.h"
v
#else // __MAKECINT__
^
| // Declare interface to the external library.
| // This part is read by cint/makecint only and
(2) | // must be described within cint limitation.
| // #pragma link statements can be added as option.
| class ExtLib { ... };
| #pragma link C++ class ExtLib;
v
#endif // __MAKECINT__
#endif // EXTLIB
////////////////////////////////////////////////////
Then do
$ makecint -mk makefile -dl extlib.dll -H extlibdmy.h -C++ extlib.cxx
$ make -f makefile
or
$ makecint -mk makefile -dl extlib.dll -H extlibdmy.h -l extlib.a
$ make -f makefile
NOTE:
If the library defines many symbols, creation of part (2) may require
significant amount of work. There will be criticism for recommending this
method. But in my past experience, this is the best practice. Many of the
library headers refer to non-standard symbols. Even if we remove cint
limitations, having one such non-standard symbol becomes a bottleneck.
Creating dummy header is an inevitable choice in order to avoid it.
# How to describe dummy header
This section explains how to describe the dummy header.
- class
You only need to describe public interface of the class and only what you
want to access from the interpreter. For example, if original header
extlib.h looks like below.
////////////////////////////////////////////////////
// extlib.h
////////////////////////////////////////////////////
class _DLLEXPORT ExtLib : _PUBLIC _BASEPROPRIETARY {
public:
// public interface
typedef int ExtLibLocalType;
ExtLib() ;
ExtLib(const ExtLib& x);
~ExtLib() ;
void publicMethod1();
int publicMethod2(const ExtLib& x);
const ExtLib& publicMethod3() const;
// public but suppose you do not need to use following
// methods from the interpreter
void publicMethod5();
int publicMethod6(const ExtLib& x);
const ExtLib& publicMethod7() const;
private:
void privateMethod5();
const char* privateMethod6(const char* x);
double privateMethod7() const;
char* x;
};
//////////////////////////////////////////////////////////
In extlibdmy.h , you only describe as follows.
////////////////////////////////////////////////////
// extlibdmy.h
////////////////////////////////////////////////////
#ifndef EXTLIB
#define EXTLIB
#ifndef __MAKECINT__
^
| // Include extlib's original header file here.
(1) | // This part is only read by C++ compiler.
| #include "extlib.h"
v
#else // __MAKECINT__
^
| // Declare interface to the external library.
| // This part is read by cint/makecint only and
(2) | // must be described within cint limitation.
| // #pragma link statements can be added as option.
| class ExtLib {
| public:
| // describe only things that you want to use from the
| // interpreter
| typedef int ExtLibLocalType;
| ExtLib() ;
| ExtLib(const ExtLib& x);
| ~ExtLib() ;
|
| void publicMethod1();
| int publicMethod2(const ExtLib& x);
| const ExtLib& publicMethod3() const;
| };
v
#endif // __MAKECINT__
#endif // EXTLIB
////////////////////////////////////////////////////
- macro defined as scalar or string constant
Macro that represents scalar or string constant can be described as is.
For example.
////////////////////////////////////////////////////
// extlibdmy.h
////////////////////////////////////////////////////
#ifndef EXTLIB
#define EXTLIB
#ifndef __MAKECINT__
^
| // Include extlib's original header file here.
(1) | // This part is only read by C++ compiler.
| #include "extlib.h"
v
#else // __MAKECINT__
^
| // Declare interface to the external library.
| // This part is read by cint/makecint only and
(2) | // must be described within cint limitation.
| // #pragma link statements can be added as option.
| #define PI 3.141592
| #define LIBVERSION 20000214
| #define LIBNAME "My External Lib"
v
#endif // __MAKECINT__
#endif // EXTLIB
////////////////////////////////////////////////////
- macro function
Macro function needs special care. Cint can not pass arguments to
macro function becuase there is no type information. You have to
translate it to ANSI C style header. You also need to provide #pragma
link information that it is a macro not a true function. Otherwise,
Cint tries to get pointer to the function which causes compilation
error.
////////////////////////////////////////////////////
// extlib.h
////////////////////////////////////////////////////
#define MAX(a,b) (a>b?a:b)
#define MIX(a,b) (a>b?a:b)
//////////////////////////////////////////////////////////
In extlibdmy.h , you describe as follows.
////////////////////////////////////////////////////
// extlibdmy.h
////////////////////////////////////////////////////
#ifndef EXTLIB
#define EXTLIB
#ifndef __MAKECINT__
^
| // Include extlib's original header file here.
(1) | // This part is only read by C++ compiler.
| #include "extlib.h"
v
#else // __MAKECINT__
^
| // Declare interface to the external library.
| // This part is read by cint/makecint only and
(2) | // must be described within cint limitation.
| // #pragma link statements can be added as option.
| double MAX(double a,double b);
| double MIN(double a,double b);
| int MAX(int a,int b);
| int MIN(int a,int b);
|
| #pragma link MACRO MAX;
| #pragma link MACRO MIN;
v
#endif // __MAKECINT__
#endif // EXTLIB
////////////////////////////////////////////////////
- pointer to function
It is recommended to use typedef for pointer to function. Cint can
process basic pointer to function syntax, however, there can be bugs.
Typedef will prevent such bug.
- template
Cint/makecint can not precompile template itself. It can only precompile
instantiated template class or function. Cint can not process partial
template specialization. Such description must be avoided in the dummy
header.
////////////////////////////////////////////////////
// extlibdmy.h
////////////////////////////////////////////////////
#ifndef EXTLIB
#define EXTLIB
#ifndef __MAKECINT__
^
| // Include extlib's original header file here.
(1) | // This part is only read by C++ compiler.
| #include "extlib.h"
v
#else // __MAKECINT__
^
| // Declare interface to the external library.
| // This part is read by cint/makecint only and
(2) | // must be described within cint limitation.
| // #pragma link statements can be added as option.
| template<class T> ExtLibArray {
| ...
| };
| // instantiate template class
| #pragma link C++ class ExtLibArray<int>;
| #pragma link C++ class ExtLibArray<TExtLiba>;
|
| template<class T> void ExtLibTmpltFunc(T a) { ... }
| // instantiate template function
| void ExtLibTmpltFunc(int a);
| void ExtLibTmpltFunc(TExtLibA a);
| #pragma link C++ function ExtLibTmpltFunc; // optional
v
#endif // __MAKECINT__
#endif // EXTLIB
////////////////////////////////////////////////////
- STL
STL is a template library. So basically you need to follow above
rule. On top of it, you need to give '-cint -M0x10' option to makecint.
STL allocator overrides operator new/delete. This conflicts with Cint's
operator new/delete. You need to give '-cint -M0x10' option to avoid
this. HP-UX needs '-cint -M0x1c'.
$ makecint -mk makefile -dl extlib.dll -H extlibdmy.h -C++ extlib.cxx
-cint -M0x10
$ make -f makefile
or
$ makecint -mk makefile -dl extlib.dll -H extlibdmy.h -l extlib.a
-cint -M0x10
$ make -f makefile
- exception
Cint can parse exception syntax and ignores it. It should be fine
to leave exception specification in the dummy header. If you find
problem , please eliminate exception specification from the dummy
header.
- global function, global variable, typedef, etc...
Most of the other C++ language constructs can be processed by
cint/makecint. You can copy the original description in original
header file as it is.
- other tips,
For other cint limitations, please refer to doc/limitati.txt
############################################################################
# Workaround 2: Modifying the original header
############################################################################
Basic rule is the same as Workaround 1. You will do it for the original
header file instead of dummy header. This method is recommended only if
you have 100% control of the library.