순수 C에 RAII를 구현하시겠습니까?
순수 C에서 RAII를 구현하는 것이 가능한가?
나는 그것이 제정신으로는 불가능하다고 생각하지만, 아마도 어떤 비열한 속임수를 써서 가능할까?표준 과부하free
함수가 떠오르거나 혹은 함수가 돌아올 때, 어떻게든 자원을 방출하는 다른 함수를 호출하도록 스택의 반환 주소를 덮어쓰거나 하는 일이 떠오른다.아니면 setjmp/longjmp 속임수로?
이것은 순전히 학문적인 관심사여서 나는 실제로 그렇게 휴대하기 힘들고 미친 코드를 쓸 생각은 전혀 없지만, 나는 그것이 가능한지 궁금하다.
이 기준서는 그러한 가능성을 포함하지 않기 때문에 내재적인 적용에 의존한다.의 의우.cleanup
특성은 변수가 범위를 벗어날 때 함수를 실행한다.
#include <stdio.h>
void scoped(int * pvariable) {
printf("variable (%d) goes out of scope\n", *pvariable);
}
int main(void) {
printf("before scope\n");
{
int watched __attribute__((cleanup (scoped)));
watched = 42;
}
printf("after scope\n");
}
인쇄물:
before scope
variable (42) goes out of scope
after scope
여기를 참조하십시오.
RAII를 C로 가져오는 하나의 솔루션(사용자가 없을 경우)cleanup()
은 은 정리를 수행할 코드로 함수 호출을 마무리하는 것이다.이것 또한 깔끔한 매크로(끝에 표시)로 포장할 수 있다.
/* Publicly known method */
void SomeFunction() {
/* Create raii object, which holds records of object pointers and a
destruction method for that object (or null if not needed). */
Raii raii;
RaiiCreate(&raii);
/* Call function implementation */
SomeFunctionImpl(&raii);
/* This method calls the destruction code for each object. */
RaiiDestroyAll(&raii);
}
/* Hidden method that carries out implementation. */
void SomeFunctionImpl(Raii *raii) {
MyStruct *object;
MyStruct *eventually_destroyed_object;
int *pretend_value;
/* Create a MyStruct object, passing the destruction method for
MyStruct objects. */
object = RaiiAdd(raii, MyStructCreate(), MyStructDestroy);
/* Create a MyStruct object (adding it to raii), which will later
be removed before returning. */
eventually_destroyed_object = RaiiAdd(raii,
MyStructCreate(), MyStructDestroy);
/* Create an int, passing a null destruction method. */
pretend_value = RaiiAdd(raii, malloc(sizeof(int)), 0);
/* ... implementation ... */
/* Destroy object (calling destruction method). */
RaiiDestroy(raii, eventually_destroyed_object);
/* or ... */
RaiiForgetAbout(raii, eventually_destroyed_object);
}
보일러 플레이트 코드는 모두 에서 표현할 수 있다.SomeFunction
매크로가 있으면 모든 통화마다 똑같기 때문에.
예를 들면 다음과 같다.
/* Declares Matrix * MatrixMultiply(Matrix * first, Matrix * second, Network * network) */
RTN_RAII(Matrix *, MatrixMultiply, Matrix *, first, Matrix *, second, Network *, network, {
Processor *processor = RaiiAdd(raii, ProcessorCreate(), ProcessorDestroy);
Matrix *result = MatrixCreate();
processor->multiply(result, first, second);
return processor;
});
void SomeOtherCode(...) {
/* ... */
Matrix * result = MatrixMultiply(first, second, network);
/* ... */
}
참고: P99와 같은 고급 매크로 프레임워크를 사용하여 위와 같은 것을 가능케 할 것이다.
요하네스 답변의 이 부분을 보완하기 위해:
정리 속성은 변수가 범위를 벗어날 때 함수를 실행한다.
정리 속성(http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Variable-Attributes.html):이 속성은 자동 기능 범위 변수에만 적용할 수 있다.
따라서 파일에 정적 변수가 있는 경우 다음과 같은 방법으로 정적 변수에 대해 RAIII를 구현할 수 있다.
#include <stdio.h>
#include <stdlib.h>
static char* watched2;
__attribute__((constructor))
static void init_static_vars()
{
printf("variable (%p) is initialazed, initial value (%p)\n", &watched2, watched2);
watched2=malloc(1024);
}
__attribute__((destructor))
static void destroy_static_vars()
{
printf("variable (%p), value( %p) goes out of scope\n", &watched2, watched2);
free(watched2);
}
int main(void)
{
printf("exit from main, variable (%p) value(%p) is static\n", &watched2, watched2);
return 0;
}
이 테스트는 다음과 같다.
>./example
variable (0x600aa0) is initialazed, initial value ((nil))
exit from main, variable (0x600aa0) value(0x16df010) is static
variable (0x600aa0), value( 0x16df010) goes out of scope
아마도 가장 쉬운 방법은 함수의 끝에 있는 레이블로 점프하기 위해 goto를 사용하는 것이지만, 그것은 아마도 당신이 보고 있는 종류의 것에는 너무 수동적일 것이다.
컴파일러가 C99(또는 상당 부분)를 지원하는 경우 다음과 같은 가변 길이 배열(VLA)을 사용할 수 있다.
int f(int x) {
int vla[x];
// ...
}
메모리가 사용되는 경우 gcc는 C99에 추가되기 훨씬 전에 이 기능을 지원했다.이는 다음과 같은 단순한 경우에 해당된다.
int f(int x) {
int *vla=malloc(sizeof(int) *x);
/* ... */
free vla;
}
그러나 그것은 당신이 파일 닫기, 데이터베이스 연결 등과 같이 dtor가 할 수 있는 다른 어떤 것도 하도록 허락하지 않는다.
나는 반송 주소를 스택에 덮어쓰는 쪽을 택할 것이다.가장 투명한 방법으로 해결되겠지.교체하는 중free
힙합의 "dump-dream"으로만 작동될 것이다.
alloca()를 보셨나요?VAR이 스코프를 벗어나면 자유로워진다.하지만 효과적으로 사용하기 위해서는 전화를 건 사람은 항상 물건을 보내기 전에 할당을 해야 한다...만약 당신이 스트러핑을 시행하고 있었다면, 음, 당신은 할당을 사용할 수 없었을 겁니다.
고유한 공유 스마트 포인터와 예외를 C로 구현하려면 https://github.com/psevon/exceptions-and-raii-in-c을 참조하십시오.이 구현은 매크로괄호에 의존한다. BEGIN...교정기를 교체하고 범위를 벗어난 스마트 포인터를 탐지하는 것은 물론 반환을 위한 매크로 교체도 종료한다.
전에는 속성 정리에 대해 몰랐어.적용 가능한 경우 확실히 깔끔한 솔루션이지만 setjmp/longjmp 기반 예외 구현에서는 잘 동작하지 않는 것 같다. 예외를 발생시킨 범위와 이를 포착하는 범위 사이의 중간 범위/기능에 대해서는 정리 기능이 호출되지 않는다.alloca에는 이 문제가 없지만 alloca를 사용하면 메모리 청크의 소유권을 스택 프레임에서 할당되므로 호출한 함수에서 외부 스코프로 전송할 수 없다.{} 대신 매크로 대괄호를 사용해야 한다고 생각하여 C++ 고유_ptr 및 shared_ptr과 유사한 스마트 포인터를 구현할 수 있으며, 추가 로직을 스코프 엔트리에 연결할 수 있다.구현에 대해서는 https://github.com/psevon/exceptions-and-raii-in-c의 autocleup.c를 참조하십시오.
my implementation of raii for c in pure c and minimal asm
@ https://github.com/smartmaster/sml_clang_raii
**RAII for C language in pure C and ASM**
**featurs : **
-easy and graceful to use
- no need seperate free cleanup functions
- able to cleanup any resources or call any function on scope exits
**User guide : **
-add source files in src folder to your project
-include sml_raii_clang.h in.c file
-annote resource and its cleanup functions
/* 샘플 코드 */
void sml_raii_clang_test()
{
//start a scope, the scope name can be any string
SML_RAII_BLOCK_START(0);
SML_RAII_VOLATILE(WCHAR*) resA000 = calloc(128, sizeof(WCHAR)); //allocate memory resource
SML_RAII_START(0, resA000); //indicate starting a cleanup code fragment, here 'resA000' can be any string you want
if (resA000) //cleanup code fragment
{
free(resA000);
resA000 = NULL;
}
SML_RAII_END(0, resA000); //indicate end of a cleanup code fragment
//another resource
//////////////////////////////////////////////////////////////////////////
SML_RAII_VOLATILE(WCHAR*) res8000 = calloc(128, sizeof(WCHAR));
SML_RAII_START(0, D000);
if (res8000)
{
free(res8000);
res8000 = NULL;
}
SML_RAII_END(0, D000);
//scope ended, will call all annoated cleanups
SML_RAII_BLOCK_END(0);
SML_RAII_LABEL(0, resA000); //if code is optimized, we have to put labels after SML_RAII_BLOCK_END
SML_RAII_LABEL(0, D000);
}
참조URL: https://stackoverflow.com/questions/368385/implementing-raii-in-pure-c
'programing' 카테고리의 다른 글
뷰에티파이 아이콘이 올바르게 표시되지 않음: "$vuetify.icons...." (0) | 2022.05.14 |
---|---|
무엇이 더 효율적인가?가루를 이용해 네모나게 만들거나, 아니면 그냥 곱해서 만들거나? (0) | 2022.05.14 |
Java 8 - 옵션 간의 차이.플랫맵 및 옵션.map (0) | 2022.05.13 |
C/C++ 컴파일러에 대한 최적의 컴파일러 경고 수준? (0) | 2022.05.13 |
힙 메모리(malloc/new)를 사용하면 비결정론적 프로그램이 생성되는가? (0) | 2022.05.13 |