9

Is there a way in C and C++ to cause functions returning void to be evaluated in unspecified order?

I know that function arguments are evaluated in unspecified order so for functions not returning void this can be used to evaluate those functions in unspecified order:

#include <stdio.h>

int hi(void) {
    puts("hi");
    return 0;
}

int bye(void) {
    puts("bye");
    return 0;
}

int moo(void) {
    puts("moo");
    return 0;
}

void dummy(int a, int b, int c) {}

int main(void) {
    dummy(hi(), bye(), moo());
}

Legal C and C++ code compiled by a conforming compiler may print hi, bye, and moo in any order. This is not undefined behavior (nasal demons would not be valid), there is simply more than one but less than infinite valid outputs and a compliant compiler need not even be deterministic in what it produces.

Is there any way to do this without the dummy return values?

Clarification: This is an abstract question about C and C++. A better original phrasing might have been is there any context in which function evaluation order is unspecified for functions returning void? I'm not trying to solve a specific problem.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Praxeolitic
  • 22,455
  • 16
  • 75
  • 126
  • If the methods are `void` why would you be using them as a parameter? – John3136 Dec 03 '14 at 04:40
  • My goal is not to use them as a parameter but to evaluate them in unspecified order. – Praxeolitic Dec 03 '14 at 04:40
  • @Gyapti The use case is curiosity. – Praxeolitic Dec 03 '14 at 04:41
  • 1
    @Praxeolitic Use `rand()`. Or just a comma operator, even the left to right order is one sub-case of unspecidified order. – Gyapti Jain Dec 03 '14 at 04:42
  • rand() is deterministic. I want the actual compiler output to be nondeterministic as per the standards. – Praxeolitic Dec 03 '14 at 04:43
  • 2
    The standards say it's *unspecified*: in practice, an actual compiler will always end up picking the same order, but exactly which order will vary depending on circumstances, compiler flags, etc. – Rufflewind Dec 03 '14 at 04:45
  • 1
    Unspecified behaviors are also deterministic for a specific compiler. – Gyapti Jain Dec 03 '14 at 04:45
  • 3
    The abstract machine described by the standards is nondeterministic. It is not impossible to build a nondeterministic compiler and it would be standards compliant. It's irrelevant that most compilers are deterministic. This is an abstract question about the languages. – Praxeolitic Dec 03 '14 at 04:48
  • @Gyapti Could you explain about comma operator? Isn't the order specified? Left to right like you say. – Praxeolitic Dec 03 '14 at 04:52
  • @Praxeolitic My point is, even if the order of evaluation is unspecified, it is possible that the set of compilers you are working with, all of them pick the same order. – Gyapti Jain Dec 03 '14 at 04:55
  • 2
    Perhaps using the same model and wrapping the functions in a pthread_create. – technosaurus Dec 03 '14 at 05:00
  • the comma operator is part of the Precedence of C operators, it is evaluated 'left to right', so there would be no question as to which function would invoked first, second, third – user3629249 Dec 03 '14 at 05:01
  • 1
    Just found a very concrete application for your question: forcing a C interpreter to execute different, but defined, behaviors. My interpreter does not model non-deterministic functions, but using void() functions I can more easily create stubs to emulate them. – anol Jun 04 '15 at 08:49

2 Answers2

11

You can take advantage of the fact that the left hand side of a the comma operator is a discarded value expression (void expression in C) like this (see it live):

int main(void) {
    dummy((hi(),0), (bye(),0), (moo(),0));
}

From the draft C++ standard section 5.18 Comma operator:

A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded-value expression (Clause 5).

and C11 section 6.5.17 Comma operator:

The left operand of a comma operator is evaluated as a void expression; there is a sequence point between its evaluation and that of the right operand. Then the right operand is evaluated; the result has its type and value.

As Matt points out is is also possible to mix the above method with arithmetic operators to achieve unspecified order of evaluation:

(hi(),0) + (bye(),0) + (moo(),0) ;
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I enjoyed this answer. Some of the more interesting things I've learned on SO have stemmed from controversial questions like this that seem silly to some. – Praxeolitic Dec 03 '14 at 05:06
  • 2
    You don't need `dummy` at all, since order of evaluation of other operators is unspecified too; e.g. `(hi(), 0) + (bye(), 0) + (moo(), 0);` – M.M Dec 03 '14 at 05:53
  • 1
    @Praxeolitic I don't think it is a silly question, usually when it comes to side effects people want to [avoid unspecified order of evaluation like the plague](http://stackoverflow.com/q/27158812/1708801) because it usually results in bad things. So this question definitely requires a mental leap. – Shafik Yaghmour Dec 03 '14 at 15:31
-1

Well there's always the obvious approach of putting pointers to the functions in a container, shuffling it up (or as suggested in a comment sorting it), and calling each item in the container. If you need to have the same behavior each run just make sure your seed is the same each time.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • @Mohit Jain Can you elaborate on how a random order generated by the same seed is different from unspecified? In either case you can easily determine the order after a single run of the program. – Mark B Dec 03 '14 at 05:02
  • 2
    Sorting the container would also work, since the ordering of function addresses is unspecified. – Benjamin Lindley Dec 03 '14 at 05:09
  • @BenjaminLindley Nice reversal! That idea might make this answer viable but does it avoid incurring UBD? Is function pointer comparison legal? – Praxeolitic Dec 03 '14 at 05:36
  • 2
    function pointer comparison is not part of the standard AFAIK (nor is casting a function pointer to an integral type), compiler may have extension – M.M Dec 03 '14 at 05:48