Josh Dybnis on 10 Jun 2003 14:22:01 -0000


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[ALACPP] Generalized Functors in C


I've ported Loki's generalized functors to C, using the preprocessor
instead of templates.

In C there are only two callable entities:
  - functions
  - pointers to functions

We add a third: 
  - functors

Since C does not have operator overloading, functors are invoked with
the syntax EXECUTE_FUNCTOR(), and are deleted with FREE_FUNCTOR().
Other than that the interface is elegant.

The code the library generates respects the C typesystem. In otherwords
it doesn't do anything dangerous like casting arguments to (void *).

It has only been tested with gcc. But it should work on any C99
compliant compiler. I don't have a web page set up, so I've attached
the files. It's a small list so I hope no one minds.

-Josh Dybnis
/******************************************************************************/
/* C port of Generalized Functor from Chapter 5 of "Modern C++ Design"        */
/******************************************************************************/

/**** functor.c **************************************************************/

#include <stdlib.h>
#include <string.h>
#include "functor.h"

void I_free_functor_chain (I_functor_chain c)
{
    I_functor f1, f2;
    
    f1 = c->f1;
    f2 = c->f2;
    free(c);
    FREE_FUNCTOR(f1);

    /* hopfully the compiler does tail-call optimization, or on a long chain
     * this could blow the stack */
    FREE_FUNCTOR(f2); 
}

void I_free_bound_functor (I_bound_functor f)
{
    FREE_FUNCTOR(f->f);
    free(f);
}

I_functor I_clone_functor (I_functor f1)
{
    I_functor f2;
    f2 = (I_functor) malloc(f1->size);
    memcpy(f2, f1, f1->size);

    return f2;
}

I_functor_chain I_clone_functor_chain (I_functor_chain c1)
{
    I_functor_chain c2;
    c2 = (I_functor_chain) malloc(sizeof(*c2));
    memcpy(c2, c1, sizeof(*c2));
    c2->f1 = CLONE_FUNCTOR(c1->f1);
    c2->f2 = CLONE_FUNCTOR(c1->f2);

    return c2;
}

I_bound_functor I_clone_bound_functor (I_bound_functor f1)
{
    I_bound_functor f2;
    f2 = (I_bound_functor) malloc(f1->size);
    memcpy(f2, f1, f1->size);
    f2->f = CLONE_FUNCTOR(f1->f);

    return f2;
}

/****************************************************************************/
/* C port of Generalized Functor from Chapter 5 of "Modern C++ Design"      */
/*                                                                          */
/* Josh Dybnis:  June 9, 2003                                               */
/****************************************************************************/
/*                                                                          */
/* If my answers frighten you then you should cease asking scary questions. */
/*                                                                          */
/* - Pulp Fiction (1994)                                                    */
/****************************************************************************/

#ifndef _FUNCTOR_H_
#define _FUNCTOR_H_

#include <stdlib.h>

