Managing Code Variants

Steven J Zeil

Last modified: Mar 31, 2019
Contents:

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 AUTOCONF

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:

./configure
make
make install

2.2 Generating The configure Script

A rough outline:

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

2.3 Example

2.3.1 1. Create a configure.ac

AC_INIT(cppSpreadsheet, 1.0, zeil@cs.odu.edu)  
AC_PREREQ([2.68])                            ➀
AM_INIT_AUTOMAKE([1.16 foreign no-define])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CXX                                 ➁
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

2.3.2 Preparing the templates

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

3: Set up Makefile.am

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


Alternatives

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:

input.OCRLauncherClass=edu.odu.cs.extract.input.OCRBatchLauncher
input.OCRProgram=OCR
input.OCRBatch=Batch
input.ocr.in_dir=c:/Luratech/ocr_in
input.ocr.out_dir=c:/Luratech/ocr_out


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);