Managing Code Variants

Steven J Zeil

Last modified: Dec 21, 2019

1 Problems

Code Variations

The Sad Story of C/C++ Portability

C Portability Quiz

How would you declare an integer counter capable of holding non-negative values up to one million? Up to one billion?

C++ Portability Quiz

How would you declare an integer counter capable of holding non-negative values up to one million? Up to one billion?

Coping With Variants in the C/C++ World


2.1 Compiling Software the Unix Way

If you’ve ever installed a Unix/Linux package from a source distribution, you’ve probably gotten used to the two-step process:

make install

2.2 Generating The configure Script

A rough outline:

  1. Create a
  2. Set up (template for eventual config.h file)
  3. Set up
  4. Set up files NEWS README AUTHORS ChangeLog
  5. Run autoreconf

2.3 Example

2.3.1 1. Create a

AC_INIT(cppSpreadsheet, 1.0,
AC_PREREQ([2.68])                            ➀
AM_INIT_AUTOMAKE([1.16 foreign no-define])
AC_PROG_CXX                                 ➁

2.3.2 Preparing the templates

2: Set up (template for eventual config.h file)

3: Set up

AM_INIT_AUTOMAKE([1.10 no-define foreign])

bin_PROGRAMS = testssheet

testssheet_SOURCES=testssheet.cpp exprparser.cpp tokenizer.cpp exprfactory.cpp expression.cpp \
     cellname.cpp numericnode.cpp stringnode.cpp cellrefnode.cpp negatenode.cpp \
     absnode.cpp sqrtnode.cpp sumnode.cpp lessnode.cpp lesseqnode.cpp \
     greaternode.cpp greatereqnode.cpp equalnode.cpp notequalnode.cpp plusnode.cpp \
     subtractnode.cpp timesnode.cpp dividesnode.cpp ifnode.cpp \
     numvalue.cpp strvalue.cpp errvalue.cpp spreadsheet.cpp cell.cpp \
     observable.cpp observerptrseq.cpp cellptrseq.cpp cellnameseq.cpp \
     absnode.h          control.h        lessnode.h        ssi.h \
     binarynode.h       dividesnode.h    minusnode.h       ssview.h \
     cell.h             elementseq.h     negatenode.h      streamtok.h \
     celllistenerseq.h  equalnode.h      notequalnode.h    stringnode.h \
     cellname0.h        errvalue.h       numericnode.h     strvalue.h \
     cellname.h         expression.h     numvalue.h        subtractnode.h \
     cellnameseq.h      exprfactory.h    observable.h      sumnode.h \
     cellptrseq.h       exprparser.h     observer.h        timesnode.h \
     cellrange.h        greatereqnode.h  observerptrseq.h  unaryexpr.h \
     cellrefnode.h      greaternode.h    plusnode.h        unarynode.h \
     clipboard.h        ifnode.h         spreadsheet.h     unittest.h \
     constantnode.h     lesseqnode.h     sqrtnode.h        value.h

2.3.3 Generating the configure Script

4: touch NEWS README AUTHORS ChangeLog

or create real versions of these.

5: run autoreconf --force --install


3 Dynamic Loading

autoconf is C/C++-centric

The configure approach relies heavily on conditional compilation features.

Java: Abstraction

Java programs are more likely varied be altering entire classes at a time.

For example:

public abstract class OCRLauncher extends Thread {
     * Launch an OCR process to convert the input
     * PDF into some kind of File  of OCR output.
     * @param inputPDFfile The PDF file to be converted to IDM (XML)
     * @param outputFile The raw OCR output
     * @return
    public abstract boolean convertPDFtoOCR
       (File inputPDFfile, File outputFile)
            throws Exception;

     * Convert a file of OCR output into IDM
     * @param inputOCRfile
     * @return XML (IDM) document
    public abstract Document convertOCRtoIDM
       (File inputOCRfile) throws Exception;

This class has distinct implementations for different OCR programs that might be installed on the running system.

Configuration via Property Files

A property file, loaded at run time, specifies which class is actually desired:


Reflection: Dynamic Loading

And the desired class is loaded dynamically:

String OCRLauncherName
   = p.getProperty(Properties.Names.OCR_LAUNCH_CLASS);
Class<?> ocrLauncherClass
   = Class.forName(OCRLauncherName);
ocr = (OCRLauncher)ocrLauncherClass.newInstance();
idmDoc = ocr.convertOCRtoIDM(inputOCR);