순수한 C로 RAII를 구현합니까?
순수한 C로 RAII 를 구현할 수 있습니까?
나는 그것이 정상적인 방법으로 가능하지 않다고 생각하지만 아마도 일종의 더러운 속임수를 사용하는 것이 가능할 것입니다. 표준 free
함수의 오버로드 가 떠오르거나 스택의 반환 주소를 덮어 써서 함수가 반환 될 때 어떻게 든 리소스를 해제하는 다른 함수를 호출합니까? 아니면 setjmp / longjmp 트릭으로?
이것은 순전히 학문적 관심사이며 실제로 그렇게 이식 할 수없고 미친 코드를 작성할 의도는 없지만 그것이 가능한지 궁금합니다.
표준에는 그러한 가능성이 포함되어 있지 않기 때문에 이것은 고유 한 구현에 따라 다릅니다. GCC의 경우 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와 같은 고급 매크로 프레임 워크를 사용하고 싶을 것입니다.
컴파일러가 C99 (또는 그것의 상당 부분)를 지원하는 경우 다음과 같은 가변 길이 배열 (VLA)을 사용할 수 있습니다.
int f(int x) {
int vla[x];
// ...
}
메모리가 제공되는 경우 gcc는 C99에 추가되기 훨씬 전에이 기능을 보유 / 지원했습니다. 이것은 (대략) 다음과 같은 간단한 경우와 같습니다.
int f(int x) {
int *vla=malloc(sizeof(int) *x);
/* ... */
free vla;
}
그러나 파일 닫기, 데이터베이스 연결 등과 같이 dtor가 수행 할 수있는 다른 작업을 수행 할 수는 없습니다.
아마도 가장 쉬운 방법은 goto를 사용하여 함수의 끝 부분에있는 레이블로 이동하는 것입니다. 그러나 그것은 아마도 여러분이보고있는 종류에 비해 너무 수동적 일 것입니다.
스택의 반환 주소를 덮어 쓰도록 선택했습니다. 가장 투명하게 작동합니다. 바꾸기 free
는 힙 할당 "개체"에서만 작동합니다.
alloca ()를 보셨습니까? var가 범위를 벗어날 때 해제됩니다. 하지만 효과적으로 사용하려면 호출자는 항상 alloca를 수행해야합니다. strdup을 구현하고 있다면 alloca를 사용할 수 없습니다.
https://github.com/psevon/exceptions-and-raii-in-c 에서 고유하고 공유 된 스마트 포인터 및 예외의 C 구현을 확인 하십시오 . 이 구현은 매크로 대괄호 BEGIN ... END에 의존하여 중괄호를 대체하고 범위를 벗어나는 스마트 포인터를 감지하며 리턴을위한 매크로 대체를 사용합니다.
전에는 속성 정리에 대해 몰랐습니다. 확실히 적용 가능한 깔끔한 솔루션이지만 setjmp / longjmp 기반 예외 구현에서는 제대로 작동하지 않는 것 같습니다. 정리 함수는 예외를 발생시킨 범위와이를 포착하는 범위 사이의 중간 범위 / 함수에 대해 호출되지 않습니다. Alloca에는이 문제가 없지만, alloca를 사용하면 메모리가 스택 프레임에서 할당되기 때문에 호출 한 함수에서 메모리 청크의 소유권을 외부 범위로 전송할 수 없습니다. C ++ unique_ptr 및 shared_ptr과 다소 유사한 스마트 포인터를 구현할 수 있습니다. {} 대신 매크로 대괄호를 사용해야하고 추가 논리를 범위 진입 / 종료에 연결할 수 있도록 반환해야한다고 생각했습니다. https://github.com/psevon/exceptions-and-raii-in-c 에서 autocleanup.c를 참조하십시오. 구현을 위해.
Johannes의 답변에서이 부분을 보완하려면 :
정리 속성은 변수가 범위를 벗어날 때 함수를 실행합니다.
정리 속성 ( http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Variable-Attributes.html ) 에는 제한 이 있습니다.이 속성은 자동 함수 범위 변수에만 적용 할 수 있습니다.
따라서 파일에 정적 변수가있는 경우 다음과 같이 정적 변수에 대해 RAII를 구현할 수 있습니다.
#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
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
'program story' 카테고리의 다른 글
모든 Java 바이트가 서명된다는 사실을 해결하는 가장 좋은 방법은 무엇입니까? (0) | 2020.12.12 |
---|---|
여러 SVN 저장소를 하나로 결합 (0) | 2020.12.12 |
GROUP BY 및 COUNT (DISTINCT)를 사용하는 LINQ to SQL (0) | 2020.12.12 |
관계형 데이터베이스에서 "튜플"이라는 용어는 무엇을 의미합니까? (0) | 2020.12.12 |
dll에서 std :: 객체 (벡터, 맵 등)를 포함하는 클래스 내보내기 (0) | 2020.12.12 |