문제

저는 C++로 구현된 오픈 소스 UNIX 도구를 사용하여 작업하고 있는데, 원하는 작업을 수행하려면 일부 코드를 변경해야 합니다.내 패치가 업스트림에 승인될 수 있도록 가능한 최소한의 변경을 하고 싶습니다.표준 C++로 구현 가능하고 더 많은 외부 종속성을 생성하지 않는 솔루션이 선호됩니다.

여기에 내 문제가 있습니다.저는 현재 fprintf()를 사용하여 매우 형식화된 데이터 구조를 파일 포인터에 인쇄하는 C++ 클래스("A"라고 부르겠습니다)가 있습니다.인쇄 기능에서는 여러 멤버 클래스의 동일하게 정의된 인쇄 기능을 반복적으로 호출합니다(예: "B").A 인스턴스의 print() 결과로 설정되어야 하는 std::string "foo" 멤버가 있는 또 다른 클래스 C가 있습니다.A의 to_str() 멤버 함수로 생각하세요.

의사코드에서:

class A {
public:
  ...

  void print(FILE* f);
  B b;

  ...  
};

...

void A::print(FILE *f)
{
  std::string s = "stuff";
  fprintf(f, "some %s", s);
  b.print(f);
}

class C {
  ...
  std::string foo;
  bool set_foo(std::str);
  ...
}

...

A a = new A();
C c = new C();

...

// wish i knew how to write A's to_str()
c.set_foo(a.to_str());

C는 상당히 안정적이지만 A와 B(및 A의 나머지 종속 항목)는 유동적 상태에 있으므로 필요한 코드 변경이 적을수록 좋습니다.현재 인쇄(FILE* F) 인터페이스도 보존해야 합니다.나는 A::to_str()을 구현하는 여러 가지 접근 방식을 고려했는데 각각 장점과 단점이 있습니다.

  1. fprintf() 호출을 sprintf()로 변경합니다.

    • 형식 문자열을 다시 작성할 필요가 없습니다.
    • print()는 다음과 같이 다시 구현될 수 있습니다:fprint(f, this.to_str());
    • 하지만 수동으로 char[]을 할당하고 많은 c 문자열을 병합한 다음 마지막으로 문자 배열을 std::string으로 변환해야 합니다.
  2. 문자열 스트림에서 a.print()의 결과를 포착해 보세요.

    • 모든 형식 문자열을 << 출력 형식으로 변환해야 합니다.변환할 fprintf()가 수백 개 있습니다. :-{
    • UNIX 파일 핸들에서 출력 스트림을 생성하는 표준 방법이 없기 때문에 print()를 다시 작성해야 합니다. 그 사람은 그럴 수도 있다고 하던데).
  3. Boost의 문자열 사용 형식 라이브러리

    • 더 많은 외부 종속성.왝.
    • Format의 구문은 짜증날 만큼 printf()와 다릅니다.

    printf(format_str, args) -> cout << Boost::format(format_str) % arg1 % arg2 % 등

  4. Qt를 사용하세요 QString::asprintf()

    • 다른 외부 종속성.

그렇다면 가능한 모든 옵션을 다 써버린 걸까요?그렇다면, 어떤 것이 최선의 선택이라고 생각하시나요?그렇지 않다면 내가 무엇을 간과했는가?

감사해요.

도움이 되었습니까?

해결책

나는 #3을 사용하고 있습니다:부스트 문자열 형식 라이브러리 - 하지만 형식 사양의 차이로 인해 어떤 문제도 발생한 적이 없다는 점을 인정해야 합니다.

나에게는 매력적으로 작동하며 외부 종속성은 더 나쁠 수 있습니다(매우 안정적인 라이브러리).

편집됨:printf 대신 Boost::format을 사용하는 방법에 대한 예제를 추가합니다.

sprintf(buffer, "This is a string with some %s and %d numbers", "strings", 42);

Boost::format 라이브러리를 사용하면 다음과 같습니다.

string = boost::str(boost::format("This is a string with some %s and %d numbers") %"strings" %42);

이것이 Boost::format의 사용법을 명확히 하는 데 도움이 되기를 바랍니다.

저는 4~5개의 응용 프로그램(파일에 형식화된 문자열 작성 또는 로그 파일에 사용자 정의 출력 작성)에서 sprintf/printf 대체 방법으로 Boost::format을 사용했으며 형식 차이로 인해 문제가 발생한 적이 없습니다.다르게 나타나는 일부(다소 모호한) 형식 지정자가 있을 수 있지만 문제는 발생하지 않았습니다.

대조적으로 나는 (내가 기억하는 한) 스트림으로는 실제로 할 수 없는 몇 가지 형식 사양을 가지고 있었습니다.

다른 팁

다음은 기능을 'sprintf'와 동일하게 만들면서 std::string을 반환하고 버퍼 오버플로 문제에 영향을 받지 않는 관용구입니다.이 코드는 제가 작성 중인 오픈 소스 프로젝트(BSD 라이센스)의 일부이므로 누구나 원하는 대로 자유롭게 사용할 수 있습니다.

