Re: a slightly updated rpm spec file for Ger Hobbelt crm114 branch
Ger Hobbelt <[email protected]>
| Newsgroups | gmane.mail.spam.crm114 |
|---|---|
| Message-ID | <[email protected]> |
On Mon, Feb 2, 2009 at 9:29 PM, Bill Yerazunis <[email protected]> wrote: > So, the current plan is ./src/lib and ./src/engine where lib contains > the stuff that's LGPL (that is, the classifiers and classifier support > code) and engine is the GPL code and contains the JIT and > language-specific code. The engine (GPL) code calls the lib (LGPL) > code to do classifications. Sounds fine with me. Copy&paste configure.ac and the makefiles and make crm114 configure 'see' libcrm114 and it's a go. Hm, have to think this over again. The usual way for libs and apps is to cd libcrm114 ./configure make make install <-- so it ends up in a known place, say, /usr/local/lib then the app cd crm114 ./configure <-- which finds the libcrm114 static or dynamic lib make make install Since there's two licenses and, in my PoV, two purposes/markets as well, it means libcrm114 is split off crm114 and crm114 is then a user of libcrm114, just like it is a user of libTRE (the regex lib). OTOH, one could keep them together (no migration effort for me) but then that would make it much harder for those who just like to use libcrm114 /without/ crm114 -- like me. Anyway, separate dirs is very good for this. > What happens when you hit a CRM_ASSERT? With no I/O, your > embedded device hangs. Depends. And hanging isn't necessary bad -- if you do it in a controlled way. Fatal disaster is fatal, after all. Mind you, CRM_ASSERTs are not a user check, they are developer checks. That's also why they are compile-time removable, and, yeah, I often leave them active in production environments as well, but that's more to do with my experience regarding corporate test quality then anything else. I always joke rather cynically that "it compiles, hence it works". Unfortunately, recent years in the corporate environment have shown me IT managers increasingly take this adagium literally (as if they had invented it on their own already, but lack the required odd sense of humor) and are so eager to 'push' the s/w you get development vs. test ratios of over 5:1 (and don't start me on design time vs. dev time. It's all done Britney Spears style: "can't sing? no worries, mate. We'll fix it in the mix." Video people (hi!) will know the equivalent for film: f*cked up the shot? no worries, we'll fix it in post! (meaning some poor sod will have to tweak the colors for weeks till it looks somewhere close to okay or get down to tedious rotoscoping. Happy happy, joy joy.) Design (e.g. scenario analysis: what's the software got to do; what's it got to do in case of X or incase of anomaly/error Y?) is 'fixed' by brute-force coding ('the mix'). So IT has this equivalent: "concept doesn't work? Heck, no sweat! We'll fiddle the code till we pass the test." (Too hard? Delete the test. Get the related delivery item scratched. Yes, I've seen it happen. More than once. "Over here, we don't test for /errors/! Our software works! We're not interested in /errors/!" [says CTO] ) But let's forget that experience for a bit (which indeed hints at 'best practices' being keeping the CRM_ASSERTs active for ever and always): then CRM_ASSERT is meant as a checkpoint to validate basic designer/developer assumptions. As such, assertions should be active (and may/will be hit) during component ~ , system ~ and integration tests. No, CRM_ASSERTs are not meant to check assumptions (pre/postconditions) which [may] vary due to end user activity (you don't assert on a crm114 script programming error coursing through the parser or interpreter, as that's a user-based error), but assertions instead are meant to catch your own obvious ('C' programming) glitches. (See the Microsoft paper; see other software development books that concern themselves with writing software in general, not just language X (that's be Java today)) Put in another way: an assertion should never trigger, no matter what you feed the software under test. Not in 'regular', nor in 'irregular' / 'erroneous' conditions. For example, yesterday I was happy to have assertions in there: I did something stupid in the Markovian code, fubar-ed it such that in effect the run-time tried to unmap an *already* unmapped mem area. Code flow like this: ptr = mmap(css) ... unmap(ptr) ... unmap(ptr) This is quite stupid, but it happened. I sprinkle Assertions throughout the code to help me catch this kind of stupidities: that's another reason why I like extended test sets WHICH INCLUDE ERRONEOUS TEST SCENARIOS: during 'make check' my code went nuclear, and instead of a hard to track down weird error (or a coredump) I got an assert statement telling me which condition failed on me. From there, it was a few brain minutes between the 'huh?!?!' and the 'oh sh*t! no!' moment, when I found what I had done and could fix it. By the way: currently GerH is configured to, by default, keep the CRM_ASSERT code active for ever and always, *and* throw a crm trap/fault instead of printf+abort/exit. But that's just a feature of my macros & configure script. To protect the natives, you'll have to use a special ./configure option when you want those cycle-nibbling CRM_ASSERTs outa there! I know you are 'against' assert, you've vented that often enough, but CRM_ASSERT is more than default assert() (= print line + abort()). Also, have you considered what the [libcrm114] code flow is to be when you hit on of that set: nonfatalerror fatalerror untrappableerror ? Because they too exhibit the same problem from a code flow perspective: the question how to recover from / continue after such failures. Another place where a callback is the way to go, so the code calling libcrm114 gets to decide what those buggers are to do _exactly_. The outer layer knows better how to handle faults anyway, oftentimes. Pop stack to original question: what to do in an embedded environment: that's precisely the type of development where actually _testing_ your software is important. During testing, I would 'activate' the CRM_ASSERTs. As they call a custom function, such a function can log the issue in any way feasible for the given embedded device under test (log to flash, sends syslog message over IP stack, etc.) and once that action completes -- we already know the developer has made a grave mistake, so any 'trustworthiness' of the software as a whole is already gone -- we can decide to either continue as if nothing happened and wait for the coredump/lockup to happen, OR halt the test device in a controlled manner. For nonfatal and fatal my initial pick would be 'continue & pray', for untrappable, it's definitely 'wait for the watchdog to kick our booty'. CRM_ASSERTs would probably, by my choice, land in the fatalerror 'continue and pray' camp, until I hit 'weird issues', where I will promptly recompile after setting them to act like untrappable, so to make sure the error log gets through and we've got controlled reboot going on. Most embedded devices have a h/w watchdog these days, so that would mean a few (mili)seconds of twiddling thumbs (after we made a best effort to get our error report stored or sent somewhere) and a reboot follows. Remember that, before you reel at this, an assert in any form is there to catch grave programmer mistakes, which would otherwise corrupt the software. null pointers in places you'd never expect them; double free()s (detectable once you adjust the free() interface using a wrapper, which also NULLs the pointer which was just free()d, for example), etc. As counter example, assert-ing on a FILE* != NULL following an fopen() call is /worse/ than Bad Form(tm). One-way trip to the firing squad, I'd say. (And if you go and look deep enough, I /might/ be candidate for that firing squad myself ;-) ) When it comes to NULL pointer checking and such, the edge [between ASSERT and untrappableerror()] becomes a little hazy: for speed's sake, one may like to dispense with checking /all/ input args in /every/ call or at any stage in between code sections -- as long as you can argue that those NULL pointers would never reach down there in such speed-demanding routines. The assert is then added to prove that argument, at least empirically. As such, it must be combined with a good test set (that's why every bug report ever should get its own test script in the permanent test set as well, BTW), i.e. a test set which has a good 'coverage' of the software whole, _plus_ a thorough code review. Since you seem to have had a look at the latest published GerH code (finally! good!) you will have found that the error checking, which I strongly advocate everywhere, is enhanced in two ways, when compared to vanilla: First, indeed, there's the CRM_ASSERTs all over the place. I haven't run any statistics on this, but I'm willing to bet that the areas where I have been busy extended crm114 will show a larger number of asserts per LOC than other parts. Take, for example, the built-in debugger: compare your quickref and mine and notice the documented differences: I can, a la gdb, 'track/view' variables in a crm114 script, even record a sort of debugger macros (view current script bit context + variable values + step to next opcode in script), modify the code flow by jumping to other locations, e.g. when I want an 'alter' in the script to behave 'the other way', etc. Lots of CRM_ASSERTs in there: all of them there to ensure my code doesn't screw up the fragile script interpreter state vector ('program counter', etc.). (Yes, the debugger language, which was already quite 'on acid' (which Eric S. Johansson, who I understand coined the phrase that crm114 is awk on acid (or was it teco on acid) will surely appreciate) is now, following my own additions, definitely something to be 'experienced' through a glass pipe. Heck, ever seen a debugger command as gorgeous as nie;v>5 ?) See old mailing list post form last year for this and more. Second, and as important, I've added error return value and other run-time checks related to script and other user-inducible faults, all of which trigger one of these forms nonfatalerror fatalerror untrappableerror ... as they should. I've been rather loose on picking nonfatal or fatal, yes, but at least it's a huge improvement already. And those checks should /stay/ in an (optimized) 'release executable', as these all check for failures which can be caused by some external element or other (crm script, file system, OS itself, heap management, etc.etc.) The one thing where GerH and vanilla can 'shake hands' regarding error handling is our common crumminess in actually *following up* on those nonfatalerror and fatalerror-s (untrappableerror just aborts the runtime, so nothing to recover there; best thing to do in an embedded setup (where you don't have the benefit of UNIX-like process cleanup) is a hardware reset): nonfatal and fatal both return signal codes (not error codes) and even when it's a nonfatal, often times the best follow up would be to clean up the current call and return to the script interpreter. In actual reality, most of the time, the code just barges on. Ouch. Definitely an area waiting for someone to sweep a broom, but I too have a limited amount of energy reserved for fixing error checking paths for existing codebases: I needed some functionality up and running in a trustworthy, stable manner. That's why I don't like 'intern done' software (and 'interns' can range from age 12 to 65 it seems): when you don't start out writing your code with a focus on error handling and propagation, next to implementing the process itself, you'll quite often find someone (yourself) having to do it all over again. (CLUMP comes to mind: still it coredumps and it irks me no end the lame excuse is 'it's experimental'. My foot. Experimental, that's NN reporting it can't converge, but coredumping software is not experimental. coredumping software is crap. And, yes, CLUMP coredumps on /all/ platforms I can test it on. Not somewhere in the middle, but quite near the start. That's vanilla code, so no 'Ger' influence there. Twas probably a rush job for a conference or a paper to get out posthaste, but you've been darn lucky the thing did something on your h/w.) And, yes, I know others have fed terabytes of mail through crm114 and never complained about any of this. Well, I got the Mano Negra because all ever happened to me was errors, errors and then some more spurious errors. So I reverted to a full fledged code review back in '07 and everything which was found to can-go-wrong, was tagged and fixed as best as possible, with a CRM_ASSERT or, almost as often, a run-time condition plus fatal or nonfatal in the vicinity. That got me out of the errors arena and into the rather more entertaining 'why the heck is the classifier underperforming?' arena. Which I still haven't left, alas. <snif> I shouldn't get me an MBA, I should get myself a top notch statistical analysis brain add-on. But that's another [William Gibson] story.) > This is why I'm truly concerned by error testing that is either turned > on during testing only ("you now know how to drive, so you can stop > wearing the seat belt") and off during production, or asserts that do > not _always_ allow the user's code to do cleanups, such as gracefully > terminating connections, cleanly aborting a mySQL query, using an > application-defined error logging and fixup facility, etc. assert: It's rather using a test dummy to check what a crash does to mr. crash test dummy. When done, taking the test dummy out of the trial volvo, then mass-produce them ('release build') and putting actual humans in them (I drive Volvo - good cars). And, yes, those humans sometimes die in accidents. That's why there's legislation about how much testing you /have/ to do /at least/. We have it for cars. We don't have it for software. So everybody from floor 0 upwards ditches their ethics out the window, hoist their laziness and pushes spot checked software out as product. End result: a planet full of crash test dummies. And, hey, this is bliss! These dummies are even free of charge! P.T. Barnum /was/ right. (Last year's software-similar example: china company exports new cheap SUV to Europe. German magazine and a few others pay for the crash test. Conclusion: drive faster than a horse can run and you, your wife and all your kids go SPLAT, one way ticket to St. Peter guaranteed. Exit SUV. Exit China company. Come back when you've done a better job. Same types you find in IT: European importer and others linked to China company are affronted and call it discrimination and market protection and other names that should rub PC people the right way. Everybody is amazed at their attitude. But when we use or make software, all of us act like that China corp. And nobody is amazed. Au contraire.) > This isn't to say that there aren't bugs in mainline; there assuredly are. > However, there's no _intentional_ failure to allow the maximum > level of recovery possible. There's recovery from error and there failure on error. The former, I relate to VMS. Awesome OS, superb software. If I am going to be reincarnated, get my soul to a VMS world. Whenever you can recover from an error, or even in case of error within error, it is done. That's maximum level of recovery for you. The latter (fail on error anyway) is, to me, what UNIX is. Your stuff goes dung-of-monkey? Give it a guru meditation (okay, that's Amiga), pardon, core dump. Error value is being nice; exit value is even cool, but nothing fancy on the recovery front. Simple: no recovery. Redo from start. Okay, that's exaggerating, but keep that visual. When I was young, I was VMS minded. In a way, I still am. Yet, for some odd reason, I don't want to be reincarnated to that parallel VMS world just yet. crm114 on the other hand feels like neither. crm114 benefits the most, IMO, from failing - in a controlled way. Currently, it doesn't fail. It doesn't recover either. Because there's quite a few spots where it simply does not check. It just plods on after [hopefully] saying something like "hey, dude. This is some bad Afghan' we're smokin' here, so things may become a wee bitty wobbly, but no worries, eh?" To the point: vanilla and GerH are still in need of a review targeted at inspecting failure nodes and deciding what to do at each of them: a lot of the nonfatals are rather fatal - at least to the call they're in - so my feeling is those should all just exit the call and pop upwards back to the script interpreter: that bugger can then handle the issue and decide (script assisted) whether to exit or retry or do something else entirely. > That implies ONE assumption/restriction for pluripot files in my > approach -- as long as you want to mmap them --: each part has to be > one block, so 'growing' them is a no-go. So it's fine for OSB et al, > but no-go for Hyperspace as that bugger *appends* when training. > > OUCH!!!! I had not thought of that. Then you're welcome to that one. It's on me. A freebie. It's scenarios. Take an idea, then plonk in each of those classifiers and ask 'what happens if I train?' 'what happens if I classify?' etc. Yes, that takes time. But before you start writing code, it's quite useful to do, because you'll know up ahead about several snags that are waiting to bite you. Makes for more robust software. > "Real" databases have very high overheads compared to the hash-and-go > that CRM114 uses for most things. Compare the per-token speed of > Spambayes (which uses a very lightweight DB) versus CRM114, and you'll > see a huge differential. Yech, not like spambayes. Heck, I wasn't talking about lightweight DBs anyway. And the hash tables underlying all the bayes classifiers is a darn smart move, so we should stick with it. Sorry, today is Exaggeration Day here, but I was referring to 'real' databases. The ones which support 'BLOBS'. binary large objects. Of the size of say, 40MByte per record. I.e. stuff where you'd put a single feature set (CSS 'file') in a single record, then do SELECT css.content_blob FROM css_collection AS css WHERE css.type = 'osb' AND css.group_id = 'my_bloody_mailfilter' or something like that, so that those CSS files as we have them now come straight out of a database - which, yes, acts as a kind of 'file system' then. That's the very basics: your pluripot is the recordset coming out of that query, and you can take this to the bank: that query will be /fast/. Because all it does it pick a few (huge) records from a measly table and that's what DBs are darn good at. Drawback to this is that that bloody BLOB is mem-copied, so mmap wins over that one. When you need to append, what you can do, for instance, is just insert the chunk to append as a record; then it's a little harder, but the SELECT up there can have an extra SORT BY so the DB can [re]order those chunks in such a way that to us, it'll [almost] look like several continuous CSS files again. But then you put that in the context of a big server, have a query cache (which will thus spit out the very same blob records on recurrence of the query) and a streamed or batched feed of messages to classify (and train) and that blob memcpy() overhead will spread across all those messages. On large numbers of messages, a memcpy() is indiscernible from a mmap as in the mmap scenario all 'pages' get hit by all those probes (multiple probes per message, multiple messages in a single run: it adds up coverage-wise, so hit coverage % will go up asymptotically). For large numbers, the recordset-memcpy will probably equal the mmap collective, as the page misses take time too: those happen once, and are, under the hood, DMA'd memcpy''s, so it'll be cutting close. Sure, this is not for home-grown mail filters, but for big boys, doing lots of messages: no matter how you are feeding such bastards, mmap or otherwise, in the end it'll only perform when the entire CSS set sits in memory. For ever. (See why I like memory-based APIs so very much? This is my growth path here, and I can do without the intermediate file I/O; it'll only slow me down no end.) Bottleneck is (and remains) classifier.train activity. That action locks all activity to that CSS, so train is (and will remain) expensive. In database terms, its impact is similar to a table lock: everything stops until daddy's done. > Admittedly, CRM114 does it with no regard to hash-clash errors; that's > intentional; after all, half the time, it's "bank error in your favor". I was planning (as a side project) to try a cuckoo-hash based layer instead of the current linear-probe hash table, as cuckoo has, I believe, so very nice characteristics which would help cut down the linear probe lengths when fill rates go up (that's generally published) AND due to its way of storing things, there's a nice little boon waiting to be had for microgroom: now, linear probe, of course, puts the latest feature at the end of a chain. That's bad for microgroom as you have to move the chain back to start wehn you groom. Re-insert all those remaining feature hashes again. With cuckoo (and especially with the extended, modern variants) you can store the latest feature AT THE FRONT OF THE CHAIN. Heck, that's a basic behavioural treat of cuckoo as it comes. So what's microgroom in such a world? Simple! Walk down chain. *Chop*. And you're done. (Modern cuckoo has dual or N entries per hash index, so no moving around required on groom.) Saves on re-placing feature hashes in microgroom chains - which happens, and must happen, in linear probe tables. You can automatically do aged-based microgroom. On the fly. Just limit the 'chain length' in cuckoo and you have microgrooming in effect. The idea is there because at a certain point in life of the CSS, the table will fill up and a periodic groom is in order. As linear probe, at fill rates nearing 50% (and exponentially worsening beyond that - it's exponential all the way, but most say @ <50% it's 'negligible') you've got to do multiple probes per hash anyway, so average cost per hash probe should be about equal for n-bucket cuckoo versus linear probe (once it fills up, which it will, quickly). Ergo: cost savings in microgroom allow for either more speed on average or more & better grooming at sustained speed. Besides, cuckoo behaves favorably at higher fill rates, so CSS's which are in use for a little longer and fill up will see far less perforrmance reduction over time then with linear probe. That's the idea. "All I need to do" is build it. That's why I've been looking at C++-ing this stuff: classifiers use hash table. base class = hash table. derived class #1 = linear probe hashtable, swap for derived class #2 cuckoo hash table, and rerun test set, check timing and ha-hum about the results. Maybe I'm right, maybe I'm wrong. When I'm right, I've got a nice alternative for osb and friends, where featuresets need to be packed in the CSS (= desired higher fill ratio) > But there's an easier way to do it for LEARN than having the user > create a callback. We know what has to happen, so let the classifier > do the heavy lifting: Actually, that's harder, because now you must tell the user up front how much space he's got to reserve for the write (hyperspace: in essense, it's arbitrary dcument length there. Okay, limited to 500K features right now, but that's nitpicking). (And forget about both sides jointly malloc/realloc/free-ing this sort of thing - that's total disaster waiting to happen.) They can as easily screw that up as when they do a callback, so what's the difference? Flexibility. Callbacks are natural for this thing as it's a 1:1 replacement of the current RTL function calls. Okay. I just suppressed a looong sigh. Don't be scared of callbacks. And if the reasoning is (yes, you've said that before, but somehow, ah well...) is that the libcrm114 ' 'users' cannot cope with a callback', well, let me be blunt here: the *user* of libcrm114 will be a 'C' programmer. Nobody else can use libcrm114, as it is a library. At the very least, someone will need to write a wrapper around it, if you're going to offer it to anyone BUT a 'C' programmer. And let me put it this way: any 'C' developer who CANNOT cope with a callback, i.e. a function pointer, should pick up his goods, clear his desk and go back to filling shacks at Wall Mart. Shocking? No. Been there. I've had a time when I didn't grok function pointers. Admittedly, that was a very short time, but then there's also been the time when I didn't understand recursion. /THAT/ time was about a year, give or take a month. I still remember. Because I saw the Light when a friend explained it to me. I was 16 at the time. Was I a programmer then? /No/. I was, irrespective of my age at the time, a *student*. I don't learn 'on the job'. I learn *before* the job. When you code 'C' and are not able to 'grok' what, say, the possible use of a 'void ***p' is, or go numb when you see typedef int fprintf_like_callback114(const char *msg, ...); ... int libcrm114_classify(fprintf_like_callback114 *cb) { ... if (internal_trace) { (*cb)("malloc failed. Stick a fork in us. We're done. (size = %lu)\n", (unsigned long int)malloc_size); } ... } then you should pick up something else. (Try Java. Less chance of running into me on the floor. ;-) ) So if you use interns (you did before, right?) and they go 'huh?' at that example bit (basic interview question. maybe?), then there's a nice little book for them to add to their curriculum. Written by Kernighan & Ritchie. It's all in there. (Okay, 'stick a fork' is yours, not Kernighan's, but you get the point.) (And let me cut off the KISS response beforehand. If 'simple' is so important it forecloses the requirement to learn anything new (and some times it sure looks like that), then wonder why we took up the effort, each of us, to learn to read and write. It took us years (16 or more, and still you're not done) and lots of agony to achieve an active vocabulary which surpasses that of the Teletubbies. That'd make us pretty much 'overachieving complexity lovers' all, because as far as I can tell, those Teletubbies communicate as well as humans in a wide range of subject matter. Ever heard an elephant talk, or type an email? Still, they get older than any human, so dexterity with grammars isn't a survival treat either. So what the Hell did we learn human languages for? Aga aga ag agg KISS.) > So, the classifier codes are free to malloc up a bigger space and copy > into it, or just use available internal space via realloc. Yech. one side of the API does the heap allocation thing, same side has to clean. Or realloc. Especially important once you drop this kind of thing in dynamically loadable libs and environments where mixed run-time libs happen. (Say: Windows. But UNIX as well: different libc's) Use callbacks and you abstract it all out to caller: you feed him the data chunk, he can do whatever he likes. copy, alloc in own space, forward to socket, whatever. Don't try to circumnavigate callbacks. It's part of the idiom. It's in the standard runtime lib. So it's nothing fancy. Get over it. > We still need to put in the capability of a policy that tells > the classifier to trade off accuracy <-> speed <-> storage footprint, > but I want to think about that first. Hm. I thought I had the different classifiers for that. ;-) (speed and accuracy and footprint) You've been telling me I am complexotron, but heck if this ain't going to be one with capital C, that way. The idea with the different hash table (cuckoo vs linear probe) is only easily doable once you move on to C++ (and, yes, the embedded world has C++. All over the place. As long as you don't go fancy-footing by plonking in STL or other template-based libs everywhere you possible can, it's perfectly okay.) And then, such 'cuckoo hash' ideas and such, are, as far as I am concerned, R&D lab *only*. It's good if the code allows things like that to be done to it, makes for a nice R&D _and_ production platform, but when I want to tune accuracy vs performance vs. footprint, I'll just cut down on the size of my CSS, set up groom or whatever the equivalent, to keep fill rate well below 50% in the hash table and that leads to good performance speed-wise, much less so accuracy-wise. But all those parameters are already in place today, for current crm114, so nothing extra needed. Want more? Pick another classifier, heck, drop in a custom tweaked VT matrix if you've got the time to research those, that's at least where I see the performance and accuracy matter (and differ). > yeah. Hmmm... given the usual grand disproportion between > LEARN and CLASSIFY (1 learn per million classifies, or so it seems) > making CLASSIFY as fast as possible is probably the win. Errr... not only Exaggeration Day for me, then. Well, I do get the point. Yes, making classify /fast/ (and keeping it going /fast/) is the ticket. When needed, I can always dedicate a machine to training only and swap CSS's in and out, so the classify boxes can keep running at full speed. Training is delayed, so be it. Decision to train is always after the fact any way; only for real-time apps which auto-train may it matter, and then it's still not a big problem, IMO, if a few messages pass through classify on box A while box B trains a previous item, only to swap the CSSs when done, so that a few messages 'slipped through' the system before the latest training was 'in'. And if I find I cannot live with that 'slippage' anyway, then I, indeed, need to get me some fancy new, faster, gear. > Do yourself a favor, don't make it all separate function arguments, > just have an interface struct (yes, I would call that a çlass', but > that's me) where all those ptr+len & callback args can be filled in. > All of them are related anyway; can't leave out half and still use a > classifier succesfully anyway. > > Matter of taste, really. I still have to type in all those assignments > and whether it goes to slots in a struct or into a calling sequence > directly makes little difference. > > De gustibus non disputandum est. Yup. But the struct way has a little advantage: it's the easy way to have 'default arg values' in C a la C++ and other languages. (Okay, with restriction). memset(&struct_var, 0, sizeof(struct_var)); /* zero all elements */ struct_var.arg3 = xyz; /* fill in only the args which you find important */ call(&struct_var); For the rest, it's indeed taste. > Um, yeah. Those should be CRM_blotz #defines... and actually, they > already are defined and everything. They're bits in a 64-bit field. Currently, yes. > Is the 64-bitness going to cause problems in the embedded system > market? Depends if you are targeting high end only or not. Some of them won't like it, that's for sure. But allow me to point you at a VERY nice 'C' feature, which you haven't used yet: bit fields. Second, when looking at those defines, a few are mutually exclusive in a way which suggests they're rather better served with an enum. Where an 'enum' has the added benefit that most compilers perform type-checking, adding that extra bit of safety/propriety coding validation at compile time, for free. The classifiers should go in an enum: typedef enum { CRM_CL_UNKNOWN = 0, // always handy to help detect morons who didn't spec this item CRM_CL_MARKOVIAN, CRM_CL_OSB, ... } crm_classifier_t; so that cuts out several bits worth' of flags. Then we have all those 'attributes' which we can mix & mash, such as 'unique', 'bychar', etc. Those can remain bit-value #defines, or, when you want to do it another way, without the preprocessor assisting you: typedef struct { unsigned unique: 1; // one bit: boolean! unsigned bychar: 1; unsigned unigram: 1; unsigned microgroom: 1; unsigned refute: 1; } crm_classifier_flags_t; The beauty of bitfields is that they 'pack' into integers, and the : 1 there clearly states to anyone reading it, it's a boolean. Besides, you can have as many as you want in there. No worries about 64-bit / long long support either. (You can also do things like 'int bitfield: 3;' for a 3-bit sized variable, but that's more for when you want to construct protocol packet headers or some such.) Personally, I use #defines with hex values, but that's mostly historic influence: PureC (~ 20 years ago) for MC68K (Atari) produced way better code for your hand-crafted if (flags & BIT_MASK_X) { ... } condition check than the equivalent if (flags_struct.bitfield_x) { ... } while the others produced (close to) identical code. As PureC was my favorite, bitfields became 'fancy' because you knew up front you'd get more opcodes and slower run-time, so I did it only when I wanted to annoy a friend of mine. Ah, those were the days. Though the best thing was when I finally had the money to buy my first ANSI C compiler, so 'the old days' are not what they're played up to be. Today, PureC is long gone (though a good friend of mine still uses it for all his MC68K work) and bitfields are treated like those hex mask #defines, like they otherwise were and are and should. -- Met vriendelijke groeten / Best regards, Ger Hobbelt -------------------------------------------------- web: http://www.hobbelt.com/ http://www.hebbut.net/ mail: [email protected] mobile: +31-6-11 120 978 -------------------------------------------------- ------------------------------------------------------------------------------ Create and Deploy Rich Internet Apps outside the browser with Adobe(R)AIR(TM) software. With Adobe AIR, Ajax developers can use existing skills and code to build responsive, highly engaging applications that combine the power of local resources and data with the reach of the web. Download the Adobe AIR SDK and Ajax docs to start building applications today-http://p.sf.net/sfu/adobe-com