??? 09/02/07 08:49 Modified: 09/02/07 09:08 Read: times Msg Score: +1 +1 Informative |
#143884 - Good topic! Responding to: ???'s previous message |
I'd say that the most valuable programming techniques are the really fundamental and universal ones that a lot of us take for granted these days and don't really think about too much. We just use them.
I'm talking about techniques like functional decomposition, which lets us tackle overwhelming problems by breaking them down into manageable pieces. Another biggie is the 40-year old idea of structured programming, which seems to have done a fairly good job of constraining the amount of spaghetti code in the world. (Except in 8052.com weekend quizzes, of course.) My favorite technique that's more along the lines of what Erik has suggested is that of encoding a program's logic in its data rather than in its procedures. The trick is to invent a table that somehow describes what you want to happen, and then build a short interpreter that reads the table and does what it says. This lets you express your solution in terms more closely related to the problem, and often results in a more compact program than you would get otherwise. One of the old Bell Labs UNIX gurus, Jon Bentley, discusses this idea in detail in one of his books--it's either Programming Pearls or More Programming Pearls: Confessions of a Coder, I can't remember which. (What, you haven't read these books yet? Do so! They're great.) As it turns out, a really good example of this technique is the implementation of a state machine! Very often, you see a state machine coded procedurally, with its logic specified by nested switch statements, something like this state = STATE_1; // Initialize state variable while (1) { // Run forever event = GetNextEvent(); switch (state) { case STATE_1: // STATE_1 switch (event) { case EVENT_1: DoSomething(); state = STATE_1; break; case EVENT_2: DoSomethingElse(); state = STATE_2; break; case EVENT_3: DoNothing(); state = STATE_3; break; } break; // End STATE_1 case STATE_2: // STATE_2 switch (event) { case EVENT_1: DoSomethingExotic(); state = STATE_3; break; case EVENT_2: DoSomethingEsoterik(); state = STATE_3; break; case EVENT_3: EatDirtAndDie(); state = STATE_1; break; } break; // End STATE_2 case STATE_3: // STATE_3 switch (event) { case EVENT_1: Etc(); state = STATE_1; break; case EVENT_2: EtcEtc(); state = STATE_1; break; case EVENT_3: EtcEtcEtc(); state = STATE_2; break; } break; // End STATE_3 } } // End 'run forever' This is already pretty nasty, and it can get a lot worse if you need a lot of different states and/or a lot of events. I think it works a lot better to specify the state machine in a table, something like this: typedef struct { void (*pEventHandler)(); int nextState; } CEventInfo; typedef struct { CEventInfo events[NUMBER_OF_EVENTS]; } CStateInfo; CStateInfo stateTable[] = { {{DoSomething, STATE_1}, {DoSomethingElse, STATE_2}, {DoNothing, STATE_3}}, {{DoSomethingExotic, STATE_3}, {DoSomethingEsoterik, STATE_3}, {EatDirtAndDie, STATE_1}}, {{Etc, STATE_1}, {EtcEtc, STATE_1}, {EatEtcEtc, STATE_2}}}; With a table like that in place, then, the "engine" that runs the state machine can be just a few lines long: state = STATE_1; // Initialize state variable while (1) { // Run forever event = GetNextEvent(); (*stateTable[state].events[event].pEventHandler)(); state = stateTable[state].events[event].nextState; } // End 'run forever' -- Russ PS: Please regard the above as C-like pseudocode, and not as ready-to-run code. I haven't tried to compile or run any of it. |