4

From Zed Shaw's, Learn C the Hard Way,in exercise 23, he talks about Duff's Device. Here is Duff's Device for reference:

int duffs_device(char *from, char *to, int count)
{
    {
        int n = (count + 7) / 8;

        switch(count % 8) {
            case 0: do { *to++ = *from++;
                        case 7: *to++ = *from++;
                        case 6: *to++ = *from++;
                        case 5: *to++ = *from++;
                        case 4: *to++ = *from++;
                        case 3: *to++ = *from++;
                        case 2: *to++ = *from++;
                        case 1: *to++ = *from++;
                    } while(--n > 0);
        }
    }

    return count;
 }

He asks the reader to:

"Create a set of macros that lets you create any length device like this. For example, what if you wanted to have 32 case statements and didn't want to write out all of them? Can you do a macro that lays down 8 at a time?"

This really stumped me and I feel I just need a nudge in the right direction. Any help would be greatly appreciated!

a3f
  • 8,517
  • 1
  • 41
  • 46
Fumbster
  • 99
  • 7
  • Duff's Device, though nifty in select coding, is not a useful general purpose C idiom. You may find some kudos using it, but be prepared for negative push-back. – chux - Reinstate Monica Dec 16 '13 at 02:25
  • @chux- I think he's just trying to learn, and that's why asking for it. – vidit Dec 16 '13 at 02:28
  • 2
    I understand it's general uselessness with today's processors, however this question about creating a macro for it really stumped me. I'm interested in figuring out how to create a macro for it. I'm not going to be able to go to sleep tonight without figuring it out. – Fumbster Dec 16 '13 at 02:30
  • Hah, I would also be of the camp of "if you're calling it explicitly 'Duff's device', you're doing it wrong". ;) – Jim Buck Dec 16 '13 at 02:37

1 Answers1

6

Something like this:

#define LAYDUFF(x, y) \
    case ((0 ## x ## y) + 1) : *to++ = *from++

#define SEVEN_AT_ONCE(x) \
    LAYDUFF(x,6); \
    LAYDUFF(x,5); \
    LAYDUFF(x,4); \
    LAYDUFF(x,3); \
    LAYDUFF(x,2); \
    LAYDUFF(x,1); \
    LAYDUFF(x,0)

#define EIGHT_AT_ONCE(x)    \
    LAYDUFF(x,7);           \
    SEVEN_AT_ONCE(x)

int duffs_device(char *from, char *to, int count)
{
    {
        int n = (count + 31) / 32;

        switch(count % 32) {
            case 0: do { *to++ = *from++;
                        SEVEN_AT_ONCE(3);                   
                        EIGHT_AT_ONCE(2);
                        EIGHT_AT_ONCE(1);
                        EIGHT_AT_ONCE(0);
                    } while(--n > 0);
        }
    }

    return count;
 }

will expand into

        case ((036) + 1) : *to++ = *from++; // = 31

                     ...

        case ((000) + 1) : *to++ = *from++; // = 1

Update:

Or, you can rewrite the first macro:

        #define LAYDUFF(x, y) \
case (8 * x + y + 1) : *to++ = *from++

Which is basically the same, except that it doesn't use octal numbers.

dsi
  • 586
  • 3
  • 13
  • 1
    `case ((x) * 8 + (y) + 1):` would have worked just as well, and is arguably easier to understand. – rici Dec 16 '13 at 02:51