2016-06-27 4 views
4

해시 맵 데이터 구조의 반복기 인터페이스를 설계하고 있습니다. 현재의 디자인은 다음과 같습니다정의가없는 불투명 한 구조체

// map.h 
typedef struct map_iterator map_iterator; 

// map.c 
struct map_iterator 
{ 
    // Implementation details 
}; 

// client.c 
map *m = map_new(); 
map_iterator *it = map_iterator_new(m); 
void *key, *value; 
while (map_iterator_next(it, &key, &value)) { 
    // Use key, value 
} 
map_iterator_free(it); 

그러나,이 반복자 객체에 대한 힙 할당을 요구하고, 클라이언트들이이 완료되면 반복자를 무료로 기억해야합니다. 내가 스택에 반복자를 반환 map_iterator_new 할 경우, 코드는 다음과 같습니다

// map.h 
typedef struct map_iterator 
{ 
    // Implementation details 
}; 

// client.c 
map *m = map_new(); 
map_iterator it = map_iterator_new(m); 
void *key, *value; 
while (map_iterator_next(&it, &key, &value)) { 
    // Use key, value 
} 

을하지만, 이것은 내가 클라이언트 코드에 map_iterator 구조체 (그렇지 않으면 내가 불완전한 유형의 오류)의 정의를 제공해야합니다. 이 정의를 숨기고 선언문 만 제공하고 싶습니다.

이것을 달성 할 수있는 방법이 있습니까? 본질적으로 클라이언트 코드에 "이 구조체는 스택에 할당 할 수 있도록 X 바이트를 사용하지만 멤버에 액세스하는 방법은 알려주지 않았습니다"라고 말하는 방법을 찾고 있습니다.

편집 : 표준 C, 제발! (컴파일러 확장/플 '폼 특정 기능 없음)

+0

이터레이터의 '구현 세부 사항'은 무엇입니까? – 2501

+0

@ 2501 반복자 상태, 예 : hashmap에 대한 포인터, 현재 버킷 인덱스, 수정 횟수 등 –

+1

이터레이터에 대한 포인터를 제공하지 않고 나중에 해방하는 것이 문제입니다. 컬 (curl) 등과 같은 많은 라이브러리도 이러한 인터페이스를 제공합니다. 차라리 첫 번째 옵션을 두 번째 옵션보다 사용하고 싶습니다. – ckruczek

답변

-1

alloca이라는 기능이 있으며 호출자의 스택에 메모리를 예약합니다. 따라서 코드는 다음과 같습니다.

// map.h 
typedef struct map_iterator map_iterator; 
extern int map_iterator_size; 
// map.c 
struct map_iterator 
{ 
    // Implementation details 
}; 
int map_iterator_size = sizeof(struct map_iterator); 
// client.c 
map *m = map_new(); 
map_iterator *it = alloca(map_iterator_size); 
map_iterator_new(m, it); 
void *key, *value; 
while (map_iterator_next(it, &key, &value)) { 
    // Use key, value 
} 
+0

고마워요.하지만 C99 표준을 엄격하게 준수하는 솔루션을 찾고 있다는 것을 잊어 버렸습니다. 미안합니다. –

+0

'alloca'는 스택 손상 취약점에 개입 한 역사를 고려해 보면 결코 사용하지 말아야 할 기능 중 하나입니다. 또한 API 사용자가 코딩 실수/선택을하기 때문에 API 디자인과는 거의 관련이 없습니다. 마지막으로 불완전한 타입은'sizeof()'를 가지지 않는다. –

+0

'alloca()'와 스택에 직접 객체를 갖는 것 사이에 (스택 손상의 관점에서) 어떤 차이점도 보이지 않습니다. 'sizeof'는 완전한 정의로 그 자리에서 계산됩니다. – nothrow

0

직렬화를 사용하십시오.

항상 T의 객체를 부호없는 char의 배열로 복사 한 다음 T 유형의 객체로 복사 할 수 있습니다.이 객체는 원래 객체와 동일합니다. T는 임의의 객체 유형이 될 수 있으며,이 자동 저장 기간 (스택)를 수행 할 수

int value = 568; 
unsigned char store[sizeof(int)]; 
memcpy(store , &value , sizeof(int)); 
int other; 
memcpy(&other , store , sizeof(int)); 
assert(other == value), 

이것은 (AB)가 사용자로부터 실행을 숨기기 위해 사용될 수있다. 실제 구현을 정의하고 사용자에게는 보이지 않는 구조체와 적절한 크기의 부호없는 char 배열 만 표시되는 구조체 두 개를 정의하십시오.

implementation.c

#include "implementation.h" 

struct invisible 
{ 
    int element1; 
    char element2 
    float element3; 
    void** element4; 
}; 

_Static_assert(sizeof(struct invisible) <= VISIBLE_SIZE); 

struct visible New(void) 
{ 
    struct invisible i = { 1 , '2' , 3.0F , NULL }; 
    struct visible v = { 0 }; 
    memcpy(&v , &i , sizeof(i)); 
    return v; 
} 

void Next(struct visible* v) 
{ 
    struct invisible i = { 0 }; 
    memcpy(&i , &v , sizeof(i)); 
    i.element1++; //make some changes 
    const int next = i.element; 
    memcpy(&v , &i , sizeof(i)); 
    return next; 
} 

implementation.h

#define VISIBLE_SIZE 24 //make sure it greater or equal to the size of struct invisible 
struct visible 
{ 
    unsigned char[VISIBLE_SIZE]; 
}; 

struct visible New(void); 
int Next(struct visible* v); 

user.c

,
#include "implementation.h" 

void Func(void) 
{ 
    struct visible v = New(); 
    while(1) 
    { 
     const int next = Next(&v); 
     if(next == 10) 
     { 
       break; 
     } 
     printf("%d\n" , next); 
    } 
} 

이 예는 회원 element1 만 접촉합니다. 실제 구현에서는 보이지 않는 구조체를 자유롭게 수정할 수 있습니다.