0

I'm working on a framework on C language, for it I want to implement exceptions, for it I'm using longjump with setjump, but on x64 machines longjump still outputs an integer.

I've created a class (struct with vptr essentially), which represents exception, but to throw it out in code I need to throw a pointer to this structure. The pointer has an unsigned long long value (qword) for x64 machines and unsigned int (dword) for x86, so I shall require only qword to be in output to handle the error.

Are there implementations of longjmp and setjmp, which can output qword?

Or maybe I could write my own longjump, but for it the original source code is required.

  • 1
    to be portable, use a global indirection... – Jean-Baptiste Yunès Apr 15 '19 at 12:26
  • @Jean-BaptisteYunès where can I find info on this? Also, this is WinAPI framework, I'm not sure portability is needed – Ilya Pakhmutov Apr 15 '19 at 12:27
  • Most languages I know of just use a thread-local to store the pointer to the current exception. `longjmp` is then used just for control transfer. – Matteo Italia Apr 15 '19 at 12:57
  • @MatteoItalia I can't talk about threading, because I have no knowledge of how it reacts to changes. So all I need to do is just create an exception pointer as a global scope variable? – Ilya Pakhmutov Apr 15 '19 at 13:03
  • 2
    There isn’t a portable (standard) variant of `longjmp()` that returns a `long` rather than an `int`. You will need to look in your platform-specific manual for options, but I don’t think you’ll find one. Can’t you simply use another global variable? – Jonathan Leffler Apr 15 '19 at 13:03
  • @JonathanLeffler thing is, program can be multithread, so a single global variable shall just be a mess, if some threads fail. Mutex or atomic volatile is the way still, but how to implement them to each thread is a bit of a problem, but it looks possible, thx for the Idea – Ilya Pakhmutov Apr 15 '19 at 13:06
  • 3
    Unless you’re jumping around in a single function and therefore the `jmpbuf` variable is local to that function, you already have a multi-threaded mess. Whatever technique you’re using to give each thread it’s own `jmpbuf` can also be used to provide extra space for the `long` return value. – Jonathan Leffler Apr 15 '19 at 13:11
  • Are you sure you want to use C and `setjmp()` / `longjmp()`? Would you be better off with C++ and exceptions? Or MS ‘ structured exception handling’? – Jonathan Leffler Apr 15 '19 at 13:14
  • @JonathanLeffler the reason I'm not using C++ is it makes a lot of things which are hidden, I just want to know how things work instead of just using things and hoping they will not kill your computer – Ilya Pakhmutov Apr 15 '19 at 13:28
  • @IlyaPakhmutov: killing your computer is a bit extreme... the codegen of C++ is decently transparent, and the support libraries (which are needed to support exception handling) of several popular implementations are open source, so you can check what they do; indeed, the "fallback" implementation of g++ exceptions (so called "sjlj") is based on `setjmp`/`longjmp`, so you could draw inspiration from there. That being said, it's indeed always interesting to try to implement these low level bits by yourself; just be aware that this stuff often can be complicated for many reasons. – Matteo Italia Apr 15 '19 at 15:53
  • @IlyaPakhmutov to get back to the threading issue: as each thread needs to have independent exception handling, all your data structures related to it must be thread-local, including the `setjmp` buffer, as correctly noted by @JonathanLeffler; since C11 the `thread_local` keyword can be applied to a global variable to make it thread-local, otherwise, if your compiler doesn't support it, you'll have to resort to compiler- or platform-specific tricks. VC++ provides since many years `__declspec( thread )`, otherwise you'll have to use the low-level Win32 `TlsAlloc`. – Matteo Italia Apr 15 '19 at 15:57
  • @MatteoItalia Thanks for the explanation, as an example I often use this code: `{MyClass m(1,2); return 0;}` What does this do? Should I use `delete`, I don't control this stuff and the implementation is sealed. In order to study C++ I'm trying to recreate simple things from it, I'd like to have a mentor on C++, but no one wants to explain stuff they don't know exactly about. – Ilya Pakhmutov Apr 15 '19 at 16:35
  • @MatteoItalia also thread_local is c++ feature, in C __declspec(thread) is an option – Ilya Pakhmutov Apr 15 '19 at 16:50
  • @IlyaPakhmutov you don't have to use delete, it's an automatic-duration variable, exactly as in C (with the difference that the destructor is called automatically when the variable goes out of scope). There's no magic at that level, it's just a matter of studying the language. `thread_local` is both a C++11 and a C11 feature (but in C it requires including ). – Matteo Italia Apr 15 '19 at 17:01
  • @IlyaPakhmutov then if it is just for learning why support 32/64 modes? Just try something into a suitable mode for your tests, then learn from the sources how exceptions are handled in different languages. – Jean-Baptiste Yunès Apr 15 '19 at 17:04
  • @IlyaPakhmutov use some global structure and encode things into a single int (your own thread id, global register number, etc) to be used as index in the table. – Jean-Baptiste Yunès Apr 15 '19 at 17:06
  • @MatteoItalia thread.h is not presented in MSVC 2015 – Ilya Pakhmutov Apr 15 '19 at 17:20
  • @Jean-BaptisteYunès x64 is a hard mode, if I could use something in 64 mode then x32 is easier (and also sort of useless, since most of the computers run x64 anyways) – Ilya Pakhmutov Apr 15 '19 at 17:27
  • @IlyaPakhmutov Yes but to learn any mode is suitable... – Jean-Baptiste Yunès Apr 15 '19 at 17:30
  • @Jean-BaptisteYunès Can you recommend some lectures on that? I've been studying at university for 4 years now, and I still can't program well. I often ask teachers, but they are just ignorant themselves, knowing only how to make console applications. I have a hard time reading information that is larger than 5 pages, but I can listen endlessly. – Ilya Pakhmutov Apr 15 '19 at 17:32
  • @IlyaPakhmutov There is a lot of resources on the subject over Internet. But as we don't have any good spec of what you are trying to achieve it is hard to tell you more. – Jean-Baptiste Yunès Apr 16 '19 at 06:50
  • 1
    There aren't many things in C or C++ that are worse than C++ exception handling but setjmp/longjmp is definitely one. Avoid. At all costs. Like the plague. – Lundin Apr 18 '19 at 06:52

