C에서 여러 개의 실행 취소를 수행하는 방법을 정리
누군가는 아마 예외에 대해 말할 것입니다.그러나 C에서 많은 코드를 반복하지 않고 다음을 깨끗하고 분명하게 수행하는 다른 방법은 무엇입니까?
if (Do1()) { printf("Failed 1"); return 1; }
if (Do2()) { Undo1(); printf("Failed 2"); return 2; }
if (Do3()) { Undo2(); Undo1(); printf("Failed 3"); return 3; }
if (Do4()) { Undo3(); Undo2(); Undo1(); printf("Failed 4"); return 4; }
if (Do5()) { Undo4(); Undo3(); Undo2(); Undo1(); printf("Failed 5"); return 5; }
Etc...
이것은 gotos를 사용하는 한 가지 경우일 수 있습니다.아니면 여러 개의 내부 기능이...
네, 그런 경우에는 반복하지 않기 위해 goto를 사용하는 것이 꽤 일반적입니다.
예:
int hello() {
int result;
if (Do1()) { result = 1; goto err_one; }
if (Do2()) { result = 2; goto err_two; }
if (Do3()) { result = 3; goto err_three; }
if (Do4()) { result = 4; goto err_four; }
if (Do5()) { result = 5; goto err_five; }
// Assuming you'd like to return 0 on success.
return 0;
err_five:
Undo4();
err_four:
Undo3();
err_three:
Undo2();
err_two:
Undo1();
err_one:
printf("Failed %i", result);
return result;
}
항상 그렇듯이 큰 "실행 취소 코드"를 피하기 위해 기능을 작게 유지하고 작업을 의미 있는 방식으로 일괄 처리하기를 원할 수도 있습니다.
이것은 gotos를 사용하는 한 가지 경우일 수 있습니다.
물론이죠, 그렇게 해보죠.가능한 구현 방법은 다음과 같습니다.
#include "stdio.h"
int main(int argc, char **argv) {
int errorCode = 0;
if (Do1()) { errorCode = 1; goto undo_0; }
if (Do2()) { errorCode = 2; goto undo_1; }
if (Do3()) { errorCode = 3; goto undo_2; }
if (Do4()) { errorCode = 4; goto undo_3; }
if (Do5()) { errorCode = 5; goto undo_4; }
undo_5: Undo5(); /* deliberate fallthrough */
undo_4: Undo4();
undo_3: Undo3();
undo_2: Undo2();
undo_1: Undo1();
undo_0: /* nothing to undo in this case */
if (errorCode != 0) {
printf("Failed %d\n", errorCode);
}
return errorCode;
}
기능에 동일한 서명이 있는 경우 다음과 같은 작업을 수행할 수 있습니다.
bool Do1(void) { printf("function %s\n", __func__); return true;}
bool Do2(void) { printf("function %s\n", __func__); return true;}
bool Do3(void) { printf("function %s\n", __func__); return false;}
bool Do4(void) { printf("function %s\n", __func__); return true;}
bool Do5(void) { printf("function %s\n", __func__); return true;}
void Undo1(void) { printf("function %s\n", __func__);}
void Undo2(void) { printf("function %s\n", __func__);}
void Undo3(void) { printf("function %s\n", __func__);}
void Undo4(void) { printf("function %s\n", __func__);}
void Undo5(void) { printf("function %s\n", __func__);}
typedef struct action {
bool (*Do)(void);
void (*Undo)(void);
} action_s;
int main(void)
{
action_s actions[] = {{Do1, Undo1},
{Do2, Undo2},
{Do3, Undo3},
{Do4, Undo4},
{Do5, Undo5},
{NULL, NULL}};
for (size_t i = 0; actions[i].Do; ++i) {
if (!actions[i].Do()) {
printf("Failed %zu.\n", i + 1);
for (int j = i - 1; j >= 0; --j) {
actions[j].Undo();
}
return (i);
}
}
return (0);
}
Do 함수 중 하나의 반환을 변경하여 반응을 확인할 수 있습니다 :)
완전성을 위해 약간의 난독화:
int foo(void)
{
int rc;
if (0
|| (rc = 1, do1())
|| (rc = 2, do2())
|| (rc = 3, do3())
|| (rc = 4, do4())
|| (rc = 5, do5())
|| (rc = 0)
)
{
/* More or less stolen from Chris' answer:
https://stackoverflow.com/a/53444967/694576) */
switch(rc - 1)
{
case 5: /* Not needed for this example, but left in in case we'd add do6() ... */
undo5();
case 4:
undo4();
case 3:
undo3();
case 2:
undo2();
case 1:
undo1();
default:
break;
}
}
return rc;
}
사용하다goto
C에서 정리를 관리합니다.
예를 들어 Linux 커널 코딩 스타일을 확인합니다.
을
goto
sis:
- 무조건적인 문장은 이해하기 쉽고 내포된 내용을 따르는 것이 줄어듭니다.
- 수정을 방지할 때 개별 종료 지점을 업데이트하지 않음으로써 발생하는 오류
- 컴파일러 작업을 저장하여 중복 코드를 최적화합니다 ;)
예:
int fun(int a) { int result = 0; char *buffer; buffer = kmalloc(SIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; if (condition1) { while (loop1) { ... } result = 1; goto out_free_buffer; } ... out_free_buffer: kfree(buffer); return result; }
특정한 경우에는 다음과 같이 보일 수 있습니다.
int f(...)
{
int ret;
if (Do1()) {
printf("Failed 1");
ret = 1;
goto undo1;
}
...
if (Do5()) {
printf("Failed 5");
ret = 5;
goto undo5;
}
// all good, return here if you need to keep the resources
// (or not, if you want them deallocated; in that case initialize `ret`)
return 0;
undo5:
Undo4();
...
undo1:
return ret;
}
가지가 한성공하지 에 함수 할 수 입니다.else if
은 변수를 해서 실패한 곳을 추적하는 입니다.switch
쉽게 롤백할 수 있는 진술도 있습니다.
int ret=0;
if(Do1()) {
ret=1;
} else if(Do2()) {
ret=2;
} else if(Do3()) {
ret=3;
} else if(Do4()) {
ret=4;
} else if(Do5()) {
ret=5;
}
switch(ret) {
case 5:
Undo4();
case 4:
Undo3();
case 3:
Undo2();
case 2:
Undo1();
case 1:
printf("Failed %d\n",ret);
break;
}
return ret;
답변에서도 했듯이 예, 다른답설바같와이명한서변에,같이를 사용하면,goto
오류 처리의 경우 C에서 적합한 경우가 많습니다.
즉, 가능하면 해당 작업이 수행되지 않았더라도 정리 코드를 안전하게 호출해야 합니다.예를 들어, 다음 대신:
void foo()
{
int result;
int* p = malloc(...);
if (p == NULL) { result = 1; goto err1; }
int* p2 = malloc(...);
if (p2 == NULL) { result = 2; goto err2; }
int* p3 = malloc(...);
if (p3 == NULL) { result = 3; goto err3; }
// Do something with p, p2, and p3.
bar(p, p2, p3);
// Maybe bar() saved references to p and p2, but we don't need
// p3 anymore.
free(p3);
return 0;
err3:
free(p2);
err2:
free(p);
err1:
return result;
}
찬성합니다.
void foo()
{
int result = -1; // Or some generic error code for unknown errors.
int* p = NULL;
int* p2 = NULL;
int* p3 = NULL;
p = malloc(...);
if (p == NULL) { result = 1; goto exit; }
p2 = malloc(...);
if (p2 == NULL) { result = 2; goto exit; }
p3 = malloc(...);
if (p3 == NULL) { result = 3; goto exit; }
// Do something with p, p2, and p3.
bar(p, p2, p3);
// Set success *only* on the successful path.
result = 0;
exit:
// free(NULL) is a no-op, so this is safe even if p3 was never allocated.
free(p3);
if (result != 0)
{
free(p2);
free(p);
}
return result;
}
변수를 초기화해야 하기 때문에 효율성이 약간 떨어집니다.NULL
하지만 추가 라벨이 필요하지 않기 때문에 더 유지 관리가 가능합니다.코드를 변경할 때 틀린 부분이 적습니다.또한 성공 경로와 실패 경로 모두에 필요한 정리 코드가 있는 경우 코드 중복을 방지할 수 있습니다.
저는 일반적으로 다음과 같은 조건을 내포함으로써 이러한 문제에 접근합니다.
int rval = 1;
if (!Do1()) {
if (!Do2()) {
if (!Do3()) {
if (!Do4()) {
if (!Do5()) {
return 0;
// or "goto succeeded", or ...;
} else {
printf("Failed 5");
rval = 5;
}
Undo4();
} else {
printf("Failed 4");
rval = 4;
}
Undo3();
} else {
printf("Failed 3");
rval = 3;
}
Undo2();
} else {
printf("Failed 2");
rval = 2;
}
Undo1();
} else {
printf("Failed 1");
rval = 1;
}
return rval;
보통저는.DoX()
수집과 입니다.malloc()
리고그고.UndoX()
는 장애가 발생한 경우에만 수행해야 하는 해당 리소스 릴리스입니다.중첩은 해당 수집과 릴리스 간의 연관성을 명확하게 보여주며 실행 취소 작업을 위해 코드를 반복할 필요가 없습니다.또한 매우 쉽게 작성할 수 있습니다. 레이블을 만들거나 유지 관리할 필요가 없으며, 수집을 작성하는 즉시 리소스 릴리스를 올바른 위치에 배치하기가 쉽습니다.
이 접근 방식은 때때로 깊이 중첩된 코드를 생성합니다.그것은 저를 크게 괴롭히지는 않지만, 당신은 그것을 문제로 여길지도 모릅니다.
이 질문은 이미 답변에 과도한 부담을 가지고 있지만, 일부 코드베이스는 기본적으로 예외가 무엇인지 깨끗한 방법으로 처리할 래퍼 코드를 가지고 있다는 것을 지적하고 싶습니다.예를 들어, MuPdf는 예외 처리를 에뮬레이트하는 longjmp를 사용하여 몇 가지 속임수를 구현했습니다.제 생각에는, 그들은 이미 C++을 사용하고 있어야 하지만, 그것은 저 뿐입니다.
그런 포장지는 직접 해보셔도 됩니다.연습으로, 여러분의 요구 사항을 생각해 보고 이를 충족시키기 위해 노력하는 (매우) 조잡한 디자인을 생각해 보겠습니다.
- 다음 작업이 실패할 경우 취소해야 하는 일련의 작업이 있습니다.
- 여러 작업은 수행된 순서와 반대로 취소해야 합니다.
- 실패한 작업은 실행 취소할 수 없습니다.결국 실패했습니다.
- 도달하지 못한 작업도 원래 실행된 적이 없기 때문에 실행 취소해서는 안 됩니다.
- 프로그래머가 다음과 같이 명시적으로 말하는 것이 이상적입니다.그는 어떤 작업을 언제 취소해야 하는지 알고 있습니다.
이 문제를 해결하기 위해 몇 가지 매크로를 고안했습니다.
#include <stdio.h>
// Define some variables to keep track of when an error happened, and how many operations should be undone.
// Names are "mangled" by prefixing them with try_. You probably should come up with a better mangling scheme than this.
#define BEGIN_TRY int try_done = 0, try_error = 0, try_count = 0
// Here's how this works:
// - First, count the expression as an operation that may need to be undone;
// - If no error occured yet, do the operation;
// - If it succeeds, count it as a "done" operation;
// - If it fails, signal the error
#define TRY(expression) try_count++; if(!try_error && !(expression)) try_done++; else try_error = 1
// Here we take advantage of the fact that the operations behave like a queue.
// This means that, no matter what, operations need to be undone in the same
// order everytime, and if an operation needs to be undone when there
// are N operations, it also needs to be undone when there are N+1 operations.
// So, we don't really need to maintain the queue, if the programmer puts the operations in the correct order already. We just
// need to know how many operations to undo, and how much total operations are there (because we need to start at the end)
#define ON_ERROR(expression) do { if(try_error && try_done >= try_count--) {try_done--; (expression);} } while(0)
// To simplify the test, the "jobs" that we will try to do just pass or fail depending on the argument passed.
int job(int result) {return result;}
void undo(int i) {printf("Undone %d.\n", i);}
#define PASS 0
#define FAIL 1
// Let's test this
int main() {
BEGIN_TRY;
// try toying with the order (and quantity) of these.
// just remember that for each "TRY" there must be one "ON_ERROR"!
TRY(job(PASS));
TRY(job(PASS));
TRY(job(FAIL));
TRY(job(PASS));
// Because only the first two operations succeeded, we should only see the effects of undo(2) and undo(1).
ON_ERROR(undo(4));
ON_ERROR(undo(3));
ON_ERROR(undo(2));
ON_ERROR(undo(1));
}
생방송으로 보세요!
저는 C 전문가가 아니기 때문에 아마 이 문제에 버그가 있을 것입니다(안전한 매크로를 작성하는 것은 어렵습니다). 하지만 제 요점은 다음과 같습니다.당신의 요구 사항을 자세히 생각해 보면, 당신이 해야 할 일은 그것들을 모두 만족시킬 수 있는 해결책을 생각해 내는 것입니다.또 다른 요점은 다음과 같습니다.goto
많은 사람들은 매크로를 악으로 봅니다.그들 중 한 명이 되지 마세요: 매크로가 코드를 더 명확하게 하고 읽기 쉽게 한다면, 반드시 그것을 사용하세요.
대부분의 할당 및 초기화 기능과 마찬가지로 함수가 어떤 상태 포인터 또는 핸들을 반환하는 경우, 이를 사용하지 않고도 상당히 깔끔하게 수행할 수 있습니다.goto
변수에 초기 값을 부여함으로써.그러면 리소스의 일부만 할당된 경우를 처리할 수 있는 단일 할당 해제 기능을 사용할 수 있습니다.
예:
void *mymemoryblock = NULL;파일 *myfile = NULL;내 주머니에 = -1; bool allocate_모든 것을 할당합니다.{나의 메모리 블록 = malloc(1000);if (!나의 메모리 블록){거짓으로 반환합니다.} myfile = fopenfiles/file", "r";if (!내 파일){거짓으로 반환합니다.} mysocket = socket (AF_INET, SOCK_STREAM, 0);if (내 소켓 < 0){거짓으로 반환합니다.} true를 반환합니다.} void delocate_모든 것을 할당 해제합니다.{if (내 소켓 >= 0){닫기(내 소켓);내 소켓 = -1;} if (내 파일){fclose(내 파일);myfile = NULL;} if (내 메모리 블록){free(내 메모리 블록);my memory block = NULL;}}
그런 다음 다음에 수행합니다.
if (_모든 것){do_the_day를 수행합니다;}delocate_allocate()
TL;DR:
저는 그것이 다음과 같이 쓰여져야 한다고 믿습니다.
int main (void)
{
int result = do_func();
printf("Failed %d\n", result);
}
자세한 설명:
함수 유형에 대해 어떤 것도 가정할 수 없다면 함수 포인터 배열을 쉽게 사용할 수 없습니다. 그렇지 않으면 정답이 될 것입니다.
모든 함수 유형이 호환되지 않는다고 가정하면 호환되지 않는 모든 함수를 포함하는 원래의 모호한 설계를 다른 것으로 포장해야 합니다.
우리는 읽을 수 있고, 유지할 수 있고, 빠른 것을 만들어야 합니다."Do_x"의 실행 취소 동작이 "Do_y"의 실행 취소 동작에 의존하지 않도록 엄격한 결합을 피해야 합니다.
int main (void)
{
int result = do_func();
printf("Failed %d\n", result);
}
에▁where디do_func
이 함수는 다음과 같습니다.printf
는 알고리즘 로직과 분리된 UI 출력입니다.
do_func
실제 함수 호출을 중심으로 래퍼 함수처럼 구현되어 결과에 따라 결과를 처리합니다.
포함, (gcc -O3 용시사),do_func
두 기능을 데 ). 이 기능을 사용할 경우에는 오버헤드가 없습니다.
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;
}
여서기특동어의제다어니됩해에레이작은정▁에 의해 제어됩니다.undo
이는 다양한 비호환 함수의 래퍼입니다.호출할 함수, 호출 순서는 각 결과 코드에 연결된 특정 동작의 일부입니다.
우리는 특정 행동을 특정 결과 코드에 연결할 수 있도록 모든 것을 정리해야 합니다.그런 다음 필요할 때 유지보수 중에 동작을 변경해야 하는 경우에만 코드를 한 곳에서 변경합니다.
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);
}
출력:
Do1
Do2
Do3
Do4
Do5
Undo4
Undo3
Undo2
Undo1
Failed 5
여기 제가 벌레에 대한 탄력성을 발견한 답이 있습니다.
네. 그것은 사용합니다.goto
는 여러분이 에 있는 명확한 .goto
구조물이 스파게티 코드를 만들 수 있지만, 이 경우 모든 다른 오류 처리 방법은 보통 이 방법을 사용하는 것보다 스파게티와 더 유사합니다.goto
IMO가 우수합니다.).
어떤 사람들은 이 코드의 형태를 좋아하지 않을 수도 있지만, 저는 이 스타일에 익숙할 때 더 깨끗하고 읽기 쉬우며(물론 모든 것이 정렬되어 있을 때) 오류에 훨씬 더 탄력적이라는 것에 이의를 제기합니다.적절한 린터/정적 분석 설정이 있고 POSIX로 작업 중인 경우 오류 처리를 잘 할 수 있도록 이러한 방식으로 코딩해야 합니다.
static char *readbuf(char *path)
{
struct stat st;
char *s = NULL;
size_t size = 0;
int fd = -1;
if (!path) { return NULL; }
if ((stat(path, &st)) < 0) { perror(path); goto _throw; }
size = st.st_size;
if (size == 0) { printf("%s is empty!\n", path); goto _throw; }
if (!(s = calloc(size, 1))) { perror("calloc"); goto _throw; }
fd = open(path, O_RDONLY);
if (fd < -1) { perror(path); goto _throw; }
if ((read(fd, s, size)) < 0) { perror("read"); goto _throw; }
close(fd); /* There's really no point checking close for errors */
return s;
_throw:
if (fd > 0) close(fd);
if (s) free(s);
return NULL;
}
typedef void(*undoer)();
int undo( undoer*const* list ) {
while(*list) {
(*list)();
++list;
}
}
void undo_push( undoer** list, undoer* undo ) {
if (!undo) return;
// swap
undoer* tmp = *list;
*list = undo;
undo = tmp;
undo_push( list+1, undo );
}
int func() {
undoer undoers[6]={0};
if (Do1()) { printf("Failed 1"); return 1; }
undo_push( undoers, Undo1 );
if (Do2()) { undo(undoers); printf("Failed 2"); return 2; }
undo_push( undoers, Undo2 );
if (Do3()) { undo(undoers); printf("Failed 3"); return 3; }
undo_push( undoers, Undo3 );
if (Do4()) { undo(undoers); printf("Failed 4"); return 4; }
undo_push( undoers, Undo4 );
if (Do5()) { undo(undoers); printf("Failed 5"); return 5; }
return 6;
}
내가 만든undo_push
O(n) 작업을 합니다.이것은 가지고 있는 것보다 덜 효율적입니다.undo
언도보다 푸시가 더 많을 것으로 예상되므로 O(n) 작업을 수행합니다.하지만 이 버전은 조금 더 단순했습니다.
좀 더 산업적인 강점을 가진 버전은 머리와 꼬리 포인터와 균등한 용량을 가질 것입니다.
기본 아이디어는 실행 취소 작업의 대기열을 스택에 유지한 다음 정리해야 할 경우 실행하는 것입니다.
이곳의 모든 것은 지역적이기 때문에 우리는 지구의 상태를 오염시키지 않습니다.
struct undoer {
void(*action)(void*);
void(*cleanup)(void*);
void* state;
};
struct undoers {
undoer* top;
undoer buff[5];
};
void undo( undoers u ) {
while (u.top != buff)
{
(u.top->action)(u.top->state);
if (u.top->cleanup)
(u.top->cleanup)(u.top->state);
--u.top;
}
}
void pundo(void* pu) {
undo( *(undoers*)pu );
free(pu);
}
void cleanup_undoers(undoers u) {
while (u.top != buff)
{
if (u.top->cleanup)
(u.top->cleanup)(u.top->state);
--u.top;
}
}
void pcleanup_undoers(void* pu) {
cleanup_undoers(*(undoers*)pu);
free(pu);
}
void push_undoer( undoers* to_here, undoer u ) {
if (to_here->top != (to_here->buff+5))
{
to_here->top = u;
++(to_here->top)
return;
}
undoers* chain = (undoers*)malloc( sizeof(undoers) );
memcpy(chain, to_here, sizeof(undoers));
memset(to_here, 0, sizeof(undoers));
undoer chainer;
chainer.action = pundo;
chainer.cleanup = pcleanup_undoers;
chainer.data = chain;
push_undoer( to_here, chainer );
push_undoer( to_here, u );
}
void paction( void* p ) {
(void)(*a)() = ((void)(*)());
a();
}
void push_undo( undoers* to_here, void(*action)() ) {
undor u;
u.action = paction;
u.cleanup = 0;
u.data = (void*)action;
push_undoer(to_here, u);
}
그러면 다음을 얻을 수 있습니다.
int func() {
undoers u={0};
if (Do1()) { printf("Failed 1"); return 1; }
push_undo( &u, Undo1 );
if (Do2()) { undo(u); printf("Failed 2"); return 2; }
push_undo( &u, Undo2 );
if (Do3()) { undo(u); printf("Failed 3"); return 3; }
push_undo( &u, Undo3 );
if (Do4()) { undo(u); printf("Failed 4"); return 4; }
push_undo( &u, Undo4 );
if (Do5()) { undo(u); printf("Failed 5"); return 5; }
cleanup_undoers(u);
return 6;
}
하지만 그것은 점점 더 우스꽝스러워지고 있습니다.
교정기가 없는 것을 시도해 보겠습니다.
int result;
result = Do1() ? 1 : 0;
result = result ? result : Do2() ? 2 : 0;
result = result ? result : Do3() ? 3 : 0;
result = result ? result : Do4() ? 4 : 0;
result = result ? result : Do5() ? 5 : 0;
result > 4 ? (Undo5(),0) : 0;
result > 3 ? Undo4() : 0;
result > 2 ? Undo3() : 0;
result > 1 ? Undo2() : 0;
result > 0 ? Undo1() : 0;
result ? printf("Failed %d\r\n", result) : 0;
네.0;
는 C C입니다.는 C(및 C++)는 다음과 같습니다.일부 함수가 이 구문과 호환되지 않는 것(예: void)을 반환하는 경우 Undo5() 스타일을 사용할 수 있습니다.
정상적인 접근 방식(고토, 중첩 또는 연결된 경우)은 다음과 같습니다.
int bar(void)
{
int rc = 0;
do
{
if (do1())
{
rc = 1;
break;
}
if (do2())
{
rc = 2;
break;
}
...
if (do5())
{
rc = 5;
break;
}
} while (0);
if (rc)
{
/* More or less stolen from Chris' answer:
https://stackoverflow.com/a/53444967/694576) */
switch(rc - 1)
{
case 5: /* Not needed for this example, but left in in case we'd add do6() ... */
undo5();
case 4:
undo4();
case 3:
undo3();
case 2:
undo2();
case 1:
undo1();
default:
break;
}
}
return rc;
}
언급URL : https://stackoverflow.com/questions/53444743/clean-ways-to-do-multiple-undos-in-c
'it-source' 카테고리의 다른 글
UNION ALL은 결과 집합의 순서를 보장합니까? (0) | 2023.08.24 |
---|---|
표에 제약 조건 표시 (0) | 2023.08.19 |
CSS는 요소의 각 단어 뒤에 줄 바꿈을 강제로 수행할 수 있습니까? (0) | 2023.08.19 |
단편에 비해 몇 가지 활동이 있습니까? (0) | 2023.08.19 |
Angular JS로 CORS를 설정하려면 어떻게 해야 합니까? (0) | 2023.08.19 |