TL;DR:
I believe it should be written as:
int main (void)
{
int result = do_func();
printf("Failed %d\n", result);
}
Detailed explanation:
If nothing can be assumed what-so-ever about the function types, we can't easily use an array of function pointers, which would otherwise be the correct answer.
Assuming all function types are incompatible, then we would have to wrap in the original obscure design containing all those non-compatible functions, inside something else.
We should make something that is readable, maintainable, fast. We should avoid tight coupling, so that the undo behavior of "Do_x" doesn't depend on the undo behavior of "Do_y".
int main (void)
{
int result = do_func();
printf("Failed %d\n", result);
}
Where do_func
is the function doing all the calls required by the algorithm, and the printf
is the UI output, separated from the algorithm logic.
do_func
would be implemented like a wrapper function around the actual function calls, handling the outcome depending on the result:
(With gcc -O3, do_func
is inlined in the caller, so there is no overhead for having 2 separate functions)
int do_it (void)
{
if(Do1()) { return 1; };
if(Do2()) { return 2; };
if(Do3()) { return 3; };
if(Do4()) { return 4; };
if(Do5()) { return 5; };
return 0;
}
int do_func (void)
{
int result = do_it();
if(result != 0)
{
undo[result-1]();
}
return result;
}
Here the specific behavior is controlled by the array undo
, which is a wrapper around the various non-compatible functions. Which functions to to call, in which order, is all part of the specific behavior tied to each result code.
We need to tidy it all up, so that we can couple a certain behavior to a certain result code. Then when needed, we only change the code in one single place if the behavior should be changed during maintenance:
void Undo_stuff1 (void) { }
void Undo_stuff2 (void) { Undo1(); }
void Undo_stuff3 (void) { Undo2(); Undo1(); }
void Undo_stuff4 (void) { Undo3(); Undo2(); Undo1(); }
void Undo_stuff5 (void) { Undo4(); Undo3(); Undo2(); Undo1(); }
typedef void Undo_stuff_t (void);
static Undo_stuff_t* undo[5] =
{
Undo_stuff1,
Undo_stuff2,
Undo_stuff3,
Undo_stuff4,
Undo_stuff5,
};
MCVE:
#include <stdbool.h>
#include <stdio.h>
// some nonsense functions:
bool Do1 (void) { puts(__func__); return false; }
bool Do2 (void) { puts(__func__); return false; }
bool Do3 (void) { puts(__func__); return false; }
bool Do4 (void) { puts(__func__); return false; }
bool Do5 (void) { puts(__func__); return true; }
void Undo1 (void) { puts(__func__); }
void Undo2 (void) { puts(__func__); }
void Undo3 (void) { puts(__func__); }
void Undo4 (void) { puts(__func__); }
void Undo5 (void) { puts(__func__); }
// wrappers specifying undo behavior:
void Undo_stuff1 (void) { }
void Undo_stuff2 (void) { Undo1(); }
void Undo_stuff3 (void) { Undo2(); Undo1(); }
void Undo_stuff4 (void) { Undo3(); Undo2(); Undo1(); }
void Undo_stuff5 (void) { Undo4(); Undo3(); Undo2(); Undo1(); }
typedef void Undo_stuff_t (void);
static Undo_stuff_t* undo[5] =
{
Undo_stuff1,
Undo_stuff2,
Undo_stuff3,
Undo_stuff4,
Undo_stuff5,
};
int do_it (void)
{
if(Do1()) { return 1; };
if(Do2()) { return 2; };
if(Do3()) { return 3; };
if(Do4()) { return 4; };
if(Do5()) { return 5; };
return 0;
}
int do_func (void)
{
int result = do_it();
if(result != 0)
{
undo[result-1]();
}
return result;
}
int main (void)
{
int result = do_func();
printf("Failed %d\n", result);
}
Output:
Do1
Do2
Do3
Do4
Do5
Undo4
Undo3
Undo2
Undo1
Failed 5