2 Answers2

1

If you want to be portable, then you can use array indexes, to an array where you store all the 64bit pointers (or simply an array of pointers to structures with pages of information about what to do on some exception).

How do you populate such an array is another question. Of course you don't need to populate the array with all the instances that can become an exception, only with the ones you have try-ed and are able to catch. But probably you will need more than just a pointer (as you have to deal with the runtime case in which you have the same exception, catched in multiple active places in your stack.)

Once you solve the above problem, probably you can even use a short int for the thing, once you have understood the nature of the problem you need to solve.

Based on reading the comments, I see you comment that a global variable is not suitable because of multithreading concerns. First, you can have it global, in the context of the thread (as e.g. the errno variable), as that's the reason of using a void * to call the routine the thread executes, and returns back once finished. You can have it there, private to thread global data.

On a second point, if you want to manage such strange things from the point of view of C, as manipulating the stack in weird ways, the functions mentioned do (I don't believe you know completely how the internals of setjmp()/longjmp() work.) I can tell you that the setjmp()/longjmp() api was written a long of time ago (in the range of 50 years now), in the times of old V6 unix code, to cope with unknown unix device drivers error processing ---a very controlled and simple environment---) simply, the use of longjmp() is far more complicated (and strongly discouraged, even by their authors K & R) than switching to a different language (like the suggested C++) that fully supports exceptions in its core (this recommendation is not mine, it has been suggested in the comments to your question)

Third. If you use setjmp() and longjmp() you need to know also that they (both) use the calling thread's stack to mark the pointer and the where to go to store the information. So, you have to control, for example, that if you do a longjmp() in a signal handler, you can severely destroy the stack of the thread executing the signal handler (which is the one that got interrupted by the signal) if it is not the same thread as the one who did the setjmp() call. The reason for this is that the thread interrupted will switch its stack with the one of the thread that did the setjmp() and both threads will begin executing code with the same stack at different points, this jumps back to the time of implementation of both functions (there was only a pdp computer available, no multiple cpus/cores as it is common today, so there was only a stack) You have to be specially careful here, because normally, the thread that generates the exception is the same that the places to catch it, but this can be false for asynchronous traps, like signal processing.

By the way, what you are doing is very interesting, and will allow you to know how a language implements internally complex behaviours like exception processing. I applause you for your courage on trying this kind of things, and don't hesitate, that if you need a mentor in C++ I'll be available for you.

Just don't give up!!

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
1

You can enclose your jmp_buf-typed variable in a larger structure, possibly larger by just sizeof(void*). Then just before calling longjmp() you can store the pointer in that extra space. There's no need to try to squeeze a pointer into an int.

Example:

#include <stdio.h>
#include <setjmp.h>

struct jmp_buf_struct
{
  jmp_buf jb;
  void* exc_ptr;
};

void may_throw(jmp_buf jb)
{
  struct jmp_buf_struct* jbs_ptr = (struct jmp_buf_struct*)jb;
  jbs_ptr->exc_ptr = "Exception message!";
  longjmp(jb, 1);
}

int main()
{
  struct jmp_buf_struct jbs;
  if (setjmp(jbs.jb))
  {
    printf("Threw %p = \"%s\".", jbs.exc_ptr, (char*)jbs.exc_ptr);
  }
  else
  {
    may_throw(jbs.jb);
    puts("Didn't throw.");
  }
  return 0;
}

Output:

Threw 0x55638ebc78c4 = "Exception message!".

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180