I was thinking about it yesterday and here is something that is quite close to the arduino approach, but doesn't suffer its performance penalty:
#define VAR_SET(var, id, pin) var ## id |= _BV(pin)
#define VAR_CLR(var, id, pin) var ## id &=~_BV(pin)
#define VAR_TOG(var, id, pin) var ## id ^= _BV(pin)
#define VAR_GET(var, id, pin) ((var ## id) & _BV(pin))
#define pinOutput(pin) VAR_SET(DDR, pin)
#define pinInput(pin) VAR_CLR(DDR, pin)
#define pinSet(pin) VAR_SET(PORT, pin)
#define pinClr(pin) VAR_CLR(PORT, pin)
#define pinTog(pin) VAR_TOG(PORT, pin)
You would define a pin, like
#define HC595_SCK B, 0 //hc595 on portb.0
and use it later:
pinOutput(HC595_SCK); //hc595_sck as output
...
do {
pinClr(HC595_SCK); //clear hc595_sck
...
pinSet(HC595_SCK); //set hc595_sck
}
If you have reassigned HC595_SCK to PORTC.6, you just need to redefine it:
#define HC595_SCK C, 6 //sck now on portc.6
and recompile.
Something like this is very portable as it does not rely on bit fields, and present no performance penalty or re-entrance issues.