#include <string>
#include <cstdarg>
#include <vector>
#include <string>

std::string
format (const char *fmt, ...)
{
    va_list ap;
    va_start (ap, fmt);
    std::string buf = vformat (fmt, ap);
    va_end (ap);
    return buf;
}



std::string
vformat (const char *fmt, va_list ap)
{
    // Allocate a buffer on the stack that's big enough for us almost
    // all the time.
    size_t size = 1024;
    char buf[size];

    // Try to vsnprintf into our buffer.
    va_list apcopy;
    va_copy (apcopy, ap);
    int needed = vsnprintf (&buf[0], size, fmt, ap);
    // NB. On Windows, vsnprintf returns -1 if the string didn't fit the
    // buffer.  On Linux & OSX, it returns the length it would have needed.

    if (needed <= size && needed >= 0) {
        // It fit fine the first time, we're done.
        return std::string (&buf[0]);
    } else {
        // vsnprintf reported that it wanted to write more characters
        // than we allotted.  So do a malloc of the right size and try again.
        // This doesn't happen very often if we chose our initial size
        // well.
        std::vector <char> buf;
        size = needed;
        buf.resize (size);
        needed = vsnprintf (&buf[0], size, fmt, apcopy);
        return std::string (&buf[0]);
    }
}

편집하다:이 코드를 작성할 때 C99 준수가 필요하다는 것과 Windows(이전 glibc도 포함)에 필요한 공간의 양을 결정하는 척도가 아니라 실패 시 -1을 반환하는 다른 vsnprintf 동작이 있다는 사실을 전혀 몰랐습니다. .여기에 제가 수정한 코드가 있습니다. 모두가 살펴보고 괜찮다고 생각하시면 해당 비용만 나열되도록 다시 편집하겠습니다.

std::string
Strutil::vformat (const char *fmt, va_list ap)
{
    // Allocate a buffer on the stack that's big enough for us almost
    // all the time.  Be prepared to allocate dynamically if it doesn't fit.
    size_t size = 1024;
    char stackbuf[1024];
    std::vector<char> dynamicbuf;
    char *buf = &stackbuf[0];
    va_list ap_copy;

    while (1) {
        // Try to vsnprintf into our buffer.
        va_copy(ap_copy, ap);
        int needed = vsnprintf (buf, size, fmt, ap);
        va_end(ap_copy);

        // NB. C99 (which modern Linux and OS X follow) says vsnprintf
        // failure returns the length it would have needed.  But older
        // glibc and current Windows return -1 for failure, i.e., not
        // telling us how much was needed.

        if (needed <= (int)size && needed >= 0) {
            // It fit fine so we're done.
            return std::string (buf, (size_t) needed);
        }

        // vsnprintf reported that it wanted to write more characters
        // than we allotted.  So try again using a dynamic buffer.  This
        // doesn't happen very often if we chose our initial size well.
        size = (needed > 0) ? (needed+1) : (size*2);
        dynamicbuf.resize (size);
        buf = &dynamicbuf[0];
    }
}

setw() 호출 및 iomanip의 기타 호출과 같은 형식화와 함께 std::string 및 iostreams를 사용할 수 있습니다.

다음은 대체 솔루션일 수 있습니다.

void A::printto(ostream outputstream) {
    char buffer[100];
    string s = "stuff";
    sprintf(buffer, "some %s", s);
    outputstream << buffer << endl;
    b.printto(outputstream);
}

(B::printto 유사) 정의하고

void A::print(FILE *f) {
    printto(ofstream(f));
}

string A::to_str() {
    ostringstream os;
    printto(os);
    return os.str();
}

물론 버퍼 오버플로를 방지하려면 sprintf 대신 snprintf를 사용해야 합니다.더 위험한 sprintfs를 선택적으로 << 형식으로 변경하여 더 안전하면서도 최소한으로 변경할 수도 있습니다.

Loki 라이브러리의 SafeFormat 헤더 파일(http://loki-lib.sourceforge.net/index.php?n=Idioms.Printf).Boost의 문자열 형식 라이브러리와 유사하지만 printf(...) 함수의 구문을 유지합니다.

이게 도움이 되길 바란다!

직렬화에 관한 것입니까?아니면 인쇄가 제대로 되나요?전자라면 Boost::serialization도 고려해보세요.이는 객체와 하위 객체의 "재귀적" 직렬화에 관한 것입니다.

{fmt} 라이브러리 제공하다 fmt::sprintf 수행하는 기능 printf-호환 가능한 형식 지정(다음에 따른 위치 인수 포함) POSIX 사양) 결과를 다음과 같이 반환합니다. std::string:

std::string s = fmt::sprintf("The answer is %d.", 42);

부인 성명:나는 이 도서관의 저자입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top