#define EXECUTE_FUNCTOR(functor, ...) \
    (functor)->execute((functor), ## __VA_ARGS__)

#define FREE_FUNCTOR(functor) \
    (functor)->free(functor)

#define CLONE_FUNCTOR(functor) \
    (functor)->clone(functor)

#define DEC_FUNCTOR_TYPE(...) I_DEC_FUNCTOR_TYPE(__VA_ARGS__)
#define I_DEC_FUNCTOR_TYPE(ret, functor, n, ...) \
    typedef struct functor *functor;                    \
    struct functor                                      \
    {   ret     (*execute)(functor I_I##n __VA_ARGS__); \
        void    (*free)   (functor);                    \
        functor (*clone)  (functor);                    \
        size_t size;                                    \
        ret (*fp)(__VA_ARGS__);                         \
    };                                                  \
    static inline functor new_##functor (ret (*fp)(__VA_ARGS__))     \
    {   static ret I_execute_##functor (functor I_I##n __VA_ARGS__); \
        functor f = (functor) malloc(sizeof(*f));                    \
        f->execute = I_execute_##functor;                            \
        f->free    = (void    (*)(functor)) free;                    \
        f->clone   = (functor (*)(functor)) I_clone_functor;         \
        f->size    = sizeof(*f);                                     \
        f->fp      = fp;                                             \
        return f;                                                    \
    }                                                                \
    static inline functor chain_##functor (functor f1, functor f2) \
    {   static void I_execute_##functor##_chain ();                \
        I_functor_chain c = (I_functor_chain)malloc(sizeof(*c));   \
        c->execute = (I_void_fp)I_execute_##functor##_chain;       \
        c->free    = (I_void_fp)I_free_functor_chain;              \
        c->clone   = (I_void_fp)I_clone_functor_chain;             \
        c->f1      = CLONE_FUNCTOR((I_functor)f1);                 \
        c->f2      = CLONE_FUNCTOR((I_functor)f2);                 \
        return (functor)c;                                         \
    }
    
#define DEF_FUNCTOR_TYPE(...) I_DEF_FUNCTOR_TYPE(__VA_ARGS__)
#define I_DEF_FUNCTOR_TYPE(ret, functor, n, ...)       \
    ret I_execute_##functor                            \
        (functor f I_MAP(0, n, I_D, I_D, __VA_ARGS__)) \
    {   f->fp(I_MAP(0, n, I_G1, I_G2, __VA_ARGS__));   \
    }                                                  \
    ret I_execute_##functor##_chain                    \
        (I_functor_chain c I_MAP(0, n, I_D, I_D, __VA_ARGS__)) \
    {   I_APPLY0                                               \
        (   EXECUTE_FUNCTOR,                                   \
            (functor)c->f1                                     \
            I_MAP(0, n, I_F, I_F, __VA_ARGS__)                 \
        );                                                     \
        return I_APPLY0                                        \
               (   EXECUTE_FUNCTOR,                            \
                   (functor)c->f2                              \
                   I_MAP(0, n, I_F, I_F, __VA_ARGS__)          \
               );                                              \
    }

#define LOC_FUNCTOR_TYPE(...) \
    DEC_FUNCTOR_TYPE(__VA_ARGS__) \
    DEF_FUNCTOR_TYPE(static __VA_ARGS__)

#define DEC_FUNCTOR_FROM_FUNCTION(...) \
    I_REORDER(I_DEC_FUNCTOR_FROM_FUNCTION, __VA_ARGS__)

#define I_DEC_FUNCTOR_FROM_FUNCTION(r1, function, n, r2, functor, m, ...) \
    typedef struct I_##functor##_##function *I_##functor##_##function; \
    struct I_##functor##_##function        \
    {   I_void_fp execute;                 \
        I_void_fp free;                    \
        I_void_fp clone;                   \
        size_t size;                       \
        I_MAP(0, n, I_A, I_A, __VA_ARGS__) \
    };                                     \
    static inline functor new_##functor##_from_##function          \
        (I_K##n I_MAP(0, n, I_B1, I_B2, __VA_ARGS__))              \
    {   r2 I_execute_##functor##_##function();                     \
        I_##functor##_##function f;                                \
        f = (I_##functor##_##function)malloc(sizeof(*f));          \
        f->execute = (I_void_fp) I_execute_##functor##_##function; \
        f->free    = (I_void_fp) free;                             \
        f->clone   = (I_void_fp) I_clone_functor;                  \
        f->size    = sizeof(*f);                                   \
        I_MAP(0, n, I_C, I_C, __VA_ARGS__)                         \
        return (functor)f;                                         \
    }                                                              \
    r1 function (__VA_ARGS__ I_J##n(m)); \
    static inline functor new_##functor(r1 (*)(I_SKIP##n(__VA_ARGS__) I_K##m)); 
    
#define DEC_POINTER_TYPE(ret, name, ...) \
    typedef ret (*name)(__VA_ARGS__);

#define DEC_FUNCTOR_FROM_POINTER(...) \
    I_REORDER(I_DEC_FUNCTOR_FROM_POINTER, __VA_ARGS__)
    
#define I_DEC_FUNCTOR_FROM_POINTER(r1, type, n, r2, functor, m, ...) \
    typedef struct I_##functor##_##type *I_##functor##_##type; \
    struct I_##functor##_##type            \
    {   I_void_fp execute;                 \
        I_void_fp free;                    \
        I_void_fp clone;                   \
        size_t size;                       \
        type fp;                           \
        I_MAP(0, n, I_A, I_A, __VA_ARGS__) \
    };                                     \
    static inline functor new_##functor##_from_##type             \
        (type fp, I_MAP(0, n, I_D, I_D, __VA_ARGS__))             \
    {   r2 I_execute_##functor##_##type();                        \
        I_##functor##_##type f;                                   \
        f = (I_##functor##_##type)malloc(sizeof(*f));             \
        f->execute    = (I_void_fp) I_execute_##functor##_##type; \
        f->free       = (I_void_fp) free;                         \
        f->clone      = (I_void_fp) I_clone_functor;              \
        f->size       = sizeof(*f);                               \
        f->fp         = fp;                                       \
        I_MAP(0, n, I_C, I_C, __VA_ARGS__)                        \
        return (functor)f;                                        \
    }                                                             \
    static inline functor new_##functor(r1 (*)(I_SKIP##n(__VA_ARGS__) I_K##m)); 

#define DEC_FUNCTOR_FROM_FUNCTOR(...) \
    I_REORDER(I_DEC_FUNCTOR_FROM_FUNCTOR, __VA_ARGS__)
    
#define I_DEC_FUNCTOR_FROM_FUNCTOR(r1, f1, n, r2, f2, m, ...) \
    typedef struct I_##f2##_##f1 *I_##f2##_##f1; \
    struct I_##f2##_##f1                   \
    {   I_void_fp execute;                 \
        I_void_fp free;                    \
        I_void_fp clone;                   \
        size_t size;                       \
        f1 f;                              \
        I_MAP(0, n, I_A, I_A, __VA_ARGS__) \
    };                                     \
    static inline f2 new_##f2##_from_##f1                     \
        (f1 b I_MAP(0, n, I_D, I_D, __VA_ARGS__))             \
    {   r2 I_execute_##f2##_##f1();                           \
        I_##f2##_##f1 f = (I_##f2##_##f1)malloc(sizeof(*f));  \
        f->execute = (I_void_fp) I_execute_##f2##_##f1;       \
        f->free    = (I_void_fp) I_free_bound_functor;        \
        f->clone   = (I_void_fp) I_clone_bound_functor;       \
        f->size    = sizeof(*f);                              \
        f->f       = CLONE_FUNCTOR(b);                        \
        I_MAP(0, n, I_C, I_C, __VA_ARGS__)                    \
        return (f2)f;                                         \
    }                                                         \
    static inline f1 new_##f1(r1 (*)(__VA_ARGS__ I_J##n(m))); \
    static inline f2 new_##f2(r1 (*)(I_SKIP##n(__VA_ARGS__) I_K##m)); 

#define DEF_FUNCTOR_FROM_FUNCTION(...) \
    I_REORDER(I_DEF_FUNCTOR_FROM_FUNCTION, __VA_ARGS__)
    
#define I_DEF_FUNCTOR_FROM_FUNCTION(r1, function, n, r2, functor, m, ...) \
    r1 I_execute_##functor##_##function                                   \
        (I_##functor##_##function f I_MAP(n, m, I_D, I_D, __VA_ARGS__)) \
    {   return function(I_MAP(0, n, I_E1, I_E2, __VA_ARGS__) I_H##n(m)  \
                        I_MAP(n, m, I_G1, I_G2, __VA_ARGS__));          \
    }

#define DEF_FUNCTOR_FROM_FUNCTOR(...) \
    I_REORDER(I_DEF_FUNCTOR_FROM_FUNCTOR, __VA_ARGS__)

#define I_DEF_FUNCTOR_FROM_FUNCTOR(r1, f1, n, r2, f2, m, ...) \
    r2 I_execute_##f2##_##f1                                       \
        (I_##f2##_##f1 f I_MAP(n, m, I_D, I_D, __VA_ARGS__))       \
    {   return EXECUTE_FUNCTOR                                     \
               (    f->f,                                          \
                    I_MAP(0, n, I_E1, I_E2, __VA_ARGS__) I_H##n(m) \
                    I_MAP(n, m, I_G1, I_G2, __VA_ARGS__)           \
               );                                                  \
    }

#define DEF_FUNCTOR_FROM_POINTER(...) \
    I_REORDER(I_DEF_FUNCTOR_FROM_POINTER, __VA_ARGS__)
    
#define I_DEF_FUNCTOR_FROM_POINTER(r1, type, n, r2, functor, m, ...) \
    r2 I_execute_##functor##_##type                                  \
        (I_##functor##_##type f I_MAP(n, m, I_D, I_D, __VA_ARGS__))  \
    {   return f->fp(I_MAP(0, n, I_E1, I_E2, __VA_ARGS__) I_H##n(m)  \
                     I_MAP(n, m, I_G1, I_G2, __VA_ARGS__));          \
    }

#define LOC_FUNCTOR_FROM_FUNCTION(...) \
    DEC_FUNCTOR_FROM_FUNCTION(static __VA_ARGS__) \
    DEF_FUNCTOR_FROM_FUNCTION(static __VA_ARGS__) 

#define LOC_FUNCTOR_FROM_POINTER(...) \
    DEC_FUNCTOR_FROM_POINTER(static __VA_ARGS__) \
    DEF_FUNCTOR_FROM_POINTER(static __VA_ARGS__) 

#define LOC_FUNCTOR_FROM_FUNCTOR(...) \
    DEC_FUNCTOR_FROM_FUNCTOR(static __VA_ARGS__) \
    DEF_FUNCTOR_FROM_FUNCTOR(static __VA_ARGS__) 

#define I_REORDER(macro, ret, functor, n, ...)                  \
    I_APPLY3                                                    \
    (   macro,                                                  \
        I_APPLY1(I_AT1,   I_SKIP##n(__VA_ARGS__)),              \
        I_APPLY1(I_AT2,   I_SKIP##n(__VA_ARGS__)),              \
        I_APPLY1(I_AT3,   I_SKIP##n(__VA_ARGS__)),              \
        ret,                                                    \
        functor,                                                \
        n                                                       \
        I_APPLY2(I_H1, I_APPLY1(I_AT3, I_SKIP##n(__VA_ARGS__))) \
        I_APPLY1(I_SKIP3, I_SKIP##n(__VA_ARGS__))               \
        I_I##n I_TAKE##n(__VA_ARGS__)                           \
    )
    
#define I_APPLY0(f, ...) f(__VA_ARGS__)
#define I_APPLY1(f, ...) f(__VA_ARGS__)
#define I_APPLY2(f, ...) f(__VA_ARGS__)
#define I_APPLY3(f, ...) f(__VA_ARGS__)
#define I_AT1(first, ...) first
#define I_AT2(first, second, ...) second
#define I_AT3(first, second, third, ...) third
#define I_MAP(n, m, f, l, ...) \
    I_APPLY0(I_MAP##m, f, l, I_SKIP##n(__VA_ARGS__))
#define I_MAP0(f, l,    ...)
#define I_MAP1(f, l, h, ...)  l(h, 1)
#define I_MAP2(f, l, h, ...)  f(h, 2)I_MAP1(f, l, __VA_ARGS__)
#define I_MAP3(f, l, h, ...)  f(h, 3)I_MAP2(f, l, __VA_ARGS__)
#define I_MAP4(f, l, h, ...)  f(h, 4)I_MAP3(f, l, __VA_ARGS__)
#define I_MAP5(f, l, h, ...)  f(h, 5)I_MAP4(f, l, __VA_ARGS__)
#define I_MAP6(f, l, h, ...)  f(h, 6)I_MAP5(f, l, __VA_ARGS__)
#define I_MAP7(f, l, h, ...)  f(h, 7)I_MAP6(f, l, __VA_ARGS__)
#define I_MAP8(f, l, h, ...)  f(h, 8)I_MAP7(f, l, __VA_ARGS__)
#define I_MAP9(f, l, h, ...)  f(h, 9)I_MAP8(f, l, __VA_ARGS__)
#define I_MAP10(f, l, h, ...) f(h, 10)I_MAP9(f, l, __VA_ARGS__)
#define I_MAP11(f, l, h, ...) f(h, 11)I_MAP10(f, l, __VA_ARGS__)
#define I_MAP12(f, l, h, ...) f(h, 12)I_MAP11(f, l, __VA_ARGS__)
#define I_MAP13(f, l, h, ...) f(h, 13)I_MAP12(f, l, __VA_ARGS__)
#define I_MAP14(f, l, h, ...) f(h, 14)I_MAP13(f, l, __VA_ARGS__)
#define I_MAP15(f, l, h, ...) f(h, 15)I_MAP14(f, l, __VA_ARGS__)
#define I_SKIP0(...)     __VA_ARGS__
#define I_SKIP1(h, ...)  I_SKIP0(__VA_ARGS__)
#define I_SKIP2(h, ...)  I_SKIP1(__VA_ARGS__)
#define I_SKIP3(h, ...)  I_SKIP2(__VA_ARGS__)
#define I_SKIP4(h, ...)  I_SKIP3(__VA_ARGS__)
#define I_SKIP5(h, ...)  I_SKIP4(__VA_ARGS__)
#define I_SKIP6(h, ...)  I_SKIP5(__VA_ARGS__)
#define I_SKIP7(h, ...)  I_SKIP6(__VA_ARGS__)
#define I_SKIP8(h, ...)  I_SKIP7(__VA_ARGS__)
#define I_SKIP9(h, ...)  I_SKIP8(__VA_ARGS__)
#define I_SKIP10(h, ...) I_SKIP9(__VA_ARGS__)
#define I_SKIP11(h, ...) I_SKIP10(__VA_ARGS__)
#define I_SKIP12(h, ...) I_SKIP11(__VA_ARGS__)
#define I_SKIP13(h, ...) I_SKIP12(__VA_ARGS__)
#define I_SKIP14(h, ...) I_SKIP13(__VA_ARGS__)
#define I_SKIP15(h, ...) I_SKIP14(__VA_ARGS__)
#define I_TAKE0(...)
#define I_TAKE1(h, ...)  h
#define I_TAKE2(h, ...)  h, I_TAKE1(__VA_ARGS__)
#define I_TAKE3(h, ...)  h, I_TAKE2(__VA_ARGS__)
#define I_TAKE4(h, ...)  h, I_TAKE3(__VA_ARGS__)
#define I_TAKE5(h, ...)  h, I_TAKE4(__VA_ARGS__)
#define I_TAKE6(h, ...)  h, I_TAKE5(__VA_ARGS__)
#define I_TAKE7(h, ...)  h, I_TAKE6(__VA_ARGS__)
#define I_TAKE8(h, ...)  h, I_TAKE7(__VA_ARGS__)
#define I_TAKE9(h, ...)  h, I_TAKE8(__VA_ARGS__)
#define I_TAKE10(h, ...) h, I_TAKE9(__VA_ARGS__)
#define I_TAKE11(h, ...) h, I_TAKE10(__VA_ARGS__)
#define I_TAKE12(h, ...) h, I_TAKE11(__VA_ARGS__)
#define I_TAKE13(h, ...) h, I_TAKE12(__VA_ARGS__)
#define I_TAKE14(h, ...) h, I_TAKE13(__VA_ARGS__)
#define I_TAKE15(h, ...) h, I_TAKE14(__VA_ARGS__)

#define I_A(type, n)    type arg##n; 
#define I_B1(type, n)   type arg##n,
#define I_B2(type, n)   type arg##n
#define I_C(type, n)      f->arg##n = arg##n;  
#define I_D(type, n)  , type arg##n
#define I_E1(type, n)     f->arg##n, 
#define I_E2(type, n)     f->arg##n
#define I_F(type, n)       , arg##n
#define I_G1(type, n)        arg##n, 
#define I_G2(type, n)        arg##n
#define I_H0(n)
#define I_H1(n)  I_I##n
#define I_H2(n)  I_I##n
#define I_H3(n)  I_I##n
#define I_H4(n)  I_I##n
#define I_H5(n)  I_I##n
#define I_H6(n)  I_I##n
#define I_H7(n)  I_I##n
#define I_H8(n)  I_I##n
#define I_H9(n)  I_I##n
#define I_H10(n) I_I##n
#define I_H11(n) I_I##n
#define I_H12(n) I_I##n
#define I_H13(n) I_I##n
#define I_H14(n) I_I##n
#define I_H15(n) I_I##n
#define I_I0
#define I_I1  ,
#define I_I2  ,
#define I_I3  ,
#define I_I4  ,
#define I_I5  ,
#define I_I6  ,
#define I_I7  ,
#define I_I8  ,
#define I_I9  ,
#define I_I10 ,
#define I_I11 ,
#define I_I12 ,
#define I_I13 ,
#define I_I14 ,
#define I_I15 ,
#define I_J0(n) I_K##n
#define I_J1(n)
#define I_J2(n)
#define I_J3(n)
#define I_J4(n)
#define I_J5(n)
#define I_J6(n)
#define I_J7(n)
#define I_J8(n)
#define I_J9(n)
#define I_J10(n)
#define I_J11(n)
#define I_J12(n)
#define I_J13(n)
#define I_J14(n)
#define I_J15(n)
#define I_K0 void
#define I_K1
#define I_K2
#define I_K3
#define I_K4
#define I_K5
#define I_K6
#define I_K7
#define I_K8
#define I_K9
#define I_K10
#define I_K11
#define I_K12
#define I_K13
#define I_K14
#define I_K15
    
typedef void (*I_void_fp)(void);
typedef struct I_functor        *I_functor;
typedef struct I_bound_functor  *I_bound_functor;
typedef struct I_functor_chain  *I_functor_chain;

/* assuming 32-bit pointers, we could save 8 bytes per-object, by moving the 
 * function pointers into static structures, at the cost of an extra pointer 
 * dereference per functor invocation */

struct I_functor
{   I_void_fp execute;
    void      (*free) (I_functor);
    I_functor (*clone)(I_functor);
    size_t size;
};

struct I_bound_functor
{   I_void_fp execute;
    I_void_fp free;
    I_void_fp clone;
    size_t size;
    I_functor f;
};

struct I_functor_chain
{   I_void_fp execute;
    I_void_fp free;
    I_void_fp clone;
    I_functor f1;
    I_functor f2;
};

I_functor       I_clone_functor       (I_functor);
I_functor_chain I_clone_functor_chain (I_functor_chain);
I_bound_functor I_clone_bound_functor (I_bound_functor);
void            I_free_functor_chain  (I_functor_chain);
void            I_free_bound_functor  (I_bound_functor);

#endif /* _FUNCTOR_H_ */
#include <stdio.h>
#include "functor.h"

/***** FUNCTORS *************************************************************/

/* functor prototype: void Alpha (int, int); */
#define ALPHA void, Alpha, 2, int, int
LOC_FUNCTOR_TYPE(ALPHA);

/* functor prototype: void Beta (void); */
#define BETA  void, Beta, 0
LOC_FUNCTOR_TYPE(BETA);

/***** FUNCTION PROTOTYPES **************************************************/

void Foo (int, int, int);
void Bar (int, int);

/* Declare a function that creates an Alpha functor by binding the first 
 * argument of Foo(). */
LOC_FUNCTOR_FROM_FUNCTION(ALPHA, void, Foo, 1, int);

/* Declare a function that creates a Beta functor by binding both arguments of
 * an Alpha functor */
LOC_FUNCTOR_FROM_FUNCTOR(BETA, ALPHA);

/***** FUNCTION DEFINITIONS *************************************************/

void Foo (int x, int y, int z) { printf("Foo(%d %d %d)\n", x, y, z); }
void Bar (int x, int y)        { printf("Bar(%d %d)\n",    x, y);    }

int main (void)
{
    /* create an Alpha functor from the function Bar(). */
    Alpha a1 = new_Alpha(&Bar);

    /* Create an Alpha functor by binding the first argument of Foo(). */
    Alpha a2 = new_Alpha_from_Foo(1);

    /* Create an Alpha functor by chaining togeather two Alpha functors. */
    Alpha a3 = chain_Alpha(a1, a2);

    /* Create a Beta functor by binding an Alpha functor with its first two 
     * arguments */
    Beta  b1 = new_Beta_from_Alpha(a3, 2, 3);
    
    /* Create a Beta functor by chaining a functor with itself. */
    Beta  b2 = chain_Beta(b1, b1);
    
    /* Execute a bunch of functors. */
    EXECUTE_FUNCTOR(a1, 4, 5);
    EXECUTE_FUNCTOR(a2, 6, 7);
    EXECUTE_FUNCTOR(a3, 8, 9);
    EXECUTE_FUNCTOR(b2);

    /* Cleanup. */
    FREE_FUNCTOR(a1);
    FREE_FUNCTOR(a2); 
    FREE_FUNCTOR(a3);
    FREE_FUNCTOR(b1);
    FREE_FUNCTOR(b2);

    return 0;
}