Setjmp / longjmp in C #. Is it possible?

I ran into a problem when I need to do goto in local area:

if(...)      
{
   DoSomethingHere();
   if (...) goto Label;
}
else if(...)
{
Label:
  DoSomethingHereToo();
}

which is apparently not possible in C #.

Yes, I know that using goto is considered bad practice, but in this case it is much easier to do this with goto. Therefore, I would prefer not to get into the whole discussion "goto - the source of all the evil." For me, a much more interesting and more general question is the possibility of setjmp / longjmp in C #. So is that even possible?

+3
source share
9 answers

-, , , "goto" - - - goto - . C : , , . -, "" .

#. # ; try-catch-finally-throw, gotos , .

# . , , , . , , , , . "Goto" , , , .

- goto. , , ,

bool doFirstThing = false;
bool doSecondThing = false;
if (firstCondition) 
{
    doFirstThing = true;
    doSecondThing = true;
}
else if (secondCondition)
{
    doSecondThing = true;
}
if (doFirstThing) 
{
    DoFirstThing();
}
if (doSecondThing)
{
    DoSecondThing();
}

, , ..

: "doSecondThing" , , , . , , , , , , . , ?

+12

, . .


# . , , #. IL , IL , #, .

, goto , , post-compiler, IL. - . : , . , .

, :

  • # , "" , .
  • # . ( ), .
  • # - ; , goto. , # .
+3

.

, , , setjmp longjmp msvcrt.dll.

[DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl, EntryPoint="_setjmp")]
static extern int setjmp(out Jmp_buf env);

[DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void longjmp(ref Jmp_buf env, int val);

[StructLayout(LayoutKind.Sequential,Size=16*4)]
struct Jmp_buf{}

, , . P/Invoke native setjmp, setjmp P/Invoke. , longjmp AccessViolationException.

. mounta... , # . , , - "" , JITted setjmp. , , - .

#, , , ++/CLI!

#include <csetjmp>
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace std;

typedef void (*UnmanagedHandler)(int code);

void mysetjmp(jmp_buf env, UnmanagedHandler handler)
{
    handler(setjmp(env));
    throw 0;
}

void mylongjmp(jmp_buf env, int val)
{
    longjmp(env, val);
}

namespace jmptestdll
{
    public delegate void JumpHandler(int code);

    public ref class JumpBuffer
    {
    private:
        jmp_buf *env;

    public:
        JumpBuffer()
        {
            env = new jmp_buf[1];
        }

        ~JumpBuffer()
        {
            this->!JumpBuffer();
        }

        void Set(JumpHandler^ handler)
        {
            if(env)
            {
                IntPtr ptr = Marshal::GetFunctionPointerForDelegate(handler);
                UnmanagedHandler act = static_cast<UnmanagedHandler>(ptr.ToPointer());
                try{
                    mysetjmp(*env, act);
                }catch(int code)
                {

                }
            }
        }

        void Jump(int value)
        {
            if(env)
            {
                mylongjmp(*env, value);
            }
        }

    protected:
        !JumpBuffer()
        {
            if(env)
            {
                delete[] env;
            }
        }
    };
}

, , , ++ , . . - mysetjmp AccessViolationException, . "" throw .

var env = new JumpBuffer();
env.Set(
    delegate(int code)
    {
        Console.WriteLine(code);
        env.Jump(code+1);
        Console.WriteLine("Goodbye world!");
    }
);

" !" , 0 . :

static JumpBuffer buf = new JumpBuffer();

static void second()
{
    Console.WriteLine("second");
    try{
        buf.Jump(1);
    }finally{
        Console.WriteLine("finally");
    }
}

static void first()
{
    second();
    Console.WriteLine("first");
}

public static void Main(string[] args)
{
    buf.Set(
        val => {
            Console.WriteLine(val);
            if(val == 0) first();
            else Console.WriteLine("main");
        }
    );

    Console.ReadKey(true);
}

:

0


1

, finally, , . , Set .

, .

, # async. , await , .

public class LongJump
{
    Continuation continuation;

    public SetAwaiter Set()
    {
        return new SetAwaiter(this);
    }

    public JumpAwaiter Jump()
    {
        return new JumpAwaiter(this);
    }

    public struct JumpAwaiter : INotifyCompletion
    {
        readonly LongJump jump;

        public JumpAwaiter(LongJump jump)
        {
            this.jump = jump;
        }

        public JumpAwaiter GetAwaiter()
        {
            return this;
        }

        public bool IsCompleted{
            get{
                return false;
            }
        }

        public void OnCompleted(Action callerContinuation)
        {
            jump.continuation.Continue();
        }

        public void GetResult()
        {

        }
    }

    public struct SetAwaiter : INotifyCompletion
    {
        readonly LongJump jump;

        public SetAwaiter(LongJump jump)
        {
            this.jump = jump;
        }

        public SetAwaiter GetAwaiter()
        {
            return this;
        }

        public bool IsCompleted{
            get{
                return false;
            }
        }

        public void OnCompleted(Action callerContinuation)
        {
            jump.continuation = new Continuation(callerContinuation);
            callerContinuation();
        }

        public void GetResult()
        {

        }
    }

    private class Continuation
    {
        private readonly int savedState;
        private readonly object stateMachine;
        private readonly FieldInfo field;
        private readonly Action action;

        internal Continuation(Action action)
        {
            stateMachine = action.Target.GetType().InvokeMember("m_stateMachine", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField, null, action.Target, null);
            field = stateMachine.GetType().GetField("<>1__state", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            savedState = (int)field.GetValue(stateMachine);
            this.action = action;
        }

        internal void Continue()
        {
            field.SetValue(stateMachine, savedState);
            action();
        }
    }
}

public static void Main(string[] args)
{
    MainAsync().Wait();

    Console.ReadKey(true);
}

public static async Task MainAsync()
{
    var jump = new LongJump();
    Console.WriteLine("Begin");
    int code = 0;
    await jump.Set();
    Console.WriteLine(code);
    code += 1;
    await InnerMethod(code, jump);
    Console.WriteLine("End");
}

public static async Task InnerMethod(int code, LongJump jump)
{
    if(code < 5)
    {
        await jump.Jump();
    }
}

COMEFROM #.

, await jump.Set(); , . await jump.Jump(); . , , , , .

, ++/CLI, await jump.Jump(), .

, # " " - .

+3

:

condition1Cache = condition1;
condition2Cache = false;
if ( condition1Cache )
{    
   yadda yadda
   condition2Cache = condition2;
}
/* short-circuit evaluation will prevent condition3 from being evaluated (and possibly having side-effects) in a manner compatible with the original code. */
if ( ( condition1Cache && condition2Cache ) || (!condition1Cache && condition3) ) 
{
   bada bing
}

?

: , , , , .

+1

rvalues, , gotos flags. :

if (condition1() ? (DoSomethingHere(),condition2()) : condition3())
  DoSomethingHere2();

, , , DoSomethingHere 2, . , , ?: ( / ).

, , gotos, "" - , , node. "goto" , "goto".

+1

:

if(condition1)      
{
   DoSomethingHere();
}

if(condition2)
{
   DoSomethingHereToo();
}

, , ?

0

, , , ,

bool doSomthingElseFlag = false
if(...)      
{
   DoSomethingHere();
   if (...)
      doSomthingElseFlag = true;
}
else if(...)
{
  DoSomethingHere2();
  if (...)
     doSomthingElseFlag = true;
}
else if(...)
{
  //Not This function does not need the other function to run
  //so we do not set the flag
  DoSomethingHere3();
}
if (doSomthingElseFlag)
{
  DoSomethingElse();
}
0

setjmp/longjmp, , : . # , , stack overflow.

0

, . :

   void method1()
    {
       for (var i = 0; i < 100; i++)
       {
           method2(i);
           Console.WriteLine(i);

            EndOfLoop: //This is something like a setjmp marker
                    ;
         }
    }

    void method2(int i)
    {
       if (i%10 == 0)
           Console.WriteLine("Next Number can be divided by 10");

   // Now Long jmp to EndOfLoop
    #if IL
        br EndOfLoop 
    #endif
    }

, :)

:

1 2 3 4 5 6 7 8 9 The next number can be divided by 10 10 11 ..... Each in a new line.

ILDASM output for method 1:

.method private hidebysig static void  method1() cil managed
{
  // Code size       35 (0x23)
  .maxstack  2
  .locals init ([0] int32 i,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0019
  IL_0005:  nop
  IL_0006:  ldloc.0
  IL_0007:  call       void Test::method2(int32)
  IL_000c:  nop
  IL_000d:  ldloc.0
  IL_000e:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0013:  nop
  IL_0014:  nop
  IL_0015:  ldloc.0
  IL_0016:  ldc.i4.1
  IL_0017:  add
  IL_0018:  stloc.0
  IL_0019:  ldloc.0
  IL_001a:  ldc.i4.s   100
  IL_001c:  clt
  IL_001e:  stloc.1
  IL_001f:  ldloc.1
  IL_0020:  brtrue.s   IL_0005
  IL_0022:  ret
} // end of method Test::method1

ILDASM output for method 2:

.method private hidebysig static void  method2(int32 i) cil managed
{
  // Code size       27 (0x1b)
  .maxstack  2
  .locals init ([0] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldc.i4.s   10
  IL_0004:  rem
  IL_0005:  ldc.i4.0
  IL_0006:  ceq
  IL_0008:  ldc.i4.0
  IL_0009:  ceq
  IL_000b:  stloc.0
  IL_000c:  ldloc.0
  IL_000d:  brtrue.s   IL_001a
  IL_000f:  ldstr      "Next Number can be divided by 10"
  IL_0014:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0019:  nop
  IL_001a:  ret
} // end of method Test::method2

Execution example:

enter image description here

-1
source

Source: https://habr.com/ru/post/1774976/


All Articles