2

Here I explain the problem. Suppose I have a struct A loaded in shared memory. The struct A might be something like this:

typedef enum{
    GPIO_1,
    GPIO_2,
    GPIO_3,
    GPIO_4,
    GPIO_5,
    N_GPIO
} gpio;

struct B {
    bool active;
    bool status;
    bool direction;
    int timestamp;
    int t1;
    int t2;
};

struct A {
    struct B gpio[N_GPIO];
};

Also suppose I have two functions which would operate on one of the B structs in A:

bool set_status(gpio g, bool status, int t1, int t2);
bool activate(gpio g, bool active);

Since A is loaded in shared memory, I need to call shmget and shmdt within the two functions above; the pseudocode for the first function should be something like this:

bool set_status(gpio g, bool status, int t1, int t2) {
    struct A shm = shmget();
    struct B target = shm.gpio[g];
    if(target.active) {
        bool res = foo1(target, status, t1, t2); //do something on struct B indexed by g
        shmdt();
        return res;
    else 
        return false;
}

The pseudocode for the second function should be something like this:

bool activate(gpio g, bool active) {
    struct A shm = shmget();
    struct B target = shm.gpio[g];
    if(target.active) {
        bool res = foo2(target, active); //do something on struct B indexed by g
        shmdt();
        return res;
    else
        return false;
}

Now, is there a way I can prevent to have that common code which manages shm and checks if B.active is set? To me, this looks something like decorators, i.e. have a function that manages shm, checks for B.active and calls a second function within it, but the problem is that that the second function might has not a unique signature (might have different number of parameters).

I would like to have something like this:

bool set_status(gpio g, bool status, int t1, int t2) {
    return decorator(foo1, g, status, t1, t2); //do something on struct B indexed by g
}

bool activate(gpio g, bool active) {
    return decorator(foo2, g, active); //do something on struct B indexed by g
}

in such a way that decorator manages shm and checks for target B.active .

Thanks!

EDIT: here is a minimum working example that you can refactor https://github.com/oliviera9/c_decorator

alain
  • 165
  • 8
  • 2
    One problem is of course that decorators are a super-high level concept, and not at all available in C. – unwind Sep 26 '18 at 08:40
  • 2
    Rather than providing pseudo-code, it would be better to provided some representative actual code. Simplifying or refactoring pseudo-code, particularly if you have left out relevant information about what the code does (e.g. do the functions being called have observable side effects that aren't obvious given only the return type and argument list?) doesn't tend to be a useful guide for factoring actual code. As described, short of macros, there is no obvious solution. And I would recommend against macros for this use case. – Peter Sep 26 '18 at 08:45
  • I provided a minimum working example as requested. – alain Sep 26 '18 at 09:42

2 Answers2

2

You could create a variadic macro:

#define DECORATOR(fn, g, ...) \
    struct A shm = shmget(); \
    struct B target = shm.gpio[(g)]; \
    if(target.active) { \
        bool res = (fn)(__VA_ARGS__)\
        shmdt(); \
        return res; \
    } else {\
        return false; \
    }

And use it like this

bool set_status(gpio g, bool status, int t1, int t2) {
    DECORATOR(foo1, g, status, t1, t2)
}

bool activate(gpio g, bool active) {
    DECORATOR(foo2, g, active)
}
Burdui
  • 1,242
  • 2
  • 10
  • 24
  • what if I have a foo which takes no arguments? – alain Sep 26 '18 at 15:25
  • @alain If foo takes no argument simply don't provide more than two elements: DECORATOR(foo, g) – Burdui Sep 26 '18 at 17:52
  • I found a solution to a problem I had with empty \_\_VA_ARGS\_\_ and you can find it here: https://stackoverflow.com/questions/5891221/variadic-macros-with-zero-arguments. Simply add ## before \_\_VA_ARGS\_\_ (https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html) – alain Sep 27 '18 at 06:54
1

How much hacking are you allowed to do? Are you allowed to change foo1 and foo2? Because you could do something with function pointers and pointer casting:

First off, your decorator-function would accept the function pointer, so foo1 and foo2 need the same function signature:

bool foo1(struct B t, void* args);
bool foo2(struct B t, void* args);

In order to pass the arguments to these two functions, I would suggest some structs:

typedef struct {
  bool status;
  int t1;
  int t2;
} foo1_args;

typedef struct {
  bool active;
} foo2_args;

And then cast these arguments inside of foo1 and foo2:

bool foo1(struct B t, void* args) {
  foo1_args* my_args = (foo1_args*)args;
  bool status = my_args->status;
  // ...
}

bool foo2(struct B t, void* args) {
  foo2_args* my_args = (foo2_args*)args;
  bool active = my_args->active;
  // ...
}

Next comes your decorator function, which does the common stuff, and calls the function pointer with the specified arguments:

bool decorator(gpio g, bool (*func)(struct B, void*), void* args) {
  struct A shm = shmget();
  struct B target = shm.gpio[g];
  if (target.active) {
    bool res = func(target, args);  // do something on struct B indexed by g
    shmdt();
    return res;
  } else
    return false;
}

And finally your activate or set_status functions:

bool set_status(gpio g, bool status, int t1, int t2) {
  foo1_args args = {status, t1, t2};
  return decorator(g, foo1, &args);
}
Mike van Dyke
  • 2,724
  • 3
  • 16
  • 31
  • I am absolutely free to modify foo1 and foo2, thus your answer is good to me. As you said (First off, your decorator-function would accept the function pointer, so foo1 and foo2 need the same function signature), I do not see other approaches to this problem. Thanks – alain Sep 26 '18 at 09:49
  • @alain Neither do I. C is a little ugly in such cases. Maybe switch to C++? :D – Mike van Dyke Sep 26 '18 at 09:57
  • I can't unfortunately, since I am developing on an existing platform... – alain Sep 26 '18 at 09:59