뭉근 : 느긋하게 타는 불

ifstream 64

프로그래밍2014. 9. 25. 02:40

2년 전 쯤에 대용량 파일(2GB 이상의 파일) 처리가 가능한 프로그램을 만들어야 했다. 32비트 시스템에서는 4바이트 크기의 파일 포인터를 사용하기에 이런 파일에 대해 입출력 작업을 할 때 윈도우 API를 제외한 일반적인 방법으로 처리하기가 힘들었다. 지금이야 윈도우 API의 강력함을 알고 있지만서도 그 때 당시 크로스컴파일에 심취해있었기 때문에 윈도우 API로 파일 처리하는 것은 내 자존심이 허락하지 않았다.

직접 알아본 파일 처리 방법은 64비트 환경에서 컴파일 그리고 _fseeki64 류의 함수를 사용, fstream을 8바이트 파일 포인터를 지원하게 일반화 프로그래밍을 하는 것이었다. 급한데로 _fseeki64 류의 함수를 사용하여 잘 처리하였지만 코딩에 미련이 안남을 수 있겠는가? 시간이 남을 때 64비트 파일 포인터를 지원하는 fstream을 만드는 방법을 찾아보았다. 아래는 fstream64를 지원하는 코드이다.

   

#ifdef WIN32

typedef int64_t streamoff64;

class streampos64
{
        typedef streampos64 _Myt;
        typedef _Mbstatet _Statetype;
public:
        streampos64(streamoff64 _Off = 0)
                : _Myoff(_Off), _Fpos(0), _Mystate(0)
                {        // construct with stream offset
                }

streampos64(_Statetype _State, fpos_t _Fileposition)
                : _Myoff(0), _Fpos(_Fileposition), _Mystate(_State)
                {        // construct with conversion state and C file position
                }

_Statetype state() const
                {        // return conversion state
                return (_Mystate);
                }

   

직접 코드를 가져다 사용하면 C4927 에러가 나는데… 이게 여간 신경 쓰이는게 아니다.

   

c:\program files (x86)\microsoft visual studio 10.0\vc\include\streambuf(424): warning C4927: illegal conversion; more than one user-defined conversion has been implicitly applied

1> while calling the constructor 'streampos64::streampos64(streamoff64)'

1> c:\~\streamutil.h(15) : see declaration of 'streampos64::streampos64'

1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\streambuf(423) : while compiling class template member function 'streampos64 std::basic_streambuf<_Elem,_Traits>::seekoff(unsigned __int64,std::ios_base::seekdir,std::ios_base::openmode)'

1> with

1> [

1> _Elem=char,

1> _Traits=char_traits64<char>

1> ]

1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\fstream(135) : see reference to class template instantiation 'std::basic_streambuf<_Elem,_Traits>' being compiled

1> with

1> [

1> _Elem=char,

1> _Traits=char_traits64<char>

1> ]

1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\fstream(889) : see reference to class template instantiation 'std::basic_filebuf<_Elem,_Traits>' being compiled

1> with

1> [

1> _Elem=char,

1> _Traits=char_traits64<char>

1> ]

1> c:\~~~~~\header.h(63) : see reference to class template instantiation 'std::basic_ifstream<_Elem,_Traits>' being compiled

(생략)

   

C4927 에러는 warning 1단계 에러로 무진장 잘나온다. 한국어 버전은 어떻게 나오는지 모르겠지만 해석해보면 네가 정의한 데이터에 대해 암묵적으로 데이터 변환이 일어났는데 이를 해결 안하면 어떤 문제가 생길 수도 있다는 것이다. 이걸 한번 해결해보는게 바로 이번 포스트의 내용이다.

CPP에서 템플릿을 사용하는 함수나 클래스는 컴파일 시간에 그 타입이 결정된다. VC는 이에 대한 로그를 스택 형식으로 보여주기 때문에 관련 로그를 살펴보기 위해서는 맨 아래부터 살펴보면 아래 15번째 줄의 코드 라인부터 컴파일이 일어났고 이에 대한 워닝이 떴다는 것을 알 수 있다.

   

   

다음은 basic_ifstream에서 발생하는데 typedef basic_filebuf<_Elem, _Traits> _Myfb에서 워닝이 발생한다. 내용은 _Filebuffer를 만드는데 여기서 워닝이 발생한다~ 그리고 이 변수는 basic_filebuf에 속해있다 이다.

   

   

   

이제 실제 워닝 발생 지점이다.

   

   

ifstream64은 다음과 같이 아래의 소스 코드에 나와있듯이 basic_ifstream에 출력 문자열의 형태와 관리 방법을 정의하는 객체인 _Traits를 새롭게 정의한다. 여기서 주목해야될 것은 바로 pos_type과 off_type이다.

   

template<typename Elem>

class char_traits64 : public std::char_traits<Elem>

{

public:

typedef Elem char_type;

typedef long int_type;

typedef streampos64 pos_type;

typedef streamoff64 off_type;

typedef _Mbstatet state_type;

};

   

typedef std::basic_ifstream<char, char_traits64<char> > ifstream64;

   

일반적으로 ifstream에서 사용되는 char_traits는 아래와 같이 streampos와 streamoff 를 사용한다. 여기서 사용되는 streampos는 fpos<_Mbstatet>이며 streamoff는 32비트 정수형이다. ( * 64비트 환경에서는 64비트 정수형이다. )

   

   

소스코드에서 streamoff64는 uint64_t로 64비트 정수형이다. streampos64는 fpos<_Mbstatet>를 64비트에 맞춰서 새롭게 정의를 했다. 워닝은 바로 이 부분에서 발생하는 것이다. streampos64를 다시 살펴보면 생성자 부분에서 워닝이 발생한다는 것을 알 수 있다. 그렇다면 생성자에서 왜 워닝이 발생하는건가?

   

   

seekoff 함수의 return 문을 보면 streampos(~)를 반환한다. 하지만 return 데이터 형식은 pos_type으로 컴파일시에는 streampos64로 결정된다. 바로 여기에서 컴파일러는 데이터 형식이 정수형으로 비슷하지만 클래스에서 어떻게 처리할지 모르기 때문에 암묵적으로 데이터 변환이 일어나고 있다고 말해주는 것이다.

   

   

이를 해결하는 방법은 streampos에 해당하는 새로운 생성자를 만들어주면 워닝이 금방 해결 된다.

   

public:

streampos64(streamoff64 _Off = 0)

: _Myoff(_Off), _Fpos(0), _Mystate(0)

{        // construct with stream offset

}

   

streampos64(std::streampos _Off)

: _Myoff(_Off), _Fpos(0), _Mystate(0)

{        // construct with stream offset

}

   

fstream64를 사용하면서 선언한 곳마다 warning이 20~30줄 떠버리면 무진장 신경쓰인다. 거기다 왠지 모르게 C4927 워닝은 무시도 안된다. 이상 마친다~

   

그런데 64비트 환경에서 32비트로 컴파일하면 파일 포인터는 32비트일까 64비트일까? -_-??? 흠..

   

ps. boost::filesystem::fstream 도 있네? 봐봐야겠다.