2011-05-11 3 views
0

구조체 멤버를 매핑하여 루프의 분기를 제거 할 수 있습니다. C에서 이것을 구현하는 가장 좋은 방법이나 컨벤션은 무엇입니까? 그 대신 2 차원 배열이 될 수 있다고 가정합니다 ... 그런 다음 char 키에 정수를 매핑 할 수 있습니까? 당신이 사용자의 입력에 따라 배열을 함수에 대한 포인터의 배열을 생성하고 처리 할 수 ​​있도록C dictionary/map

char chunk[32]; 
    int n; 
    int i; 
    char *ptr = config; 
    while (*ptr != '\0') { 
     int items_read = sscanf(ptr, "%31[^;]%n", chunk, &n); 

     if(chunk[0] == 'S' && chunk[1] == 'P') { 
      for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
       theMeas[i].signal_path = atoi(&chunk[2]); 
      } 
     }  
     if(chunk[0] == 'T' && chunk[1] == 'L') { 
      for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
       theMeas[i].trace_length = atoi(&chunk[2]); 
      } 
     }  
     if(chunk[0] == 'S' && chunk[1] == 'R') { 
      for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
       theMeas[i].sample_rate = atoi(&chunk[2]); 
      } 
     } 

     chunk[0]='\0'; 
     if (items_read == 1) 
      ptr += n; 
     if (*ptr != ';') { 
      break; 
     } 
     ++ptr; 
    } 
+1

파이썬과 같은 사전을 원하십니까, 아니면 '구조체'에서 명명 된 데이터에 액세스하는 더 쉬운 방법을 원하십니까? 하나는 다른 하나보다 훨씬 더 어렵습니다. –

+0

if 분기를 제거하고 싶습니다. 그래서 청크가 구조체 멤버에 매핑되는 루프에서 하나의 명령문을 사용할 수 있습니다. 파이썬 같은 동적 인 언어에서 나는 theMeas [chunk] = chunk [2]를 할 수있었습니다. –

+0

'chunk'는 "theMeas [i] [chunk]"를 (파이썬에서도) 할 수있는 것처럼 보이지 않습니다. 처음 두 글자를 원하는 회원의 머리 글자로 사용하고 있습니다. 'chunk [0]'과'chunk [1]'만을 사용한다. –

답변

3

나는 당신이 (이상적) 원하는 것은 사전 용의자 :

물론
theMeas[i]["signal_path"] = atoi(&chunk[2]); 

, 위의 구문은 C에서 발생하지 않습니다,하지만 여기 정말 중요하지 않아. 문제는 사전 데이터 유형을 구현하는 모든 코드를 작성해야한다는 것이며, 과도하다고 생각합니다. 가지 (

foreach(signal_path, trace_length, sample_rate) 

그리고 난 당신이 할 수있는 당신에게 여기있어 :

그래서 나는 당신이 (정말) 원하는 것은 루프에서 사용할 수있는 이름을 가질 수있는 방법 의심)!가장 간단한 방법은 enum 함께 대신 struct 회원의

enum fields { 
    signal_path, 
    trace_length, 
    sample_rate, 
    END_fields, 
    UNKNOWN_fields, 
    BEGIN_fields = 0, 
}; 

, 당신은 배열 사용 인덱스로

int theMeas[size][END_fields]; 

는 "회원"이 사용

theMeas[i][signal_path]; 

당신 모든 "회원"을 통해 반복 할 수 있습니다. 다음을 사용할 수 있습니다.

for(enum fields j = BEGIN_fields; j != END_fields; j++) 
    theMeas[i][j]; 
당신은 문자 기반의 비교를 얻을 할 때

이 조금 분해 않지만, 우리는 약간의 작업을 수행 할 수 있습니다

theMeas[i][from_abv(chunk)] = atoi(&chunk[2]); 

또는 :

const char *to_str(enum fields f) 
{ 
#define FIELD(x) case x: return #x 
    switch(f) 
     { 
     FIELD(signal_path); 
     FIELD(trace_length); 
     FIELD(sample_rate); 
     default: return "<unknown>"; 
     } 
#undef FIELD 
} 

