2011-11-10 4 views
1

현재 다른 사람이 만든 클라이언트 - 서버 모델을 사용하는 작은 C++ 프로젝트에서 작업하고 있습니다. 데이터가 네트워크를 통해 전송되고 제 의견으로는 잘못된 순서입니다. 그러나 그것은 내가 바꿀 수있는 것이 아닙니다.바이트 []에서 구조체로의 타입 변환

예시적인 데이터 스트림 (단순)

0x20 0x00 (C++: short with value 32) 
0x10 0x35 (C++: short with value 13584) 
0x61 0x62 0x63 0x00 (char*: abc) 
0x01 (bool: true) 
0x00 (bool: false) 

난으로이 특정 스트림을 나타낼 수

struct test { 
    short sh1; 
    short sh2; 
    char abc[4]; 
    bool bool1; 
    bool bool2; 
} 

그리고 test *t = (test*)stream;하지만, 숯 *는 가변 길이를 가지며, 그것을 캐스트 할 수있다. 그러나 항상 null로 종료됩니다.

내가 실제로 구조체에 스트림을 주조 방법이 없다는 것을 이해하지만, 내가 struct test() { test(char* data) { ... }} (생성자를 통해 변환)보다 더 나은 방법이있을 것인지 궁금

+2

Yuk, 이진 직렬화. 엔디안을 보상하십시오. –

+1

그리고'char *'는 가변 길이를 가질 수 없습니다. 항상'char * '의 크기입니다. –

+0

@Tom van der Woerdt, "abc"가 참조하는 데이터가 "Null 종료 문자열"이며 크기가 변경 될 수 있음을 의미합니까? 어리석은 질문처럼 들리지만 그게 더 낫다 ;-) – umlcat

답변

2

그냥 소요 멤버 함수를 추가 문자 버퍼 (함수 입력 매개 변수 char *)에 넣고 구문 분석하여 test 구조체를 채 웁니다.
이렇게하면 더 명확하고 읽기 쉽습니다.

암시 적 변환 생성자를 제공하는 경우 최소한 예상 할 때 변환을 수행 할 위협이 생성됩니다.

+3

'명시 적'변환 생성자가 초기화되지 않은 객체를 생성하는 것보다 낫지 만. –

+2

'explicit' 키워드가 있습니다. –

+0

동의 함. 'explicit'은 어색한 암시 적 변환으로부터 보호하지만 여전히 읽기 쉽지 않습니다. 의미있는 이름을 가진 함수는 명확하게 목적을 나타 내기 때문에 여기에 적절합니다. –

3

Marshalling 또는 serialization이라고합니다.

스트림에서 한 번에 한 바이트 씩 읽거나 (버퍼에 모두 넣어서 읽음) 구조의 구성원에 대한 충분한 데이터가 있으면 입력하십시오.

문자열에 관해서는 끝나는 0을 치기 전까지 읽은 다음 메모리를 할당하고 그 문자열을 해당 버퍼에 복사 한 다음 구조체의 포인터에 할당합니다.

문자열을이 방법으로 읽는 것은 이미 버퍼에 메시지가있는 경우 가장 간단하고 효과적입니다. 그 이유는 문자열에 대한 임시 버퍼가 필요 없기 때문입니다.

구조를 완료하면 문자열을 포함하는 메모리를 수동으로 해제해야한다는 것을 기억하십시오.

+2

이거나 그가 C++을 사용하고 있기 때문에 단순히'std :: string'을 사용할 수 있습니다 –

0

바이트 시퀀스에서 가변 길이 데이터를 읽을 때 은 모든 것을 단일 구조 또는 변수에 맞춰서는 안됩니다. 포인터는 또한이 가변 길이를 저장하는 데 사용됩니다.

다음 제안은 테스트되지 않습니다

// data is stored in memory, 
// in a different way, 
// NOT as sequence of bytes, 
// as provided 
struct data { 
    short sh1; 
    short sh2; 
    int abclength; 
    // a pointer, maybe variable in memory !!! 
    char* abc; 
    bool bool1; 
    bool bool2; 
}; 

// reads a single byte 
bool readByte(byte* MyByteBuffer) 
{ 
    // your reading code goes here, 
    // character by character, from stream, 
    // file, pipe, whatever. 
    // The result should be true if not error, 
    // false if cannot rea anymore 
} 

// used for reading several variables, 
// with different sizes in bytes 
int readBuffer(byte* Buffer, int BufferSize) 
{ 
    int RealCount = 0; 

    byte* p = Buffer; 

    while (readByte(p) && RealCount <= BufferSize) 
    { 
     RealCount++ 
     p++; 
    } 

    return RealCount; 
} 

void read() 
{ 
    // real data here: 
    data Mydata; 

    byte MyByte = 0; 

    // long enough, used to read temporally, the variable string 
    char temp[64000]; 
    // fill buffer for string with null values 
    memset(temp, '\0', 64000); 

    int RealCount = 0; 

    // try read "sh1" field 
    RealCount = (readBuffer(&(MyData.sh1), sizeof(short))); 
    if (RealCount == sizeof(short)) 
    { 
     // try read "sh2" field 
     RealCount = readBuffer(&(MyData.sh2), sizeof(short));  
     if (RealCount == sizeof(short)) 
     { 
      RealCount = readBuffer(temp, 64000); 
      if (RealCount > 0) 
      { 
       // store real bytes count 
       MyData.abclength = RealCount; 
       // allocate dynamic memory block for variable length data 
       MyData.abc = malloc(RealCount); 
       // copy data from temporal buffer into data structure plus pointer 
       // arrays in "plain c" or "c++" doesn't require the "&" operator for address: 
       memcpy(MyData.abc, temp, RealCount); 

       // comented should be read as: 
       //memcpy(&MyData.abc, &temp, RealCount); 

       // continue with rest of data 
       RealCount = readBuffer(&(MyData.bool1), sizeof(bool)); 
       if (RealCount > 0) 
       { 
        // continue with rest of data 
        RealCount = readBuffer(&(MyData.bool2), sizeof(bool)); 
       } 
      } 
     } 
    } 
} // void read() 

건배.