|
Þ briarpig » code |
|
09aug09
«
programming
direction « I had a lot of plans for this site when I started. Now I no longer have plans, except to enjoy myself. Obviously the fiction is just for fun. But, how do I plan to find fun in writing about code? Well, it's unlikely to be fun, so I have to settle for useful or vaguely interesting. Sub pages under this one focus on one or another specific topic; this page mainly notes a tendency for things to go wrong. My first three target topics are my habits in code writing, async coding styles, and elements of dynamic programming languages. But these pages might remain stubs for some time. I'll write about coding habits first. (Note jobs is an early draft of this page, considering context in jobs for coding; you can skip that one.) 02sep09
«
void
A new void page develops random snippets of sample code from scratch, without prior context. (Maybe I'll start with continuations in C++.) 05sep09
«
main
A new main page describes a main() entry point and a simple computing base for writing and testing C and C++ code depending on little else. (A C runtime is assumed true, but not much else is assumed—no standard C++ libraries.) 11sep09
«
i/o
A new io page shows in and out streams, of various sorts, that would otherwise make main bigger than necessary. 24aug09
«
thorn
Older sample code pages are rooted by the thorn page. Newer sample code, rooted under this page, has a different aim. 08sep09
«
big picture
Obviously, this is a great place to shill a big vision of code, if I have one to sell, which I don't. Maybe you hope I'll show you where to stand and move the world, provided you have a lever long enough. Sorry, I don't know how, because you can't make a lever long enough. Like all other tools, code becomes fragile when scaled. In the real world, what happens in the big picture with code depends more on what people are like, than on machines. And since people are complex, that's what you get. (I'm simplifying, of course.) So most of my content is about code in-the-small, in specific contexts, so you can get precise effects right in a narrow domain, at a level where experiments work. Many folks are tempted by an idea you can extend a big organizing vision from a top level domain down into bottom details, and expect nothing but order. But this ignores how chaos works. The closest I come to advising how to avoid chaos is this: keep your tools simple, few, and crisp. Even then, you still have a big challenge. Now, onto simple tools in small contexts. Steel yourself for random, isolated, narrow tactics. 22aug09
«
model
perspective « What's my programming model? I use C++, so do I think everything's an object? What's my theory about how coding works? My perspective is very low level. The model I use is bytes in memory—all code and data are byte patterns in an address space. Everything else is simulated on top of this. Pointers are just hardware level simulations of space reference. In other words, I think in assembler, or machine code—obviously in an abstract manner when a processor isn't specified. When I code in C or C++, I don't use the model promised by the language. Executing code modifies byte values stored in memory. My model of application behavior is which bytes change at runtime, and when, and what those bytes mean to anyone who looks at them. If this isn't how you think too, you may have trouble following some of my code. In particular, if you think a programming language is about syntax, or specific standard class names, then I pity you. (But I fear you too, and try to keep clear.) tasks « With that perspective, most of my code is concerned with tasks of moving bytes around where needed, while touching fewer cache lines for performance, while coping with concurrent asynchronous events like disk i/o and network traffic. Local goals tend to focus on representing state of some computation in progress, using bytes in memory, in a form suited to handling future event messages, also using bytes in memory. Sample code I show may feature very mundane bits of this:
This is boring, of course. But that's what most coding is about: building up more interesting effects from very boring constituent pieces. Unfortunately for you, I'm only going to present boring parts—unless I ever get around to showing a toy language interpreter, which will only be slightly less boring. You might find it more interesting, but I don't anymore. You can use this stuff to get higher level things done that I still find interesting. But I won't say much about that; I just want more folks to grasp simple things. 22aug09
«
agenda
verify « I must have an agenda, right? Every programmer wants to sell you their pet theory about how it's done—the best thing since sliced bread. Isn't that what you expect? A sell job? So what am I selling? A programming language? A style of coding, like functional or object-oriented? Or some new fangled magic bullet? Actually, I don't have anything to sell you. No single thing always works best. So keep doing whatever you're doing, if it works. Personally, I use a little of everything. Sure, some of my code is object-oriented, and other parts are better under concurrency without shared mutable state. But that's not what I'm selling. You might say some of my tactics are evidence-oriented, but I'm not interested in coining new jargon. So my concern is often just evidence—how do you know one thing happens instead of another? By using scientific method. Try it and see what happens. But to see you must measure: confirm requirements and falsify anything you hope never happens. In other words, I don't see anything but science in my agenda, so why is a new term needed? Maybe my agenda is: verify what you believe. In my code you'll see I suspect a language's runtime is wrong. I check for things like use of C++ objects before construction or after destruction, because in practice it happens, though rarely. So I don't take language guarantees for granted. I test them: usually with aggressive asserts, or the like. It reduces debugging time. trust « Some of my coding style is lack of trust: I don't believe third party libraries are correct unless I verify correct behavior myself, at least by sampling. Neither do I trust my own code without tests and runtime sampling. I advocate re-inventing wheels when you can test and verify your wheel, but not an existing standard wheel. Time permitting, I write code to print everything, so I can eyeball serialized object graphs to look for problems—or just to see agreement with expectations. Code to print everything is time consuming: it's as bad as time for manual memory management of space in C and C++. It strikes many folks as a waste of time. So in work environments, few objects get printing code unless I have time for unit tests. trying « I think productivity is enhanced when you can try something experimentally with rapid turn around, and see the results, especially if results are isolated from other code, so you can be sure any effect is due only to what you tried (and not all other dependencies in your build system). Speed and clarity of evidence matters. This is a theme you should expect to see in my sample code: one should ask, "How long before I can see if this does what I expect?" Objects should be usable in isolation, without requiring a huge app context to exist first. Low level code must be grounded in terms clear enough that you can try pieces independently. Print your inputs, intermediary objects, and outputs. Look at them: See anything wrong? To make complex systems, compose them from towers of simpler components. But if you don't know what simple pieces do, in detail, you don't know what they do in composition either. Many folks are lulled into false security when nothing obviously seems wrong, looking at coarse global behavior. That's a mistake. Seek out bad news. Don't hide from negative evidence. The sooner you find out, the more time you have for fixes, and the less time you waste pursuing false causes for quirky symptoms in system behavior. 10aug09
«
background
audience « I assume you know both C and C++ quite well. I won't teach you how to use C and C++. In fact, if it's your goal to code in C and C++ "the way you're supposed to," then you ought to read the appropriate religious texts instead of my sample code, which might poison your style with casual hacking heresy. goals « Specific coding goals are notably absent. (This is addressed further on a separate page.) Code I discuss might appear in any app at all, just as part of moving data around or seeing this occurs correctly. When you code in C or C++, managing memory and low level detail occurs all the time, no matter what your high level goal. My coding topics are application independent patterns, typically near the bottom of your call tree, where memory is touched. So even though I typically work on servers, none of the code I show is server specific, except perhaps in a sense more care is needed to re-use limited memory as effectively as possible. (You can never have enough memory in a scaling server, so it's always limited, no matter how much you have.) I don't care if you're doing web apps or some other app. I'll never discuss what you're trying to achieve as a product. As a result, no top-down context is assumed or implied, so you might feel unconscious frustration when nothing is qualified by the end goal. (You might keep asking: What are we trying to achieve? Sorry, you need to supply that part.) 10aug09
«
example
utils « Most of my content might be examples of C and C++ code written to do something specific. So there's no over-arching grand plan, just tiny puzzles with plausible looking fast solutions. But many items do have something in common: I do them over and over again, starting from scratch, or cloning old versions. Why should I write these trivial programming examples? I don't find them interesting any more. Quite the reverse: I'm sick of some of them. But it's usually necessary to do many of these things in most projects, no matter what the end product. Note there's no domain specific context for most of my sample code. So here's a note on that topic: These basic utilities do simple things, and won't help you solve large, complex problems, beyond squaring away trivial details at the bottom of your call tree, where bottom-up pieces go. You'll still be swamped by large problems. But teasing apart detail at the bottom will be easier. Solving your big problem will be just as hard; only small problems will get simpler. 10aug09
«
chronic
rewrites « My coding style assumes you'll rewrite everything, multiple times, like I did. As a result, I often expect this is necessary. So I code less for posterity: if something must be changed, I just change it. This approach frees you—a little—from making a best choice up front. There is no choice best for all problems, for all time. I hope this explains a weird thing you'll notice: no provision is made for solving problems generally in a framework so code can be re-used without change. My code re-use plan is simple but labor intensive: you'll rewrite the code. If you have a better way, then more power to you. I started my professional C++ life in 1990 on a project aiming to create "correct" C++ libraries, surviving use by many apps without big changes. The cost of such high quality library targets is not my problem. Now I work on one-off applications—usually servers—whose code is not re-used by other products, other than a next revision of the same product. Arbitrary rewrites are okay. So I write code with less flexibility than it might have, because strong type checking forms a tighter loop right around my intended target. Narrow scope can make code more likely to work if it just manages to compile. And if it wanders off target, debugging is easier when fewer options are possible. 10aug09
«
religion
avoided « I have little to say about various coding movements. I simply use techniques when useful. For example, folks often enthused about "object-oriented" programming in the 90's, as if it would solve all the world's problems. My code is sometimes object-oriented, but often not, depending on whether it helps a design without making code complex. An oo style helps when alternatives conform to one api. By preference, I often use C++ as a "better C" because class namespaces do much of what I need: isolate behavior in the right scope. And when I do the same things in C the result is more verbose. Unfortunately, C++ is a fairly complex language, if you use all features, which I avoid. Using C++, when C might do as well, might seem like a cost I could avoid imposing on readers. Why should I make you learn some C++ if you don't know it already? Frankly, that's not my problem. Since I know C++, I treat the cost of using it as zero even if your cost is higher. I treat C as sometimes absolutely necessary to use, because client code might be written in C, so a C++ api must be wrapped in a C layer. |