;;; c-delegate.ils --- Solution to "Link C++ - SKILL"
;; Copyright (C) 2013 Damien Diederen
;; @author Damien Diederen
;; Permission is hereby granted, free of charge, to any person
;; obtaining a copy of this software and associated documentation
;; files (the "Software"), to deal in the Software without
;; restriction, including without limitation the rights to use, copy,
;; modify, merge, publish, distribute, sublicense, and/or sell copies
;; of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.
;;; Commentary:
;; Cf. http://www.cadence.com/Community/forums/p/24699/1318711.aspx#1318711.
;;
;; The goal of this small package is to demonstrate communication
;; between SKILL and an external executable program (the "delegate").
;;
;; Inputs are passed as command-line arguments, results are parsed out
;; from the delegate's standard output. The delegate is written in C,
;; and can be found in the c-delegate.c source file. It must be
;; compiled prior to use; e.g. by running:
;;
;; $ make CC= CFLAGS=
;;
;; from the package's directory.
;;
;; The main entry point is MyInvokeCDelegate; here are basic usage
;; instructions:
;;
;; $ $CDSHOME/tools/dfII/bin/skill
;; (load "pkg/c-delegate/c-delegate.ils")
;; |- t
;; (MyInvokeCDelegate 1.1 1.2 1.3 1.4 5)
;; |- ((1.1 1.2)
;; | (2.4 2.6)
;; | (3.7 4.0)
;; | (5.0 5.4)
;; | (6.3 6.8))
;;
;; This has been tested on a Linux box, using Veda 0.2.0
;; (http://crosstwine.com/veda/):
;;
;; $ make
;; cc -o c-delegate c-delegate.c
;; $ veda test-package
;; Testing c-delegate
;; === RUN Test_InvokeCDelegate
;; --- PASS: Test_InvokeCDelegate
;;; Code:
;; Predeclaration of our globally visible entry point.
;; @ignore
(define MyInvokeCDelegate nil)
;; Scope/hide utility functions and related data.
(let ()
;; The base name of the executable to invoke.
(define cDelegate
"c-delegate")
;; The full path to this SKILL++ file, as seen by (load "..."); used
;; by FindSibling.
(define thisFilename
(when (isCallable 'get_filename)
(get_filename piport)))
;; FindSibling returns the complete path to filename, which is a
;; sibling to this file, or nil if no such readable file could be
;; found.
(defun FindSibling (filename)
(when thisFilename
(let ((suffix (rindex thisFilename "/")))
(when suffix
(letseq ((diff ((strlen thisFilename) - (strlen suffix)))
(prefix (substring thisFilename 1 (diff + 1)))
(pathname (strcat prefix filename)))
(when (isReadable pathname)
pathname))))))
;; PrepareCommand builds the command line to be used to invoke the
;; delegate. It takes the same arguments as InvokeCDelegate, and
;; massages them into string form.
(defun PrepareCommand (x0 y0 dx dy steps)
(let ((path (or (FindSibling cDelegate)
(strcat "./" cDelegate))))
(sprintf nil "%L %L %L %L %L %L"
path x0 y0 dx dy steps)))
;; ParseFloatPair expects two (and exactly two)
;; whitespace-separated, atof-parseable values in string, and
;; returns them as a SKILL list of floating-point values.
;;
;; It returns nil on failure.
(defun ParseFloatPair (string)
;; This could be anything; from a simple sscanf to a very complex
;; parser depending on the chosen data format.
(let ((pair (mapcar atof (parseString string))))
(when (and (equal (length pair) 2)
(not (memq nil pair)))
pair)))
;; InvokeCDelegate passes its arguments to an external program which
;; implements an extremely complicated algorithm involving legacy
;; code, and which cannot possibly be rewritten in SKILL(++).
;;
;; See that program's detailed documentation for more information.
;;
;; This function takes care of the I/O, and parses the results back
;; into a list of 2-lists of floating-point values.
(defun InvokeCDelegate (x0 y0 dx dy steps)
(let ((cmd (PrepareCommand x0 y0 dx dy steps)))
(let ((ipc (ipcBeginProcess cmd))
;; Accumulator.
(pairs nil)
;; Temporary.
(data nil))
;; Nothing to be provided on standard input.
(ipcCloseProcess ipc)
(while (setq data (ipcReadProcess ipc))
(let ((pair (ParseFloatPair data)))
;; Error strategy?
(when pair
(push pair pairs))))
(ipcWait ipc)
(let ((status (ipcGetExitStatus ipc)))
(unless (zerop status)
;; Nonzero means something went wrong.
(error "Command %L exited with %L." cmd status)))
(reverse pairs))))
;; Exports the local function.
(setq MyInvokeCDelegate InvokeCDelegate))
;;; c-delegate.ils ends here