다양한 플랫폼을 지원하도록 코딩하는것이 쉽지가 않네요.

superkkt의 이미지

우선 저는 리눅스 / C 환경에서 개발을 주로 하기 때문에 윈도우는 예외로 하겠습니다. 제가 말하는 다양한 플랫폼이라면 일반적인 의미보다 좀 더 작은 개념으로, 리눅스와 여러 유닉스들을 지원하는 정도가 되겠습니다. 일단 저는 코딩을 할 때 아래 사이트를 참조합니다. 저는 이 사이트가 POSIX 표준에 관한 레퍼런스라고 생각하고 있는데 혹시 잘못 알고 있다면 지적 부탁드립니다.

http://www.opengroup.org/onlinepubs/000095399/toc.htm

현재 백업 소프트웨어를 개발중인데 처음부터 여러 플랫폼을 지원하는 계획은 없었습니다. 다만 저 혼자 생각에 POSIX 표준 함수들만 사용해서 만들고, 프로토콜이 특정 OS 또는 컴파일러에 종속되지 않도록만 만들면 리눅스와 다른 유닉스에서는 자연스럽게 돌아갈거라고 생각했었습니다. 그래서 실제로 다른 플랫폼에서도 돌아가는지 테스트하기 위해, 주 개발머신과 다른 CPU, 다른 컴파일러를 사용해서 테스트를 해봤습니다. 솔라리스 9 SPARC(Sun Studio 11) 환경에서 컴파일하고 돌려봤는데.. 생각만큼 잘 되지는 않더군요. :-(

일단 제일 처음 삽질을 했던것이 snprintf의 리턴값이였습니다. 문자열을 저장하는데 필요한 메모리 사이즈를 계산하기 위해 snprintf의 인자 중 버퍼 사이즈를 0으로해서 실행하고, 리턴받은 값만큼 메모리를 할당하는 루틴이 있습니다. 이 부분과 관련해서 위 POSIX 레퍼런스 사이트에는 아래와 같이 적혀 있습니다.

int snprintf(char *restrict s, size_t n, const char *restrict format, ...);
 
If the value of n is zero on a call to snprintf(), nothing shall be written, the number of bytes that would have been written had n been sufficiently large excluding the terminating null shall be returned, and s may be a null pointer.

그래서 아래와 같이 사용을 했습니다.

len = snprintf(NULL, 0, "%s %s", arg1, arg2);

이 코드가 리눅스 (gcc) 환경에서는 원하는대로 잘 작동을 했는데, 솔라리스 (Sun Studio) 환경에서는 -1이 리턴이 되더군요. 한참 삽질 후에 아래와 같이 하면 되는걸 발견했습니다.

char buf[1];
 
len = snprintf(buf, sizeof(buf), "%s %s", arg1, arg2);

이건 왜 그런걸까요? 제가 참고하는 사이트가 POSIX 표준에 관한것이 아닌걸까요? 아니면 제가 레퍼런스 해석을 잘못한건가요? 아니면 Sun Studio가 POSIX를 따르지 않는것일까요?

hayarobi의 이미지

Concerning the return value of snprintf(), SUSv2 and C99 contradict
each other: when snprintf() is called with size=0 then SUSv2 stipulates
an unspecified return value less than 1, while C99 allows str to be
NULL in this case, and gives the return value (as always) as the number
of characters that would have been written in case the output string
has been large enough.

이렇게 나오네요. 혹시 컴파일러에 C규약 설정하는 부분이 없을까요?

---------- 시그 *****
저도 세벌식을 씁니다.
M$윈도우즈, 리눅스, 맥오에스텐, 맥오에스클래식을 모두 엔드유저 수준으로 쓴답니다.
http://psg9.egloos.com

=================
잠못자는 한솔아빠

superkkt의 이미지

음.. 그럼 제가 보고있는 레퍼런스가 C99을 기준으로 작성된걸까요? 솔라리스에서 컴파일할때 C99을 사용하도록 시도해 봐야겠군요.

======================
BLOG : http://superkkt.com

======================
BLOG : http://superkkt.com

전웅의 이미지

snprintf 의 반환값은 C99 표준에서 기존 구현과 차이를 갖도록 정의된
유명한 부분 중 하나입니다 - 기존 *printf 함수군이 갖고 있던 귀찮은
결점을 해결했다는 점에서 바람직하다고 봅니다.

그나저나 다양한 *nix 환경을 지원하고 싶으시다면서 굳이 C99 에 의존
하려 하시는 이유를 모르겠습니다. C90 과는 달리 C99 는 아직 온전히
지원되지 않는 환경이 많습니다.

생성될 문자열 길이를 미리 예측하기 어려워 snprintf 를 사용해 측정
하고자 하신다면, 표준 버전에 따라 조건부 컴파일이 되도록 만든 후에
/dec/null 로 fprintf 를 하여 그 반환값을 검사해 이후 과정에 사용
하는 방법도 있습니다.

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

superkkt의 이미지

/dev/null로 하는 기가막힌 방법이 있었군요. :-) 그리고 이식성을 위해 C99 표준에 의존했다기 보다는 POSIX 표준(위 링크 사이트)에 의존했다고 할 수 있는데, POSIX == C99 이라면 그 말이 그 말이군요. :-(

답변 감사합니다.

======================
BLOG : http://superkkt.com

======================
BLOG : http://superkkt.com

hayarobi의 이미지

저도 한수 잘 배웁니다.^^

---------- 시그 *****
저도 세벌식을 씁니다.
M$윈도우즈, 리눅스, 맥오에스텐, 맥오에스클래식을 모두 엔드유저 수준으로 쓴답니다.
http://psg9.egloos.com

=================
잠못자는 한솔아빠

winner의 이미지

/dec/null --- /dev/null

확실히 restrict 가 들어가는 것을 보니 C99 이군요.

그런데 Windows 를 배제한 상태에서 fprintf 를 쓴다면 조건부 compile 이 필요없지 않나요?

전웅의 이미지

> 그런데 Windows 를 배제한 상태에서 fprintf 를 쓴다면 조건부 compile 이
> 필요없지 않나요?
>

트릭이 주어지면 쓰는 건 사용하는 사람 맘대로죠 뭐. :-)

