C에서 큰 구조물에서 하위 구조를 얻는다
-
22-08-2019 - |
문제
나는 매우 큰 것을 가지고 있습니다 struct
기존 프로그램에서. 이 구조물에는 많은 비트 필드가 포함되어 있습니다.
나는 그것의 일부를 저장하고 싶습니다 (예 : 150 중 10 개의 필드).
서브 클래스를 저장하는 데 사용하는 예제 코드는 다음과 같습니다.
typedef struct {int a;int b;char c} bigstruct;
typedef struct {int a;char c;} smallstruct;
void substruct(smallstruct *s,bigstruct *b) {
s->a = b->a;
s->c = b->c;
}
int save_struct(bigstruct *bs) {
smallstruct s;
substruct(&s,bs);
save_struct(s);
}
또한 매번 변경하고 싶기 때문에 어느 부분을 선택하는 것이 너무 번거롭지 않기를 바랍니다. 내가 전에 제시 한 순진한 접근법은 매우 연약하고 인재 할 수 없습니다. 최대 20 개의 다른 필드를 확장 할 때는 모두에서 필드를 변경해야합니다. smallstruct
, 그리고, 그리고 substruct
기능.
나는 두 가지 더 나은 접근법을 생각했다. 불행히도 둘 다 외부를 사용해야합니다 실 내 구조를 구문 분석하는 도구처럼.
첫 번째 접근법은 자동으로 생성하는 것입니다 substruct
기능. 나는 단지 구조물을 설정할 것이다 smallstruct
, 그것을 구문 분석하고 생성하는 프로그램이 있습니다. substruct
필드에 따라 기능합니다 smallstruct
.
두 번째 접근법은 (C Parser와 함께)에 대한 메타 정보를 구축하는 것입니다. bigstruct
, 그런 다음 구조물의 특정 필드에 액세스 할 수있는 라이브러리를 작성하십시오. 그것은 Java의 클래스 반사의 임시 구현과 같습니다.
예를 들어, 구조물에 대한 구조 조정이 없다고 가정합니다
struct st {
int a;
char c1:5;
char c2:3;
long d;
}
다음 메타 정보를 생성하겠습니다.
int field2distance[] = {0,sizeof(int),sizeof(int),sizeof(int)+sizeof(char)}
int field2size[] = {sizeof(int),1,1,sizeof(long)}
int field2bitmask[] = {0,0x1F,0xE0,0};
char *fieldNames[] = {"a","c1","c2","d"};
나는 얻을 것이다 i
th 이 기능의 필드 :
long getFieldData(void *strct,int i) {
int distance = field2distance[i];
int size = field2size[i];
int bitmask = field2bitmask[i];
void *ptr = ((char *)strct + distance);
long result;
switch (size) {
case 1: //char
result = *(char*)ptr;
break;
case 2: //short
result = *(short*)ptr;
...
}
if (bitmask == 0) return result;
return (result & bitmask) >> num_of_trailing_zeros(bitmask);
}
두 방법 모두 추가 작업이 필요하지만 파서가 메이크 파일에 있으면 하위 구조를 바꾸는 것은 산들 바람입니다.
그러나 외부 의존성 없이도 그렇게하고 싶습니다.
누구든지 더 나은 아이디어가 있습니까? 내 아이디어가 좋은 곳은 인터넷에서 내 아이디어를 구현할 수있는 일이 있습니까?
해결책
필드의 순서를 변경하는 것이 문제가되지 않으면 소규모 스트럭 필드가 함께있는 방식으로 Bigstruct 필드를 재정렬 할 수 있으며 단순히 단순히 서로 캐스팅하는 문제 (아마도 오프셋을 추가 할 수 있음). . 같은 것 :
typedef struct {int a;char c;int b;} bigstruct;
typedef struct {int a;char c;} smallstruct;
int save_struct(bigstruct *bs) {
save_struct((smallstruct *)bs);
}
다른 팁
설명을 통해 원래 구조에 액세스하고 수정할 수있는 것처럼 보입니다. 나는 당신이 당신의 하위 구조를 완전한 유형으로 리팩토링 한 다음 (당신의 예에서와 같이) 그 구조를 큰 구조의 필드로 만들어 원래 구조의 모든 필드를 작은 구조로 캡슐화하는 것이 좋습니다.
작은 예에서 확장 :
typedef struct
{
int a;
char c;
} smallstruct;
typedef struct
{
int b;
smallstruct mysub;
} bigstruct;
소규모 스트럽 정보에 액세스하는 것은 다음과 같이 수행됩니다.
/* stack-based allocation */
bigstruct mybig;
mybig.mysub.a = 1;
mybig.mysub.c = '1';
mybig.b = 2;
/* heap-based allocation */
bigstruct * mybig = (bigstruct *)malloc(sizeof(bigstruct));
mybig->mysub.a = 1;
mybig->mysub.c = '1';
mybig->b = 2;
그러나 당신은 또한 포인터를 작은 구조물로 전달할 수도 있습니다.
void dosomething(smallstruct * small)
{
small->a = 3;
small->c = '3';
}
/* stack based */
dosomething(&(mybig.mysub));
/* heap based */
dosomething(&((*mybig).mysub));
이익:
- 매크로가 없습니다
- 외부 의존성이 없습니다
- 메모리 주문 캐스팅 해킹이 없습니다
- 깨끗하고 읽기 쉬운 코드.
매크로는 당신의 친구입니다.
한 가지 해결책은 큰 구조물을 자체 파일로 옮기고 매크로 파티를 갖는 것입니다.
구조를 정상적으로 정의하는 대신 시작_structure, end_structure, normal_field, subset_field와 같은 매크로를 선택하십시오.
그런 다음 파일을 몇 번 포함시켜 각 패스에 대한 해당 구조를 재정의 할 수 있습니다. 첫 번째는 정의를 정상 구조로 바꾸고, 두 유형의 필드는 정상적으로 출력됩니다. 두 번째는 Normal_field가 아무것도없고 서브 세트를 생성 할 것입니다. 세 번째는 서브 세트 필드를 복사하기위한 적절한 코드를 만듭니다.
구조의 단일 정의로 끝나면 서브 세트에 어떤 필드를 제어하고 적절한 코드를 자동으로 생성 할 수 있습니다.
메타 데이터를 얻는 데 도움을주기 위해 오프셋 () 매크로를 참조 할 수 있습니다.
나는이 접근법을 취할 것을 제안한다 :
- 큰 구조를 쓴 사람을 저주하십시오. 부두 인형을 얻고 재미있게 보내십시오.
- 어떻게 든 필요한 큰 구조의 각 필드를 표시하십시오 (매크로 또는 댓글 또는 무엇이든)
- 헤더 파일을 읽고 표시된 필드를 추출하는 작은 도구를 작성하십시오. 주석을 사용하는 경우 각 필드에 우선 순위 또는 정렬 할 수있는 무언가를 줄 수 있습니다.
- 하위 구조에 대한 새 헤더 파일을 작성하십시오 (고정 헤더 및 바닥 글 사용).
- 함수가 포함 된 새 C 파일을 작성하십시오
createSubStruct
큰 구조물에 대한 포인터를 취하고 포인터를 하위 구조로 반환합니다. - 함수에서 수집 된 필드를 통한 루프 및 방출
ss.field = bs.field
(즉, 필드를 하나씩 복사하십시오). - Makefile에 작은 도구를 추가하고 새 헤더 및 C 소스 파일을 빌드에 추가하십시오.
사용하는 것이 좋습니다 gawk
, 도구로서, 당신이 편한 스크립팅 언어; 건축하는 데 30 분이 걸립니다.
편집] 당신이 정말로 반성을 시도하고 싶다면 (내가 제안하는 것; 그것은 많은 일이 C에서 그 일을하게 될 것입니다), offsetof()
매크로는 당신의 친구입니다. 이 매크로는 필드의 오프셋을 구조로 반환합니다 (가장 자주 ~ 아니다 이전 필드의 크기의 합). 보다 이 기사.
edit2] 자신의 파서를 쓰지 마십시오. 자신의 파서를 올바르게 얻으려면 몇 달이 걸립니다. 나는 내 인생에서 많은 파서를 썼기 때문에 알고 있습니다. 대신 복사 해야하는 원래 헤더 파일의 일부를 표시 한 다음, 알고있는 하나의 구문 분석기 (C 컴파일러 중 하나)에 의존하십시오. 이 작업을 만드는 방법은 다음과 같습니다.
struct big_struct {
/**BEGIN_COPY*/
int i;
int j : 3;
int k : 2;
char * str;
/**END_COPY*/
...
struct x y; /**COPY_STRUCT*/
}
도구를 복사하십시오 /**BEGIN_COPY*/
그리고 /**END_COPY*/
.
특별한 주석을 사용하십시오 /**COPY_STRUCT*/
도구를 생성하도록 지시합니다 memcpy()
과제 등 대신
이것은 몇 시간 안에 작성 및 디버깅 할 수 있습니다. 기능없이 C에 대한 구문 분석기를 설정하는 데 시간이 오래 걸릴 것입니다. 즉, 유효한 C를 읽을 수있는 것이 있지만 C를 이해하는 구식의 일부와 데이터에 유용한 부분을 작성해야합니다.