Functional programming in C: Implementation

-

Cleanup

Let’s start sim­ple with the cleanup func­tion. First we need the usual bar­rage of in­cludes. G_BEGIN_DECLS al­lows the header to be linked in C++.

#ifndef L_UTILS_INCLUDED
#define L_UTILS_INCLUDED

#include "glib.h"

G_BEGIN_DECLS

#include <stdlib.h>
#include <stdio.h>
This feature is GCC specific. It uses __attribute((cleanup(f))) where f is the cleanup function. In this case the cleanup function just frees the memory.

#ifdef __GNUC__

 static inline void __autofree(void *p) {
     void **_p = (void**)p;
     free(*_p);
 }

au­to_­clean is a build­ing block that you can use to plug in your own cleanup func­tion. In the com­mon case of mem­ory al­lo­ca­tion, I cre­ated a wrap­per macro au­to_free to make it even eas­ier.

#define auto_clean(f)   __attribute((cleanup(f)))
#define auto_free       auto_clean(__autofree)

Lambdas

I took this one from here.

If you think about it, a lambda is just an ex­pres­sion that re­turns a func­tion. This macro cre­ates a nested func­tion, called fn, in­side a state­ment ex­pres­sion and re­turns it. Unfortunately these fea­tures are gcc spe­cific.

Remember that lamb­das are not al­lo­cated on the heap, so you have to be care­ful on how you used them.

#define lambda(return_type, function_body)                                          
  ({                                                                                
    return_type __fn__ function_body                                                
    __fn__;                                                                         
  })

#endif

Unions

A union type is what you would ex­pect: a struct that con­tains an un­named union and a field to spec­ify which type it is. We need the list of types in union_decl to cre­ate the kind enum. The us­age of VA_ARGS al­lows to use what­ever syn­tax you want to go into the enum (i.e. spec­ify int val­ues).

Having to spec­ify the the types here is un­for­tu­nate as you are go­ing to need to spec­ify it in the union_­case macros as well.

I haven’t found an­other way to do it. If you do, let me know.

#define union_decl(alg, ...)                                                        
typedef struct alg {                                                                
    enum {  __VA_ARGS__ } kind;                                                     
    union {

You spec­ify each type for the union with union_­type. That looks pretty good to me.

#define union_type(type, ...)                                                       
    struct type { __VA_ARGS__ } type;

Ideally you should­n’t need to spec­ify alg here. Perhaps there is a way to avoid do­ing so.

#define union_end(alg)                                                              
    };} alg;

You can then set the fields on the union type by us­ing the be­low macro. Notice the us­age of the new struct con­struc­tor here to al­low op­tional named pa­ra­me­ters.

This is a state­ment, so it can­not go into an ex­pres­sion place. I think I could make it an ex­pres­sion that re­turns the ex­ist­ing (or a new) union. This is go­ing to be a scean­rio if peo­ple are not us­ing gcc state­ment ex­pres­sions.

#define union_set(instance, type, ...)                                              
    G_STMT_START {                                                                  
        (instance)->kind     = (type);                                              
        (instance)->type   = (struct type) { __VA_ARGS__ };                         
    } G_STMT_END

This is an util­ity macro. It is a ver­sion of g_as­sert that you can use in an ex­pres­sion po­si­tion.

#define g_assert_e(expr) (                                                          
    (G_LIKELY (!expr) ?                                                             
   (void)g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, 
                                             #expr)                             
    : (void) 1) )

And this al­lows to fill the de­fault case in a match state­ment im­ple­mented as a ternary op­er­a­tor. It prints out a text rep­re­sen­ta­tion of the ex­pres­sion and re­turns it.

#define union_fail(...) (g_assert_e(((void)(__VA_ARGS__) , false)), (__VA_ARGS__))

The rest of the code is com­mented out. It is a macro way to do pat­tern match­ing. For me, the ternary op­er­a­tor is sim­pler, but I left it there in case you want to play with it.

/*
#define union_case_only_s(instance, type, ...)                                      
        G_STMT_START {                                                              
        if((instance)->kind == (type)) {                                            
            G_GNUC_UNUSED struct type* it = &((instance)->type); __VA_ARGS__; }     
        else g_assert_not_reached();                                                
        } G_STMT_END

#define union_case_first_s(alg, instance, type, ...)                                
    G_STMT_START {                                                                  
        alg* private_tmp = (instance);                                              
        if(private_tmp->kind == type) {                                             
            G_GNUC_UNUSED struct type* it = &((private_tmp)->type); __VA_ARGS__; }

#define union_case_s(type, ...)                                                     
        else if(private_tmp->kind == type) {                                        
            G_GNUC_UNUSED struct type* it = &((private_tmp)->type); __VA_ARGS__; }

#define union_case_last_s(type, ...)                                                
        else if(private_tmp->kind == type) {                                        
            G_GNUC_UNUSED struct type* it = &((private_tmp)->type); __VA_ARGS__; }  
            else g_assert_not_reached(); } G_STMT_END

#define union_case_default_s(...)                                                   
        else __VA_ARGS__; } G_STMT_END

// Need to use assert here because g_assert* cannot be used in expressions as it expands to do .. while(0)
#define union_case_only(instance, type, ...)                                        
        ( (instance)->kind == (type) ? (__VA_ARGS__) : (assert(false), __VA_ARGS__) )

#define union_case_first(instance, type, ...)                                       
        ( (instance)->kind == (type) ? (__VA_ARGS__) :

#define union_case(instance, type, ...)                                             
        (instance)->kind == (type) ? (__VA_ARGS__) :

#define union_case_last(instance, type, ...)                                        
        (instance)->kind == (type) ? (__VA_ARGS__) : (assert(false), (__VA_ARGS__)) )
#define union_case_default(...)                                                     
        (__VA_ARGS__) )

*/

G_BEGIN_DECLS

#endif // L_UTILS_INCLUDED

Tags