2012-02-02 3 views
1

사용자 제공 char*에 쓰는 대신 std :: string을 반환하여 자동 할당 sprintf를 수행하는 함수를 작성했습니다. (IOSTREAMS 또는 Boost.Format이나 친구를 추천 어떤 답변을 바랍니다 -. 나는 다른 상황에서 그들을 사용합니까, 그들이 존재 알고,하지만 요구 사항이 특정 사건에 대한이)std :: string 및 stdarg.h

std::string FormatString(const std::string& format, va_list argList) 
{ 
    char smallBuffer[500], *text = smallBuffer; 
    int length = _countof(smallBuffer); 

    // MSVC is not C99 conformant, so its vsnprintf returns -1 
    // on insufficient buffer space 
    int outputSize = _vsnprintf(text, length, format.c_str(), argList); 
    while (outputSize < 0 && errno == ERANGE && length > 0) 
    { 
     length <<= 1; 
     if (text != smallBuffer) { delete[] text; } 
     text = new char[length]; 
     outputSize = _vsnprintf(text, length, format.c_str(), argList); 
    } 
    if (outputSize < 0) 
    { 
     throw std::runtime_error("Failed to format string."); 
    } 

    std::string ret(text); 
    if (text != smallBuffer) 
    { 
     delete[] text; 
    } 
    return ret; 
} 

std::string FormatString(const std::string& format, ...) 
{ 
    va_list argList; 
    va_start(argList, format); 

    std::string result; 
    try 
    { 
     result = FormatString(format, argList); 
    } 
    catch(...) 
    { 
     va_end(argList); 
     throw; 
    } 
    va_end(argList); 

    return result; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int foo = 1234; 
    std::string bar = "BlaBla"; 
    std::cout << FormatString("%i (%s)", foo, bar.c_str()) << std::endl; 
    return 0; 
} 

을 (그리고 네, C++ 형식의 문자열을 C++ 형식의 문자열로 파이프하는 것의 아이러니가 있습니다. 이것은 단지 테스트 코드 일뿐입니다.)

불행히도 VS2008을 사용하면 잘못된 인수를 읽었 기 때문에 printf 내부의 내부에서 심하게 부숴졌습니다. va_list (디버거에 따르면 va_start 다음에 "실제"첫 번째 매개 변수 바로 앞의 4 바이트 null 시퀀스를 가리킴).

특히 variadic 함수에서 const std::string& format을 단지 std::string format (즉, 값으로 전달)으로 변경하면 올바르게 작동합니다. 물론 그것은 const char *으로 변경하면됩니다.

이것은 일종의 컴파일러 버그입니까, 아니면 참조 매개 변수와 함께 va_list를 사용하는 것이 합법적이지 않습니까?

+0

[참조 매개 변수와 함께 varargs를 사용하는 문제가 있습니까?] (http://stackoverflow.com/questions/222195/are-there-gotchas-using-varargs-with-reference-parameters). –

+0

감사합니다. 거기에도 좋은 설명이 있습니다. 부끄러움이 내 사전 쿼리 검색에서 나오지 않았다. :) – Miral

답변

1

참조를 전달하려는 경우 운이 좋지 않을 것으로 생각됩니다. 다음은 C++ 2011 표준 18.10의 주제에 대해 말해야하는 것입니다 [support.runtime] 제 3 항 :

헤더에있는 va_start를() 매크로 두 번째 매개 변수에 ISO C 장소는 제한을 이 국제 표준과 다른 매개 변수 parmN은 함수 정의의 변수 매개 변수 목록 (... 직전의 매개 변수 목록)에서 가장 오른쪽 매개 변수의 식별자입니다. 230 매개 변수 parmN이 함수, 배열 또는 참조 유형으로 선언되거나 형식이 매개 변수가없는 인수를 전달할 때 발생하는 형식과 호환되지 않는 경우이 동작은 정의되지 않습니다.

+0

고마워, 그게 내가 의심하는거야. "매개 변수가없는 인수를 전달할 때 발생하는 형식"이 무엇인지는 확실하지 않지만. – Miral

+0

나는 이것이 매개 변수 이름이없는 인수, 즉 변수 인수 목록에 전달 된 인수를 참조한다고 생각합니다. 타입 변환이 있기 때문에 (예 :'float'에서'double')이 것은 본질적으로 가변 길이 인수 목록을 통과 할 수없는 것을 전달하려는 경우를 참조합니다. –

관련 문제