[
Lists Home |
Date Index |
Thread Index
]
My comments on your API. Some of this has been said before; I tred to
include my justification whenever I remembered an item being repeated.
The API and library should be as small as possible. This will make it
easy for other folks to pull out the "hard" part (the XML writing), and
adapt it for their environments. The more features you add, and the
more flexibility you support, the harder it is to do this, and the more
you move down the slippery slope of code bloat. Make simple things
simple; hard things are handled by Gnome libxml.
Work in UTF-8. If you want to be nice to Windows folks add genx....W()
functions to handle "..."L kinds of strings. If users can't work in
UTF-8, then developers can easily modify your library to support their
encodings, or if worst comes to worst "if you want iconv you know where
to find it."
Follow the standard open-modify-close model. Support only one document
per genxWriter object. That gets rid of genxNew; it would also get rid
of genxClose if it existed. (See, I told you the current model is too
complicated. :) Look at unix open syscall; flags specify calling sequence.
Modifiers -- gxSetUserData, gxSetError, gxSetAllocator are allowed after
the open call but not after any output has been done. This means they
should be able to return an error condition if called at the wrong time.
Should SetAllocator also take pointers to realloc and free functions?
As someone else said, use typed opaque pointers:
typedef struct genxWriter_struct* genxWriter;
genxWriter genxOpen(int flags, ...)
Flags include:
gxoFILE -- next argument is FILE* for output
gxoNOPROLOG -- don't generate XML prolog
gxoSTANDALONE -- output "standalone='yes" in prolog
gxoABORT -- call abort() on any error (for debugging mode)
gxoFIXAMP -- turn & and < into & and <
(perhaps gxoNOFIXMAP, I don't care)
I like the idea behind StartStartTag and StartTag, but how about
StartTag and StartComplexTag ?
Provide a validity checker so users can avoid generating errors
typedef enum {
genxPI, genxANAME, genxENAME, genxCOMMENT, genxTEXT
} genxITEM;
int genxValid(genxITEM id, char* text);
/* Theoretically PI's have a "target", but since you didn't support
* that in genxPI, I'm assuming it's bundled. */
This shows an abiguity in your checkName function. Is "xmlns" a valid
name? What about "xml:lang"? What about "foo:bar" ? Are these
functions standalone, or only valid in the context of current output?
That might be most correct, but will be confusing when the output of
Validity depends on whether or not a particular namespace was declared, e.g.
I'm uneasy about genxStartTag -- requiring developers to keep four
string arrays in sequence seems clunky. It's cleaner and not much more
work to organize related data:
typedef struct {
const char *ns, *prefix, *name, *value
} genxAttribute;
And pass in an array of those, instead.
genxScanUTF8 is a bad name -- looks to much like sscanf() et al. Again,
I'd merge that and the Check functions into the single Valid predicate.
How important is single-character output, really? Making them use a
temporary buffer is not very inconvenient. And if you believe must
single-char output will be in some kind of loop, anyway...
I'm unsure about C strings vs pointer/length. The latter removes the
need for single-char output routine. It also makes it easy to snip
something out of a buffer and output it, without making a local copy.
The downside is if someone adapts this for multibyte encodings, getting
the length right can be hard. On the other hand, I betcha 99% of the
time that length argument will be strlen, which makes some argument
about my previous point, not sure if it's for or against. Your call;
folks (like me) will gripe no matter which way you go.
Anyhow, hope this helps.
/r$
--
Rich Salz, Chief Security Architect
DataPower Technology http://www.datapower.com
XS40 XML Security Gateway http://www.datapower.com/products/xs40.html
XML Security Overview http://www.datapower.com/xmldev/xmlsecurity.html
|