enum fields from_str(const char *c) 
{ 
#define FIELD(x) if(!strcmp(c, #x)) return x 
     FIELD(signal_path); 
     FIELD(trace_length); 
     FIELD(sample_rate); 
     default: return UNKNOWN_fields; 
#undef FIELD 
} 

enum fields from_abv(char *c) 
{ 
    for(enum fields i = BEGIN_fields; i < END_fields; i++) 
     { 
     char *field = field_str(i); 
     if(tolower(c[0]) == field[0] && tolower(c[1]) == strchr(field, '_')[1]) 
      return i; 
     } 
    return UNKNOWN_fields; 
} 

귀하의 if 문 는 대체 될 수있다 더 안전하게 :

enum fields j = from_abv(chunk); 
if(j != UNKNOWN_fields) theMeas[i][j] = atoi(&chunk[2]); 
else /* erroneous user input */; 

어느 정도까지 얻을 수 있습니다.

많은 부분을 자동화하는 매크로 작성을 용이하게하기 위해 의도적으로 명명 체계를 사용했습니다. 의 시도하자

#define member(name, ...) \ 
    enum name { __VA_ARGS__, \ 
       M_END_##name, \ 
       M_UNKNOWN_##name, \ 
       M_BEGIN_##name = 0 } 

#define miter(name, var) \ 
     enum name var = M_BEGIN_##name; var != M_END_##name; var++ 

#define msize(name) M_END_##name 

사용법 : 마지막 비트가 그렇게 나쁜 것 같지 않습니다

// define our fields 
member(fields, signal_path, trace_length, sample_rate); 

// declare object with fields 
int theMeas[N][msize(fields)]; 

for(size_t i = 0; i < N; i++) 
    // iterate over fields 
    for(miter(fields, j)) 
     // match against fields 
     if(j == from_abv(chunk)) 
      theMeas[i][j] = atoi(&chunk[2]); 

있다. 여전히 struct과 비슷한 접근을 theMeas[i][signal_path]을 통해 허용하지만, "멤버"를 반복 할 수 있으며 매크로 뒤에있는 많은 부분을 숨길 수 있습니다.

to_strfrom_str 함수는 자동화하기 위해 약간의 매크로 트릭을 사용합니다. 아마도 P99를 살펴 봐야 할 것입니다. from_abv 함수는 다음에 반복 가능한 필드를 만들 때 밑줄이있는 이름을 사용한다는 것을 보장 할 수있는 방법이 없으므로 일반적인 경우에 권장 할만한 기능이 아닙니다. 물론 from_abv 함수를 삭제하고 구성원에게 SP, TLSR과 같은 이해하기 어려운 이름을 제공하여 문자열 데이터와 직접 비교할 수 있지만 strcmpmemcmp 크기로 변경해야합니다 (sizeof(#x) - 1)의 인수. 그리고 모든 장소 방금 자동으로 생성 할 수 있습니다 from_str을 사용하십시오 from_abv이는.)

그러나, from_abv 정의 어렵지 않다, 당신은 복사하여 붙여 넣기 정직 수 if 블록을 위에 얹어 놓으십시오 - 좀 더 효율적일 것입니다.하지만 "멤버"를 추가하면 함수를 업데이트해야합니다 (작성한대로 추가하면 추가됩니다).

+0

이것은 내가 서류를 연기 할 때 일어나는 일입니다. –

1

C는 pointers to functions 지원합니다. 이를 위해서는 동일한 서명으로 추가 기능을 구현해야합니다.

또 다른 방법은 if ​​절을 별도의 함수로 캡슐화하고 인수로 호출하는 것입니다.

그러나 나는 어떤 방법으로도 속도를 향상시킬 수 없을 것이라고 생각합니다.

+0

고마워요. 속도를 찾지 않고 코드의 중복을 줄이려고합니다. –

0

당신은 정수에 대한 포인터를 사용하여,이 같은 논리 뭔가를 다시 작성할 수 있습니다 :이 방법은 실제로 작업을 수행하는 코드에서합니다 (if 문)을 변경하는 어떤 변수에 대해 결정을 분리

while (*ptr != '\0') { 
    int items_read = sscanf(ptr, "%31[^;]%n", chunk, &n); 

    int *p = NULL; 
    if(chunk[0] == 'S' && chunk[1] == 'P') { 
     p = &theMeas[i].signal_path; 
    }  
    if(chunk[0] == 'T' && chunk[1] == 'L') { 
     p = &theMeas[i].trace_length; 
    }  
    if(chunk[0] == 'S' && chunk[1] == 'R') { 
     p = &theMeas[i].sample_rate; 
    } 

    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
     *p = atoi(&chunk[2]); 
    } 

이는 각 경우에 공통적입니다 (for 루프).

p == NULLchunk[1]이 예상 한 것과 일치하지 않는지 확인하고 싶습니다.

+0

아, 틀린 반복. 청크 cmd를 모든 cmd 유형에 대한 구조체 멤버에 매핑하려고합니다. 따라서 if 분기를 제거하고 루프에서 하나의 명령문 만 가질 수 있습니다. –

+0

아니면'else if' /'else if' /'else'를 사용하여 일치하지 않는 경우를 잡아라. –

+0

@eat_a_lemon : 죄송합니다. 귀하가 실제로 원하는 것을 이해하지 못하고 있습니다. 나는 구조체 멤버에 "청크 cmd를 매핑"하는 것이 무엇을 의미하는지 알지 못합니다. –

0

간단한 C99에서는 배열 인덱스가 부호없는 정수일 수 있기 때문에 불가능합니다. 그러나 적절하게 strncmp() 함수가 더 적합합니까?

#define EQUALN(a,b,n) (strncmp(a, b, n) == 0) 

... 

if(EQUALN(chunk, "SP", 2)) { 
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
     theMeas[i].signal_path = atoi(&chunk[2]); 
    } 
}  
else if(EQUALN(chunk, "TL", 2)) { 
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
     theMeas[i].trace_length = atoi(&chunk[2]); 
    } 
}  
else if(EQUALN(chunk, "SR", 2)) { 
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
     theMeas[i].sample_rate = atoi(&chunk[2]); 
    } 
} 
+0

