Next: , Previous: , Up: Tips   [Contents][Index]


A.3 Tips for writing compatible code

Writing compatible code is very easy whenever a problem is decoupled enough. That is when your problem is of abstract nature and does not explicitly depend on a certain OS, emacs flavour, external tools, or file system layout.

In that case you would simply divide your code in two parts, the abstract layer which contains the solution of your problem in an abstract (read independent) way, and the implementation layer which contains OS specific, flavour specific, tool specific and/or file system layout specific implementations and which thus provides one or more interfaces for your abstract layer.

Fine, so why are there so many problems between, say, code for FSF Emacs and (S)XEmacs? Well, programmers are not always professional software designers. Also, compatibility often plays a minor role, especially in young projects or projects with little man power. But the most understandable reason might be that one or more pieces of code simply cannot be decoupled or at least not in an efficient way.

Anyway, let us look at very simple compatibility issues. The most trivial case of course is a problem independent from all of the aforementioned requisites, like sorting a list of numbers ascendingly. This can be done using only an iteration function (like while), assignment operators (setq), a predicate (<) and the list primitives cons, car, cdr. All of these routines are available in any emacs on any OS. Hence compatibility is not an issue.

Nonetheless, even such a simple thing can be transformed into a broadly incompatible mess. Just assume that programmers choose the way with least obstacles – some people prefer the term lazy though – they will definitely use a primitive like sort, stable-sort or sort*. Or assume that sorting numbers is just a small subproblem of a larger one and the context stipulates that numbers must be arranged in vectors instead of lists. In such cases consider the following small checklist.

A.3.1 How to write a compatibility layer

There is no generic approach to do that. However, there is a general idea which has been mentioned already in the introduction. Decouple your program so much that you do not call any functions, macros or variables which are suspicious to be incompatible.

Let us work out this idea in a practical scenario. Imagine you have to deal with aspect ratios as they occur in image data or video material, e.g. 16:9, 16:10 or the like. Now initial research reveals that both XEmacs 21.5.x and SXEmacs may provide built-in support for rational quotients, whereas XEmacs 21.4.x and GNU Emacs do not.

Now since we know that we only want to multiply fractions with integers, we merely provide this single operation, along with a constructor, a predicate and two accessors. Note, we will put the detection code along with the two implementations in the following example. In practice each implementation usually occupies a file of its own and the detection resides at a central location, for example in the library’s main file.

(when
  ;; check if SXEmacs is build with ENT support (bigq submodule)
  (and (featurep 'ent) (featurep 'bigq))

  ;; the constructor (takes two arguments, two rational integers)
  (defalias 'expkg-make-quotient #'//)
  ;; the predicate (takes one argument, a bigq)
  (defalias 'expkg-quotient-p #'bigqp)

  ;; an accessor for the numerator of a quotient (takes a bigq)
  (defalias 'expkg-quotient-num #'numerator)
  ;; an accessor for the denominator of a quotient (takes a bigq)
  (defalias 'expkg-quotient-den #'denominator)

  ;; a * function for quotients by integers
  (defalias 'expkg-mult-quo-int #'*))

(when
  ;; or is it XEmacs with ratio support? SXE says `t', too, so check
  ;; for non-SXE
  (and (not (featurep 'sxemacs)) (featurep 'ratio))

  (defalias 'expkg-make-quotient #'div)
  (defalias 'expkg-quotient-p #'ratiop)

  (defalias 'expkg-quotient-num #'numerator)
  (defalias 'expkg-quotient-den #'denominator)

  (defalias 'expkg-mult-quo-int #'*))

;; just check if expkg-make-quotient is bound already
(unless (fboundp #'expkg-make-quotient)

  ;; constructor
  (defun expkg-make-quotient (numerator denominator)
    "Return the quotient numerator/denominator"
    ;; we choose to store in a 3 component vector
    (vector 'quotient numerator denominator))

  ;; predicate
  (defun expkg-quotient-p (object)
    "Return `t' if OBJECT is a quotient."
    ;; we check for the vector property, if length equals 3 and
    ;; finally if our indicator is present
    (and (vectorp object)
         (= (length object) 3)
         (eq (aref object 0) 'quotient)))

  ;; num accessor
  (defun expkg-quotient-num (quotient)
    "Return the numerator of QUOTIENT."
    ;; check if QUOTIENT is indeed what we expect
    (if (expkg-quotient-p quotient)
        (aref quotient 1)
      (error "Wrong type argument, quotientp `%s'" quotient)))

  ;; den accessor
  (defun expkg-quotient-den (quotient)
    "Return the denominator of QUOTIENT."
    ;; check if QUOTIENT is indeed what we expect
    (if (expkg-quotient-p quotient)
        (aref quotient 2)
      (error "Wrong type argument, quotientp `%s'" quotient)))

  ;; multiplication
  (defun expkg-mult-quo-int (quo int)
    "Return the product of QUO with INT."
    (if (and (expkg-quotient-p quo)
             (integerp int))
        (expkg-make-quotient
         ;; new-num <- num * int
         (* (expkg-quotient-num quo) int)
         ;; new-den <- den
         (expkg-quotient-den quo))
      ;; otherwise barf
      (error "Wrong type argument, quotientp `%s', integerp `%s'"
             quo int))))

Now that’s it! Now we can use the functions defined here to interface the quotient functionality in an abstract way. Moreover, this example demonstrates how to decouple a concept (quotients in this case) from the actual data type used to represent the concept, in order of appearance quotients can be represented as bigqs, as ratios or as vectors.

However, as you may have noticed not all internals can be abstracted in a sane way. In our case, the abstract layer will make up quotients of integers. We simply assumed – or knew – that these exist in all emacs flavours and behave identically.


Next: , Previous: , Up: Tips   [Contents][Index]