1

When writing threaded code in C, I first have to create some struct which includes all the arguments and a wrapper function. This leads to lots of code bloat and is not easy to read. See:

struct some_function_args {
  int arg1;
  int arg2;
  int arg3;
};

void some_function_wrapper(struct some_function_args* args) {
  some_function(args->arg1, args->arg2, args->arg3);
}

int main() {
  struct my_args;
  my_args.arg1 = 1;
  my_args.arg2 = 2;
  my_args.arg3 = 3;

  pthread_create(..., some_function_wrapper, &my_args);
  pthread_join(...);
}

Is there some kind of macro or library (maybe using varargs) which automatically creates the needed structs and wrapper functions for me, like this? Or is this not possible at all in C?

int main() {
  MY_THREAD thread = IN_THREAD {
    some_function(1, 2, 3);
  }

  JOIN_THREAD(thread);
}
iblue
  • 29,609
  • 19
  • 89
  • 128
  • 1
    Note that `some_function_wrapper` should take a `void*` and explicitly cast it to `struct some_function_args*`. Otherwise it'll get called with the wrong prototype, which is undefined behaviour. – Quentin Feb 04 '15 at 21:38
  • Newer C-standards (C99) have compound initializers: `struct my_args = {.arg1 = 1, .arg2 = 2, .arg3 = 3};` – EOF Feb 04 '15 at 22:12

2 Answers2

0

EDIT: I released some code. See threadify.h.

With the following macros you can do things like this:

char a = 'A';
int  b = 23;
char[] c = "Example";


pthread_t thread;

// THREAD3 because it takes 3 arguments of variable type.
THREAD3(thread, a, b, c, {
  printf("test: %c %d %s\n", a, b, c);
});

JOIN(thread);

It's possible in GCC, because GCC has two non-standard extensions:

  • nested functions
  • The typeof() operator

There are some drawbacks in this huge mess of macros:

  • You cannot pass rvalues (because it's not possible to create pointers to them)
  • You need to use the appropriate THREAD0, THREAD1, ... macro depending on the number of arguments (not sure if it's possible to work around this using variadic macros).

    #include <stdio.h>
    #include <pthread.h>
    
    // Nested definition to work around preprocessor prescan
    #define __CAT(arg1, arg2) arg1 ## arg2
    #define CAT(arg1, arg2) __CAT(arg1, arg2)
    
    // Use the current line number to create a unique name for objects
    #define NAME(arg1) CAT(arg1, __LINE__)
    
    // Creates a thread without any arguments
    #define THREAD0(thread, code) \
    void NAME(__pthread_wrapper)(void) {\
      do {code;} while(0); \
    }; \
    pthread_create(&thread, NULL, (void*)NAME(__pthread_wrapper), NULL);
    
    // Creates a thread with one argument by creating a struct
    // and passing all values via this struct.
    #define THREAD1(thread, arg1, code) \
    typedef struct { \
      typeof(arg1)* NAME(__pthread_arg1); \
    } NAME(__pthread_struct); \
      void NAME(__pthread_wrapper)(NAME(__pthread_struct)* data) {\
      do {code;} while(0); \
    }; \
    NAME(__pthread_struct) NAME(__data); \
    NAME(__data).NAME(__pthread_arg1) = &arg1; \
    pthread_create(&thread, NULL, (void*)NAME(__pthread_wrapper), &NAME(__data));
    
    #define THREAD2(thread, arg1, arg2, code) \
    typedef struct { \
      typeof(arg1)* NAME(__pthread_arg1); \
      typeof(arg2)* NAME(__pthread_arg2); \
    } NAME(__pthread_struct); \
      void NAME(__pthread_wrapper)(NAME(__pthread_struct)* data) {\
      do {code;} while(0); \
    }; \
    NAME(__pthread_struct) NAME(__data); \
    NAME(__data).NAME(__pthread_arg1) = &arg1; \
    NAME(__data).NAME(__pthread_arg2) = &arg2; \
    pthread_create(&thread, NULL, (void*)NAME(__pthread_wrapper), &NAME(__data));
    
    #define THREAD3(thread, arg1, arg2, arg3, code) \
    typedef struct { \
      typeof(arg1)* NAME(__pthread_arg1); \
      typeof(arg2)* NAME(__pthread_arg2); \
      typeof(arg3)* NAME(__pthread_arg3); \
    } NAME(__pthread_struct); \
      void NAME(__pthread_wrapper)(NAME(__pthread_struct)* data) {\
      do {code;} while(0); \
    }; \
    NAME(__pthread_struct) NAME(__data); \
    NAME(__data).NAME(__pthread_arg1) = &arg1; \
    NAME(__data).NAME(__pthread_arg2) = &arg2; \
    NAME(__data).NAME(__pthread_arg3) = &arg3; \
    pthread_create(&thread, NULL, (void*)NAME(__pthread_wrapper), &NAME(__data));
    
    /* THREAD4, THREAD5, ... */
    
    #define JOIN(thread) pthread_join(thread, NULL);
    
iblue
  • 29,609
  • 19
  • 89
  • 128
  • 1
    Maybe you could isolate the parameters in a pair of parentheses like so : `THREAD(thread, (a, b, c), {...})` ? I'm no PP guru, but I've seen crazy stuff done in Boost and this looks basic enough. – Quentin Feb 04 '15 at 22:16
  • Then I still need to iterate through them somehow to create the struct. Maybe I could build some `#for VAR in (a,b,c)` loop using the following technique: http://stackoverflow.com/a/10542793/773690 – iblue Feb 04 '15 at 22:20
  • 1
    Boost PP will probably be of great use here. Also please have a look at [my comment above](http://stackoverflow.com/questions/28330956/how-to-avoid-code-bloat-when-using-pthreads-in-c/28332447#comment45010969_28330956) about the function pointer. This way will probably work, but better standard-compliant than sorry. – Quentin Feb 04 '15 at 22:22
-1

'When writing threaded code in C, I first have to create some struct which includes all the arguments and a wrapper function. This leads to lots of code bloat and is not easy to read'. Sure. That's what you pay for making your app, (assuming it's not stupid), either X times faster or X times easier to implement becasue it's easier described as independent functionality. You want something for nothing?

Martin James
  • 24,453
  • 3
  • 36
  • 60