One thing that I see repeated that I should correct:
I am neither 'extending' C, nor writing a new language. I'm only using what C provides.
The structure/class definition does one thing that C++ doesn't, and that's propagate certain kinds of code into the various implementations required for a native solution to a fundamental problem - serialization. The nearest approximation to this code in C++ would essentially require you to write a nice, slow interpreter.
One of the major reasons people turn away from C, even C++ is that these language constructs that I can now maintain trivially are such a hassle to maintain in C/C++, yet easy to manage in other languages. Additional reasons are people have awful habits with pointers, and write bad code.
Here's one more little construct I'll share, it's called a 'generator', and it's a very old device, but it replaces the need for most kinds of threads with a very simple cooperative model. Very handy for console/embedded things.
What it implements is a state machine with anonymous and signalled states, based on __LINE__ directives and a switch, rather than a stack and CPU/OS state. It's very handy for controlling things that happen over time, like a login protocol, or animating something while waiting for it to move. You write simple code like the following, and it will idle or wait. More complex implementations can easily be done where one expects many thousands of these things to operate, where the 'wait' states take the generator out of a list that would otherwise cycle things... but the implementation given was made for a few top-level items.
Keep the number of conditions relatively small, and the switch overhead is insignificant. Keep the number of objects small and top-level, and even long switches are insignificant.
Code:
void MyFunc( MyClass* class )
{
{
// Happens each cycle
}
gen_begin( &class->gen )
{
MyClass_init( class );
}
gen_wait( &class->gen, MyClass_acked(class) )
{
MyClass_localinit( class );
}
gen_setloop(&class->gen)
{
MyClass_cycle( class );
}
gen_handle( &class->gen, sig_Collision )
{
// Handle event...
gen_loop(&class->gen);
}
gen_handle( &class->gen, sig_Hear )
{
// Handle event...
gen_loop(&class->gen);
}
gen_handle( &class->gen, sig_See )
{
// Handle event...
gen_loop(&class->gen);
}
gen_end( &class->gen, throwassert() )
{
// Happens once when generator 'ends'
}
}
Code:
#define _gen_begin (1+type_min(int32)) /* Initial 'begin' state of generator, generally at start of gen handling */
#define _gen_end 2+type_min(int32) /* Final state of generator Just don't define some of it in a macro on the first line of the file. */
#define _line -__LINE__ /* Anonymous state of generator (negative line number in source file) */
#define _gen_signal(sig) (sig) /* Enumerated state of generator */
/**
* \brief C++ version of generator
*
* Inherit from gen (class MyClass : public gen) and place handler in a member function with access to it.
*
**/
#ifdef __cplusplus
class generator
{
public:
int line; /* Line number (if <0), or signal (if >= 0) */
int loop; /* State loop point */
void init() { line = loop = _gen_begin; }
void denit() { line = loop = _gen_end; }
generator() { init(); }
~generator() { denit(); }
void signal( int signal ) { line = _gen_signal(signal); }
};
#else
typedef struct generator
{
int line; /* Line number (if <0), or signal (if >= 0) */
int loop; /* State loop point */
} generator;
#endif
/**
* \brief Declare a generator as static or global data
* May appear in global scope or in structure body
*
* \param gen What to call it; in other invocations it should have whatever indirection is required to reach it; i.e. struct->gen
*
* Works a bit like the list templates in ll.h
**/
#define gen_auto( gen ) gen gen = { _gen_begin, _gen_begin };\
/**
* \brief Initialize or reset a generator
*
* \param gen What to call it; in other invocations it should have whatever indirection is required to reach it; i.e. struct->gen
*
* May appear internal or external to state function, and be conditional
**/
#define gen_init( gen ) { gen.line = gen.loop = _gen_begin; }
/**
* \brief Finish a generator off
*
* \param gen What to call it; in other invocations it should have whatever indirection is required to reach it; i.e. struct->gen
*
* May appear internal or external to state function, and be conditional
**/
#define gen_denit( gen ) { gen.line = gen.loop = _gen_end; }
/**
* \brief Wake up generator, if it was 'finished', and jump to a signal for its next cycle
*
* May appear internal or external to state function, and be conditional
*
* Keep in mind that if the signal was not defined, we'll invoke the 'onbadstate' in gen_end later over it
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
* \param signal The enumerated signal to set off when the generator wakes up
**/
#define gen_signal( gen, signal ) { gen.line = _gen_signal(signal); }
/**
* \brief The beginning of a generator block in cycle
*
* It should have one or more yield states, and be terminated with an 'gen_end()'
* Entire state machine may be wrapped by a conditional
* Code preceding this statement will be executed each time any state is executed
* Implementation of a state machine in the cycler is optional
*
* May only appear at beginning of state machine
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
* If it's a global or auto, just the 'gen' from
**/
#define gen_begin( gen )\
switch( gen.line ) \
{ \
case _gen_begin:
/**
* \brief This is the end state, matching the gen_begin()
* \param onbadstate Something to do when cued to unknown state; may do nothing,
* throw an assert on debug, a macro that harvests local state and reports,
* a 'catch all' subroutine handler for certain kinds of signal handlers, etc.
*
* May only appear at end of state machine
*
* Can not be part of a conditional.
*
* On bad state, finishes the generator, by default
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_end( gen, onbadstate ) \
{} \
gen.line = _gen_end;\
/* Fall-through */\
case _gen_end:\
/* Code following state machine is executed */\
break;\
default: \
{\
/* onbadstate may optinally branch with gen_signal or gen_restart to recover harmlessly */\
onbadstate; \
gen.line = _gen_end;\
}\
return; \
}
/**
* \brief This will yield to main loop and start on the next piece of state when we get back.
*
* May only appear in generator body between gen_begin and gen_end
*
* Can not be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_yield(gen)\
{} \
gen.line = _line; \
return; \
case _line:
/**
* \brief This will yield and repeat the present state from the beginning
*
* May only appear in generator body between gen_begin and gen_end
*
* May be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_repeat(gen) return
/**
* \brief This will poll and not proceed to next state until 'condition' returns non-zero
*
* May only appear in generator body between gen_begin and gen_end
*
* Can not be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
* \param condition Condition to test for. Flow continues when it returns true.
**/
#define gen_wait(gen,condition) \
gen_yield(gen)\
{ if( !(condition) ) return; }
/**
* \brief This will sit and wait until some signal occurs to wake it.
*
* May only appear in generator body between gen_begin and gen_end
*
* Can not be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_sleep(gen) \
gen_yield(gen)\
return;
/**
* \brief This set a loop point for loop to reach
*
* May only appear in generator body between gen_begin and gen_end
*
* Can not be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_setloop(gen) \
{} \
gen.loop = gen.line = _line; \
return; \
case _line:
/**
* \brief This will loop back to last executed gen_setloop position
*
* May only appear in generator body between gen_begin and gen_end
*
* May be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_loop(gen) \
{\
gen.line = gen.loop; \
return; \
}
/**
* \brief This is state to accept a signal
* \param signal The enumerated signal this handles
* May only appear in generator body between gen_begin and gen_end
* If state falls through to this, it will through to the end (generator is skipped in cycle until state is set)
*
* May only appear in generator body between gen_begin and gen_end
*
* Can not be part of a conditional.
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_handle( gen, signal ) \
gen_sleep(gen)\
case _gen_signal(signal):
/**
* \brief Signal to a given state, and yield for that state to take place
*
* May only appear in generator body between gen_begin and gen_end
*
* May be part of a conditional
*
* Keep in mind that if the signal was not defined, we'll invoke the 'onbadstate' in gen_end later over it
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
* \param signal The enumerated signal to set off when the generator wakes up
**/
#define gen_signal_yield( gen, signal )\
{\
gen.line = _gen_signal(signal);\
return;\
}
/**
* \brief Restart generator from beginning, and end this state
*
* May only appear in generator body between gen_begin and gen_end
*
* May be part of a conditional
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_restart_yield(gen) \
{\
gen.line = _gen_begin;\
return;\
}
/**
* \brief End generator, and end this state
*
* May only appear in generator body between gen_begin and gen_end
*
* May be part of a conditional
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_finish_yield(gen) \
{\
gen.line = _gen_end;\
return;\
}
/**
* \brief Restart generator from beginning
*
* May appear internal or external to state function
*
* May be part of a conditional
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_restart(gen) \
{\
gen.line = _gen_begin;\
}
/**
* \brief End generator
*
* May appear internal or external to state function
*
* May be part of a conditional
*
* \param gen Name of generator with whatever indirection is required to reach its instance.
**/
#define gen_finish(gen) \
{\
gen.line = _gen_end;\
}
#endif /* GEN_H */