안녕하세요 고마워, 그래도 진술은 여전히 ​​거기에있다. 나는 그들을 제거하려고 노력하고있다. –

0

항상이 세 가지 옵션 중 하나 인 경우 세 가지 경우에 대해 "최소의 완벽한 해시"를 구성 할 수 있습니다 (상당히 큰 경우).

L = 76, 0 mod 4 
P = 80, 0 mod 4 
R = 82, 2 mod 4 
S = 83, 3 mod 4 
T = 84, 0 mod 4 

그래서 S + P (3) 모드 4, T + L 0 개조 4이고, S + R 1 개조 4. 최소 아니지만 : 문자 집합을 가정 ASCII (또는 ASCII와 일치) 인

size_t lookup[3] = { 
    offsetof(Mea, trace_length), 
    offsetof(Mea, sample_rate), 
    0, 
    offsetof(Mea, signal_path) 
}; 

size_t offset = lookup[((unsigned)chunk[0] + chunk[1]) % 4]; 

for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { 
    int *fieldptr = (int*)(((char*)(theMeas+i)) + offset); 
    *fieldptr = atoi(&chunk[2]); 
} 

당신은 매크로 나 인라인 기능이 돼지에 대한 몇 가지 립스틱을 넣어 선호 할 수도, 또는 대신 int *fieldptrchar *fieldptr을 가지고 ((char*)theMeas) + offset 그것을 시작하고 sizeof(Mea) 각 시간을 증가 : 충분히 닫습니다.

친숙한 데이터에 의존 할 수 없다면 데이터가 좋지 않은 경우 아무 것도 쓰지 않으려면 어떤 종류의 분기 (조건부 호출 또는 함수 포인터를 통한 호출)가 필요합니다. 1로 유지하기 위해서조차도 3 가지 경우에 64k- 엔트리 룩업 테이블이 필요합니다. 이것은 일종의 희소성이 있기 때문에 아마도 조건을 사용하는 것이 좋습니다.