pablo1977 pablo1977 - 1 year ago 55
C Question

Well formed pairings in function call

This is a question about norms in standard C11, concerning to side effects when function arguments are evaluated in an expression.

I am trying to define a macro in standard C that emulate the "method"-like syntax of an OOP language, in a rudimentary way.

I have designed a solution, whose main ideas I will expose here, but I have some doubts about its conformance to C11.

I need to do the exposition first and at the end I will make the specific question, which is related to evaluation of expressions involving function calls. Sorry by the long post.

So, given a

, or well a
struct *
, I would be happy if I could do a "method" call, in this way:


The typical manner in that this problem is solved is something like that:

  • Define the "class" by means of a

    typedef struct foo_s { void foo_method(struct foo_s * this); } *foo_class;
    foo_class x = malloc(sizeof(struct foo_s));
    x->foo_method = some_function_defined_over_there___;

  • Then make the call by repeating the object in the ``this'' parameter:


One can try to define some kind of "method-call" macro:

#define call(X, M) ((X)->M(X))

However this approach is bad, since the evaluation of
can duplicate side-effects (this is the well-known pitfail of repeating a macro parameter twice).

[By using tricky macros I can handle the case of an arbitrary number of parameters for the method
, for example, by using
and a few of intermediate macro hacks.]

To solve the problem of repetition of macro arguments, I decided to implement a global stack, maybe hidden as a static array in a function:

(void*) my_stack(void* x, char* operation)
static void* stack[100] = { NULL, };
// 'operation' selects "push" or "pop" operations on the stack.
// ...
// IF (operation == "push") then 'x' itself is returned again.

So now, I avoid the duplication of side effects in the macro, by writtin `X' only once:

#define call(X, M) (((foo_class)my_stack((X), "push")) -> M (my_stack(0,"pop")))

As you can see, my intent is that the function-like macro be considered by the C-compiler as a expression, whose value is the value returned by the method

I have written only once the paramater
inside the macro-body, its value was stored in the stack. Since one needs this value be able to access the "method" member of
itself, this is the reason why the function
returns the value of
: I need to reuse it immediately as part of the same expression that has pushed the value
in the stack.

Although this idea seems to easily solve the problem of duplication of
in the
macro, they appear more issues.

  • One can have "methods" whose arguments are also objects stored in the stack by using the same macro

  • Even more, we could have arguments in the "method" whose values are obtained as the result of evaluating other "methods".

  • Finally, other functions or methods appearing as arguments of a given "method" could modify the stack, because they are, probably, functions modifying the stack by using the

I want that my macro be consistent in all that cases. For example, let us suppose that
, are

On the other hand, let us suppose that we have, in
, the following "method" member:

int (*meth)(foo_class this, int, int);

Finally, we could make the "method" call:

call(x1, meth, (call (x2, 2, 2), call(x3, 3, 3)) ) ;

[The real syntax for the macro is not necessarilly as it's been showing there. I expect that the main idea is understood.]

The intent is to emulate this function call:

x1->meth(x1, x2->meth(x2,2,2), x3->meth(x3,3,3));

The problem here is that I'm using a stack to emulate the following duplications of objects in the call:

For example:
((foo_class)(my_stack(x2,"push"))) -> meth (my_stack(0,"pop"), ...)

MY QUESTION IS: Can I be always sure the paring "push"/"pop" in any possible expression (that consistently use the
macro) gives all the time the expected pair of objects?

For example, if I am "pushing" x2, it would be completely wrong that the x3 be "popped".

MY CONJECTURE IS: The answer would be YES, but after a deep analysis of the standard document ISO C11 around the topic of sequence points.

  • There is a sequence point between the expression that produces the "method" (actually, the "function") to be called, and the expressions of the arguments to be passed to it.
    Thus, for example,
    is stored in the stack before the
    method is considered to be called.

  • There is a sequence point after the evaluation of all the arguments passed to the function and before the actual function call.

    Thus, for example, if new objects
    x4, x5
    , etc. are "pushed"/"popped" in the stack while the call to
    happens, these objects
    will appear and disappear in the stack after
    x2, x3
    , have already gone from the stack.

  • There are not any sequence point between arguments in a function call.

    Thus, the following expressions could interleave when they are been evaluated (when they are argurments of the function call shown above, involving

    my_stack(x2,"push") -> meth(my_stack(0,"pop"),2,2)
    my_stack(x3,"push") -> meth(my_stack(0,"pop"),3,3)

    It could happen that after the objects
    be "pushed" in the stack, the "pop" operations could happen ill paired:
    could be "popped" in the
    line, and
    could be "popped" in the
    line, against the desired.

    This situationg is completely unlikely, and it seems that under Standard C99 there is not formal solution.

However, in C11 we have the concept of
inderminately sequenced
side effects.

We have that:

  • When a function is called, all its side effects are resolved indeterminaly sequenced respect any other expression around the expression that makes the function call. [See paragraph (12) here: sequence points].

  • Since the side effects in the function call of
    involved in the expression:

    my_stack(x2,"push") -> meth(my_stack(0,"pop"),2,2)

    have to resolved "completely before" or "completely after" the side effects in:

    my_stack(x3,"push") -> meth(my_stack(0,"pop"),3,3)

    I conclude that the "push" and "pop" operations are well paired.

Is it OK my interpretation of the standard? I will cite it, just in case:

[C11,] There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.94)

[Footnote 94]: In other words, function executions do not ‘‘interleave’’ with each other.

That is, although the order of evaluation of arguments in a function call cannot be predicted, I think that one can, anyway, be sure that the rules of "sequence" stablished in ISO C11 are enough to ensure that the "push" and "pop" operations work well in this case.

So, a "method"-like syntax can be used in C, in order to emulate a rudimentary but consistent OOP capability of "methods as members of objects".

Answer Source

No, I don't think you can be guaranteed that this does what you want.Let us decompose your expression

my_stack(x2,"push") -> meth(my_stack(0,"pop"),2,2)
<<<<<< A >>>>>>>>>>         <<<<<<< B >>>>>>
<<<<<<<<<<<<< C >>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<< D >>>>>>>>>>>>>>>>>>>>>>>>

The evaluations of B and C are completely independent and must both be done before the function call D. The arguments of a function and the function designator are not much different for that.

Because A and B are function calls, they are in fact sequenced, but this is indeterminely, so you don't know which one comes first and which second.

I think you would be far better off by making your call an inline function. If you really need versions of call for different types, you could go to select the function with a _Generic expression. But as someone already said in the comments, you are really at the limits of what you should reasonably do in C.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download