제가 글을 쓸 때 생각했던 방법은 아래와 같은 방법이었습니다. (글
읽으시는 분들이 필요에 따라 더 기발한 방법으로 사용하시리라
믿습니다.)

/* mysnprintf.c */
 
#include <stdio.h>
#include <stdarg.h>
 
#if __STDC_VERSION__ < 199901L
#undef snprintf
 
int snprintf(char *s, size_t n, const char *f, ...)
{
    static FILE *nulldev;
    va_list arg;
 
    va_start(arg, f);
 
    if (n == 0) {
        if (nulldev == NULL &&
            (nulldev = fopen("/dev/null", "w")) == NULL)
            n = -1;
        else
            n = vfprintf(nulldev, f, arg);
    } else
        n = vsnprintf(s, n, f, arg);
 
    va_end(arg);
 
    return n;
}
#endif
 
/* end of mysnprintf.c */

사용할 수 있는 상황은, snprintf 를 지원하긴 하지만 n == 0 일 때의
행동이 C99 와 맞지 않는 경우입니다.

이 코드를 놓고 표준 관점에서 따지자면 딴지걸 부분이 없진 않지만,
"실용적"으로는 무난한 방식입니다. __STDC_VERSION__ 빼놓고는 예약된
명칭과 관련된 문제도 없구요.

테스트하지 않은 코드이므로 문제점이나 기타 필요에 따른 수정은 알아서
해주세요. --;

* 회식하고 와서 소스 조금 수정합니다. ^^

--
Jun, Woong (woong at icu.ac.kr)
Web: http://www.woong.org (서버 공사중)

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

cmoh1110의 이미지

궁금한 것이 있는데요..
문자열 길이를 계산하려고 할 때 굳이 snprintf()같이 덩치가 큰(?)
함수를 써야만 하는 이유가 있나요?
strlen()만으로는 부족한 뭔가가 있는 것인가요?

superkkt의 이미지

가변인자를 처리하기 위해서 입니다. 포맷 스트링과 가변인자를 받아서 그것을 문자열로 구성하는데 필요한 사이즈만큼 메모리를 할당해서 돌려주는 함수입니다. 아래와 같이 사용하죠.

str = dup_str("%s: %s(%d)", name, msg, size);

======================
BLOG : http://superkkt.com

======================
BLOG : http://superkkt.com

bemore의 이미지


심지어 윈도우즈98/NT/2000/2003/XP/Vista 간에도
크로스플랫폼 코딩은 어렵습니다.

---------------------------
심장은 암에 걸리지 않는다.
MacMini CoreDuo, Tiger/SuSE

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.