📄 gsl-design.texi
字号:
unmaintainable code. However, it's easy to go down that road if youdon't think about it ahead of time.It is better to choose simplicity over completeness. In designing newparts of the library keep modules independent where possible. Ifinterdependencies between modules are introduced be sure about where youare going to draw the line.@node Code Reuse, Standards and conventions, Design of Numerical Libraries, Design@section Code ReuseIt is useful if people can grab a single source file and include it intheir own programs without needing the whole library. Try to allowstandalone files like this whenever it is reasonable. Obviously theuser might need to define a few macros, such as GSL_ERROR, to compilethe file but that is ok. Examples where this can be done: grabbing asingle random number generator.@node Standards and conventions, Background and Preparation, Code Reuse, Design@section Standards and conventionsThe people who kick off this project should set the coding standards andconventions. In order of precedence the standards that we follow are,@itemize @bullet@item We follow the GNU Coding Standards.@item We follow the conventions of the ANSI Standard C Library.@item We follow the conventions of the GNU C Library.@item We follow the conventions of the glib GTK support Library.@end itemizeThe references for these standards are the @cite{GNU Coding Standards}document, Harbison and Steele @cite{C: A Reference Manual}, the@cite{GNU C Library Manual} (version 2), and the Glib source code.If the project has a philosophy it is to "Think in C". Since we areworking in C we should only do what is natural in C, rather than tryingto simulate features of other languages. If there is something which isunnatural in C and has to be simulated then we avoid using it. If thismeans leaving something out of the library, or only offering a limitedversion then so be it. It is not worthwhile making the libraryover-complicated. There are numerical libraries in other languages, andif people need the features of those languages it would be sensible forthem to use the corresponding libraries, rather than coercing a Clibrary into doing that job. It should be borne in mind at all time that C is a macro-assembler. Ifyou are in doubt about something being too complicated ask yourself thequestion "Would I try to write this in macro-assembler?" If the answeris obviously "No" then do not try to include it in GSL. [BJG]It will be useful to read the following paper,@itemize @asis@item Kiem-Phong Vo, ``The Discipline and Method Architecture for ReusableLibraries'', Software - Practice & Experience, v.30, pp.107-128, 2000.@end itemize@noindentIt is available from@url{http://www.research.att.com/sw/tools/sfio/dm-spe.ps} or the earliertechnical report Kiem-Phong Vo, "An Architecture for Reusable Libraries"@url{http://citeseer.nj.nec.com/48973.html}.There are associated papers on Vmalloc, SFIO, and CDT which are alsorelevant to the design of portable C libraries.@itemize @asis@itemKiem-Phong Vo, ``Vmalloc: A General and Efficient MemoryAllocator''. Software Practice & Experience, 26:1--18, 1996.@url{http://www.research.att.com/sw/tools/vmalloc/vmalloc.ps}@itemKiem-Phong Vo. ``Cdt: A Container Data Type Library''. Soft. Prac. &Exp., 27:1177--1197, 1997@url{http://www.research.att.com/sw/tools/cdt/cdt.ps}@itemDavid G. Korn and Kiem-Phong Vo, ``Sfio: Safe/Fast String/File IO'',Proceedings of the Summer '91 Usenix Conference, pp. 235-256, 1991.@url{http://citeseer.nj.nec.com/korn91sfio.html}@end itemize@node Background and Preparation, Documentation, Standards and conventions, Design@section Background and PreparationBefore implementing something be sure to research the subjectthoroughly. This will save a lot of time in the long-run. The two mostimportant steps are,@enumerate@itemto determine whether there is already a free library (GPL orGPL-compatible) which does the job. If so, there is no need toreimplement it. Carry out a search on Netlib, GAMs, na-net,sci.math.num-analysis and the web in general. This should also provide alist of existing proprietary libraries which are relevant.@item make a comparative survey of existing implementations in thecommercial/free libraries. Examine the typical APIs, methods ofcommunication between program and subroutine, and classify them so thatyou are familiar with the key concepts or features that animplementation may or may not have, depending on the relevant tradeoffschosen. Be sure to review the documentation of existing libraries foruseful references.@itemread up on the subject and determine the state-of-the-art. Find thelatest review papers. A search of the following journals should beundertaken.@itemize @asis@item ACM Transactions on Mathematical Software@item Numerische Mathematik@item Journal of Computation and Applied Mathematics@item Computer Physics Communications@item SIAM Journal of Numerical Analysis@item SIAM Journal of Scientific Computing@end itemize@end enumerate@node Documentation, Namespace, Background and Preparation, Design@section DocumentationDocumentation: the project leaders should give examples of how thingsare to be documented. High quality documentation is absolutelymandatory, so documentation should introduce the topic, and give carefulreference for the provided functions. The priority is to providereference documentation for each function. It is not necessary toprovide tutorial documentation.Use free software, such as GNU Plotutils, to produce the graphs in themanual.Some of the graphs have been made with gnuplot which is not truly free(or GNU) software, and some have been made with proprietaryprograms. These should be replaced with output from GNU plotutils.@node Namespace, Header files, Documentation, Design@section NamespaceUse @code{gsl_} as a prefix for all exported functions and variables.Use @code{GSL_} as a prefix for all exported macros.All exported header files should have a filename with the prefix @code{gsl_}.All installed libraries should have a name like libgslhistogram.aAny installed executables (utility programs etc) should have the prefix@code{gsl-} (with a hyphen, not an underscore).@node Header files, Target system, Namespace, Design@section Header filesInstalled header files should be idempotent, i.e. surround them by thepreprocessor conditionals like the following,@example#ifndef __GSL_HISTOGRAM_H__#define __GSL_HISTOGRAM_H__...#endif /* __GSL_HISTOGRAM_H__ */@end example@node Target system, Function Names, Header files, Design@section Target systemThe target system is ANSI C, with a full Standard C Library, and IEEEarithmetic.@node Function Names, Object-orientation, Target system, Design@section Function NamesEach module has a name, which prefixes any function names in thatmodule, e.g. the module gsl_fft has function names likegsl_fft_init. The modules correspond to subdirectories of the librarysource tree.@node Object-orientation, Comments, Function Names, Design@section Object-orientationThe algorithms should be object oriented, but only to the extent that iseasy in portable ANSI C. The use of casting or other tricks to simulateinheritance is not desirable, and the user should not have to be awareof anything like that. This means many types of patterns are ruledout. However, this is not considered a problem -- they are toocomplicated for the library. Note: it is possible to define an abstract base class easily in C, usingfunction pointers. See the rng directory for an example. When reimplementing public domain fortran code, please try to introducethe appropriate object concepts as structs, rather than translating thecode literally in terms of arrays. The structs can be useful justwithin the file, you don't need to export them to the user.For example, if a fortran program repeatedly uses a subroutine like,@exampleSUBROUTINE RESIZE (X, K, ND, K1)@end example@noindentwhere X(K,D) represents a grid to be resized to X(K1,D) you can makethis more readable by introducing a struct,@examplestruct grid @{ int nd; /* number of dimensions */ int k; /* number of bins */ double * x; /* partition of axes, array of size x[k][nd] */@}voidresize_grid (struct grid * g, int k_new)@{...@}@end example@node Comments, Minimal structs, Object-orientation, Design@section CommentsFollow the GNU Coding Standards. A relevant quote is,``Please write complete sentences and capitalize the first word. If alower-case identifier comes at the beginning of a sentence, don'tcapitalize it! Changing the spelling makes it a different identifier.If you don't like starting a sentence with a lower case letter, writethe sentence differently (e.g., "The identifier lower-case is ...").''@node Minimal structs, Algorithm decomposition, Comments, Design@section Minimal structsWe prefer to make structs which are minimal. For example, if a certaintype of problem can be solved by several classes of algorithm (e.g. withand without derivative information) it is better to make separate typesof struct to handle those cases. i.e. run time type identification isnot desirable.@node Algorithm decomposition, Memory allocation and ownership, Minimal structs, Design@section Algorithm decompositionIterative algorithms should be decomposed into an INITIALIZE, ITERATE,TEST form, so that the user can control the progress of the iterationand print out intermediate results. This is better than usingcall-backs or using flags to control whether the function prints outintermediate results. In fact, call-backs should not be used -- if theyseem necessary then it's a sign that the algorithm should be broken downfurther into individual components so that the user has complete controlover them. For example, when solving a differential equation the user needs to beable to advance the solution by individual steps, while tracking arealtime process. This is only possible if the algorithm is broken downinto step-level components. Higher level decompositions would not givesufficient flexibility.@node Memory allocation and ownership, Memory layout, Algorithm decomposition, Design@section Memory allocation and ownershipFunctions which allocate memory on the heap should end in _alloc(e.g. gsl_foo_alloc) and be deallocated by a corresponding _free function(gsl_foo_free).Be sure to free any memory allocated by your function if you have toreturn an error in a partially initialized object.Don't allocate memory 'temporarily' inside a function and then free itbefore the function returns. This prevents the user from controllingmemory allocation. All memory should be allocated and freed throughseparate functions and passed around as a "workspace" argument. Thisallows memory allocation to be factored out of tight loops.@node Memory layout, Linear Algebra Levels, Memory allocation and ownership, Design@section Memory layoutWe use flat blocks of memory to store matrices and vectors, not C-stylepointer-to-pointer arrays. The matrices are stored in row-major order-- i.e. the column index (second index) moves continuously through memory. @node Linear Algebra Levels, Exceptions and Error handling, Memory layout, Design@section Linear Algebra LevelsFunctions using linear algebra are divided into two levels:For purely "1d" functions we use the C-style arguments (double *,stride, size) so that it is simpler to use the functions in a normal Cprogram, without needing to invoke all the gsl_vector machinery.The philosophy here is to minimize the learning curve. If someone onlyneeds to use one function, like an fft, they can do so without havingto learn about gsl_vector. This leads to the question of why we don't do the same for matrices.In that case the argument list gets too long and confusing, with(size1, size2, tda) for each matrix and potential ambiguities over rowvs column ordering. In this case, it makes sense to use gsl_vector andgsl_matrix, which take care of this for the user.So really the library has two levels -- a lower level based on C typesfor 1d operations, and a higher level based on gsl_matrix andgsl_vector for general linear algebra.Of course, it would be possible to define a vector version of thelower level functions too. So far we have not done that because it wasnot essential -- it could be done but it is easy enough to get byusing the C arguments, by typing v->data, v->stride, v->size instead.A gsl_vector version of low-level functions would mainly be aconvenience.@node Exceptions and Error handling, Persistence, Linear Algebra Levels, Design@section Exceptions and Error handlingThe basic error handling procedure is the return code (see gsl_errno.hfor a list of allowed values). Use the GSL_ERROR macro to mark anerror. The current definition of this macro is not ideal but it can bechanged at compile time. You should always use the GSL_ERROR macro to indicate an error, ratherthan just returning an error code. The macro allows the user to traperrors using the debugger (by setting a breakpoint on the functiongsl_error). The only circumstances where GSL_ERROR should not be used are where thereturn value is "indicative" rather than an error -- for example, theiterative routines use the return code to indicate the success orfailure of an iteration. By the nature of an iterative algorithm"failure" (a return code of GSL_CONTINUE) is a normal occurrence andthere is no need to use GSL_ERROR there.Be sure to free any memory allocated by your function if you return anerror (in particular for errors in partially initialized objects).@node Persistence, Using Return Values, Exceptions and Error handling, Design@section PersistenceIf you make an object foo which uses blocks of memory (e.g. vector,matrix, histogram) you can provide functions for reading and writingthose blocks,@exampleint gsl_foo_fread (FILE * stream, gsl_foo * v);int gsl_foo_fwrite (FILE * stream, const gsl_foo * v);int gsl_foo_fscanf (FILE * stream, gsl_foo * v);int gsl_foo_fprintf (FILE * stream, const gsl_foo * v, const char *format);@end example@noindentOnly dump out the blocks of memory, not any associated parameters suchas lengths. The idea is for the user to build higher level input/outputfacilities using the functions the library provides. Use the functions@exampleint gsl_block_fread (FILE * stream, gsl_block * b);int gsl_block_fwrite (FILE * stream, const gsl_block * b);int gsl_block_fscanf (FILE * stream, gsl_block * b);int gsl_block_fprintf (FILE * stream, const gsl_block * b, const char *format);@end example@noindentor@exampleint gsl_block_raw_fread (FILE * stream, double * b, size_t n, size_t stride);int gsl_block_raw_fwrite (FILE * stream, const double * b, size_t n, size_t stride);int gsl_block_raw_fscanf (FILE * stream, double * b, size_t n, size_t stride);int gsl_block_raw_fprintf (FILE * stream, const double * b, size_t n, size_t stride, const char *format);@end example@noindentto do the actual reading and writing.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -