-3

I have multiple locations in my code where I want to be able to jump to one specific location and return to where I was before.

A function calls provides that control flow but is not an option for me as I want the code I branch to to access a number of variables and passing all of them as arguments to the function call wouldn't be practical or efficient.

And the goto statement is only built to take a label, i.e. expected to be a one-way ticket.

Currently I am achieving what I need with the following:

void *return_addr;
int x,y;
...
return_addr=&&RETURN_0;
goto SOMEWHERE;
RETURN_0:
...
x+=1;
...
return_addr=&&RETURN_1;
goto SOMEWHERE;
RETURN_1:
...


SOMEWHERE:
y=x;
...
goto *return_addr;

Is there something more elegant and less cumbersome?

CubeJockey
  • 2,209
  • 8
  • 24
  • 31
Lolo
  • 3,935
  • 5
  • 40
  • 50
  • 4
    Isn't that what calling functions is all about? If you want to use `goto` so much you should consider old 1970's BASIC. – Some programmer dude Dec 30 '15 at 16:55
  • 4
    Why are you basically writing assembly in C? **Use functions.** If you have to access a lot of global state, put the relevant pieces in a structure, and pass a pointer to it to the functions. If you mark the functions `static`, they may well be inlined, and effectively achieve what you're trying to do; but with the added benefit of not being a freaking unmaintainable disaster. – Jonathon Reinhart Dec 30 '15 at 16:57
  • @JoachimPileborg See what I said about function calls in my original post. Think of my "SOMEWHERE" code as a place when I work on not just y but many other variables. Passing each of them as arguments would be very inefficient. – Lolo Dec 30 '15 at 16:57
  • 2
    Just make all your variables global, so that you can access them from anywhere. I mean, this design is already terrible. Can't make it much worse. – void_ptr Dec 30 '15 at 16:57
  • 3
    You do know about structures? One structure to collect all related variables, and then just pass the structure (or a pointer to it). – Some programmer dude Dec 30 '15 at 16:58
  • Which architecture? If x86, stop worrying about argument-passing inefficiencies. – cadaniluk Dec 30 '15 at 16:58
  • Yes, this is very close to assembly as I am writing code for an embedded platform. I am staying close to low-level instructions available on my target architecture using intrinsics but going all the way to assembly would be overkill. Function calls with many arguments or a struct for all my variables would both be too inefficient. And using globals, well, that's quite a step further down in the "terrible design" route... – Lolo Dec 30 '15 at 17:02
  • And what's your gripe with structs? – mittmemo Dec 30 '15 at 17:02
  • The gripe with structs is that accessing each variable in my structs is one more load. This quickly becomes too costly for the app I am writing where each cycle counts. – Lolo Dec 30 '15 at 17:04
  • A slightly less horrid approach might be to use macro's and an enum field in a loop. That would allow you to _"simulate"_ the jumping back and forth without using functions. While I know you're on an embedded platform, the occasional function isn't always a bad thing. Perhaps consider using a struct instead of loose variables, and pass a single pointer as argument instead? – Elias Van Ootegem Dec 30 '15 at 17:04
  • Yes, macro is yet another option. That one I can't do here because the SOMEWHERE code I jump to is pretty large and I need to jump to it from many places. Instruction cache misses start being an issue. – Lolo Dec 30 '15 at 17:06
  • 1
    Would love to hear why this question is down-voted by the way. The coding practice for high-level code and low-level embedded code, both of which I write regularly, are just not always the same once trying to write code that runs as efficiently as possible. – Lolo Dec 30 '15 at 17:12
  • "Passing each of them as arguments would be very inefficient". Measured much? – n. m. could be an AI Dec 30 '15 at 17:14
  • 1
    To get forward: **What's your CPU?** – dlask Dec 30 '15 at 17:21
  • 1
    @Lolo: Even on embedded platforms there is no need to write such code. Spaghetti have to be on a plate with a good sauce, but not in source code. Others already gave you enough information how to write correct code (`static` functions, global variables). If your platform is **that** small and you only have a crappy compiler, keep the C expressions simple. Otherwise use Assembler directly; that would still be better readable than what you want to accomplish. Because Assembler is designed for this, C not. Condolences to whoever has to read such code. – too honest for this site Dec 30 '15 at 17:25
  • 2
    @dlask: In another comment he mentions a cache, so the CPU cannot be **that** small. I think OP either has an XY-problem or has no experience with "modern" (i.e. since 2000) compilers. Also that sounds like premature optimisation, as he does not provide any proof clean code will be too slow. – too honest for this site Dec 30 '15 at 17:33
  • The target processor is a proprietary DSP. The compiler is everything but crappy and staying in C with intrinsics rather than assembly is immensely preferable. Again, I _do_ agree that every single alternate approach that has been suggested is cleaner; it just ends up less efficient in the end. The only approach just as efficient as my current approach (efficiency wise) is to use globals and I am not clear why this is a better coding approach. – Lolo Dec 30 '15 at 17:35
  • You can have your variables collected in a global structure. In this case you have a single global name but the access time to individual variables stays unchanged because their addresses can be resolved in the compile time. Moreover, it's meaningless to simulate function calls when standard function calls can be used directly. – dlask Dec 30 '15 at 17:38
  • "Would love to hear why this question is down-voted by the way" -- NMDV, but I speculate that part of the reason is that you seem not to want an answer, at least not to the question you have actually posed. What you seem really to be trying to do is to justify your rather horrible code. Moreover, to do so you are making questionable assertions and adding extra conditions as you go. – John Bollinger Dec 30 '15 at 18:27
  • If indeed there is no "fast enough" for your program, so that truly every cycle counts, then you probably want to be hacking the critical parts in assembly. Or you want a skilled assembly programmer for the target architecture hacking them, anyway. – John Bollinger Dec 30 '15 at 18:34
  • @JohnBollinger Thanks for the insight. I see how this can be perceived as my trying to get my ugly solution endorsed, which really isn't my intent. Perhaps clearer would have been to ask simply: "Is there a way in C to express what many assembly languages support with the concept of a call and return statements." The answer to that is: "No, other than a function call with no argument, which doesn't prevent variables to be accessible unless they are global" – Lolo Dec 30 '15 at 18:36
  • @dlask Thanks. One global name only for the struct is clean. Though the compiler will figure out the addresses for accessing the struct elements, these will still turn into load and store to that struct as opposed to keeping content in registers. I agree that function calls make the code much more readable: I am not arguing this point. – Lolo Dec 30 '15 at 18:41
  • How many variables are there? How many registers? – dlask Dec 30 '15 at 18:54
  • Around 24 general purpose registers. The time critical code I work on fills most of these registers with the variables and temp calculations needed in the inner loop. Looking at all these comments again, I realize that the "cold shower" I got on my question comes in part from the fact that my question is narrowly focused on discussing the usage of C for very time-critical code, self contained code where I am staying as close to assembly as I can while leveraging the compiler for instruction scheduling and register allocation. – Lolo Dec 30 '15 at 23:18

4 Answers4

1

Is there something more elegant and less cumbersome?

You are obviously using GCC, as the computed goto statement is a GCC extension. With GCC we can use a nested function and access local variables without needing to pass them as arguments:

{
    int x, y;

    void SOMEWHERE()
    {
        y = x;
        //...
    }

    //...
    SOMEWHERE();
    //...
    x += 1;
    //...
    SOMEWHERE();
    //...
}
Armali
  • 18,255
  • 14
  • 57
  • 171
  • 1
    Wow. I had forgotten about this question that I got hammered for. Though I am no longer in need for this, I believe your suggested approach is exactly what I was after. It implements the flow I described and doesn't require to make variables global. – Lolo Feb 12 '18 at 23:00
0

Let's have the variables collected in a structure:

struct data_t {
    int a;
    int b;
    /* and so on */
    int x;
    int y;
};

Let's have the repeated code defined in a function:

void func(struct data_t* data) {
    data->y = data->x;
    /* and so on */
}

Let's have the function used:

struct data_t data = {1, 2, ..., 24, 25};
func(&data);
data.x += 1;
func(&data);
/* and so on */
dlask
  • 8,776
  • 1
  • 26
  • 30
  • I agree it is a cleaner approach coding wise. I am staying away from this approach as each variable access is another memory load and/or store on a system where memory bandwidth is already the bottleneck. – Lolo Dec 30 '15 at 17:09
  • Not clear. You have to access your variables anyway. If you make your structure global, for example, and access the variables directly from the called function there is no difference in the variable access time. In other words, why don't you use a function? – dlask Dec 30 '15 at 17:15
  • 1
    @Lolo: you seem determined not to use structs, and dismiss them as less efficient. In theory they are, but a lot of compilers have found clever ways to optimize struct member access in such a way that they perform (almost) identical to variables. Have you looked into how your compiler might help you in this case (ie what optimization options you have: eg inline functions and the equivalent of `-fomit-frame-pointer` for your compiler) – Elias Van Ootegem Dec 30 '15 at 17:18
  • 1
    @EliasVanOotegem Yes, I do look at the disassembly generated by the compiler directly to see how close the assembly gets from what I need. Inlining function isn't something I want here for the same reason that macros won't work for me either: larger code with cache instruction misses. And structs do get translated in memory load and store for each access, something that doesn't add up to what I need in my end. I use regularly every single approach that has been suggested so far here (structs, macros, inlining). This is just a place where none of these meet my need. – Lolo Dec 30 '15 at 17:28
  • @Lolo: Just covering the basics, I'm not saying you didn't look at the disassembly, just trying some rubber-ducking :). That being said, I'm not sure if we'll be able to help in any way. I'm beginning to think you're sort of looking for somebody to say that the code you're writing is acceptable. Maybe in your very specific case it is, but I'm sure you'll understand why it's making most of us cringe :) – Elias Van Ootegem Dec 30 '15 at 17:37
  • @EliasVanOotegem No, the code is not acceptable. ;-) – dlask Dec 30 '15 at 17:40
0

C has setjmp() / longjmp(), which can support what you describe. Do not use them. Even more, however, do not rely on your current approach, which is not standard C, and which is terribly poor form.

What you describe is what functions are for. If you have a lot of data that you must share between the caller and callee, then either

  • record them in file-scope variables so that both functions can access them directly, or
  • create one or more complex data types (presumably structs) with which to hold and organize the data, and give the callee access by passing a pointer to such a struct.
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

A state machine can be written like this:

typedef enum { start, stop, state1, ... } state;

state s = start;
while (s != stop) {
 switch (s) {
 case start:
   do_stuff; // lots of code
   // computed goto
   s = cond ? state23 : state45;
   break;
   ...

Need a call stack?

 state stack[42]; int sp=0;
 ...
    do_stuff;
    stack[sp++] = state33;
    s = state45; // call
    break;
  case state33:

  case state45:
    do_processing; // some code
    s = stack[--sp]; // ret
    break;

You should only do this after you benchmark your time-critical code sections and find that the normal function call mechanism is indeed the bottleneck.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243