7
2

More than 1 year has passed since last update.

C言語のprintfにGoの%v相当のものが実装できないか実験してみた

Posted at

はじめに

@shiozaki さんの投稿された記事『C言語のprintfにはGoの%v相当のものが存在しない理由』を拝見し、「Cでもそういうことができたら便利になるのかな?」と思い雑に実験してみました。

書いてみたコード

printfの代わりとなる関数myprintfを作成しました。実験用の手抜き実装なのでprintfの機能の多くを含んでおりません。
C11で追加された機能である_Genericを使用しています。

myprintf.h
#ifndef MYPRINTF_H
#define MYPRINTF_H

enum {TypeChr, TypeInt, TypeLng, TypeDbl, TypeStr};
typedef struct {
    int t;
    union {
        char c;
        int i;
        long l;
        double d;
        const char* s;
    } u;
} any_t;

#define ANY(x) _Generic((x) \
    , char:        AnyChr \
    , int:         AnyInt \
    , long:        AnyLng \
    , double:      AnyDbl \
    , char*:       AnyStr \
    , const char*: AnyStr \
)(x)

any_t AnyChr(char x);
any_t AnyInt(int x);
any_t AnyLng(long x);
any_t AnyDbl(double x);
any_t AnyStr(const char*  x);
int myprintf(const char* f, ...);

#endif/*MYPRINTF_H*/
myprintf.c
#include "myprintf.h"
#include <stdio.h>
#include <stdarg.h>

any_t AnyChr(char x)
{
    return (any_t){.t = TypeChr, .u.c = x};
}

any_t AnyInt(int x)
{
    return (any_t){.t = TypeInt, .u.i = x};
}

any_t AnyLng(long x)
{
    return (any_t){.t = TypeLng, .u.l = x};
}

any_t AnyDbl(double x)
{
    return (any_t){.t = TypeDbl, .u.d = x};
}

any_t AnyStr(const char* x)
{
    return (any_t){.t = TypeStr, .u.s = x};
}

int myprintf(const char* f, ...)
{
    va_list ap;
    va_start(ap, f);
    int l = 0;
    int c;
    while ((c = *f++) != '\0') {
        if (c == '%') {
            if (f[0] == '%') {
                l += printf("%%");
                f++;
            } else if (f[0] == 'c') {
                l += printf("%c", va_arg(ap, int));
                f++;
            } else if (f[0] == 'd') {
                l += printf("%d", va_arg(ap, int));
                f++;
            } else if (f[0] == 'l' && f[1] == 'd') {
                l += printf("%ld", va_arg(ap, long));
                f += 2;
            } else if (f[0] == 'f') {
                l += printf("%f", va_arg(ap, double));
                f++;
            } else if (f[0] == 's') {
                l += printf("%s", va_arg(ap, const char*));
                f++;
            } else if (f[0] == 'v') {
                any_t a = va_arg(ap, any_t);
                switch (a.t) {
                case TypeChr:
                    l += printf("%c", a.u.c);
                    break;
                case TypeInt:
                    l += printf("%d", a.u.i);
                    break;
                case TypeLng:
                    l += printf("%ld", a.u.l);
                    break;
                case TypeDbl:
                    l += printf("%f", a.u.d);
                    break;
                case TypeStr:
                    l += printf("%s", a.u.s);
                    break;
                }
                f++;
            }
        } else {
            putchar(c);
            l++;
        }
    }
    va_end(ap);
    return l;
}

使い方

第1引数の書式に%c %d %ld %f %s %v のみ使用できます。
%c %d %ld %f %sでそれぞれ文字、整数、大きい整数、浮動小数点数、文字列を出力できます。

#include "myprintf.h"

int main(void)
{
    char c = 'C';
    int i = 12345;
    long l = 123456789012345L;
    double d = 1.234567;
    const char* s = "hogehoge";

    myprintf("%c\n", c);
    myprintf("%d\n", i);
    myprintf("%ld\n", l);
    myprintf("%f\n", d);
    myprintf("%s\n", s);
}
実行結果
C
12345
123456789012345
1.234567
hogehoge

%vで文字、整数、大きい整数、浮動小数点数、文字列を出力できます。出力したい引数はANY()で括る必要があります(ウワダセェ)。

#include "myprintf.h"

int main(void)
{
    char c = 'C';
    int i = 12345;
    long l = 123456789012345L;
    double d = 1.234567;
    const char* s = "hogehoge";

    myprintf("%v\n", ANY(c));
    myprintf("%v\n", ANY(i));
    myprintf("%v\n", ANY(l));
    myprintf("%v\n", ANY(d));
    myprintf("%v\n", ANY(s));
}
実行結果
C
12345
123456789012345
1.234567
hogehoge

所感

%vを使用することで第1引数の書式と第2引数以降の型を合わせることに気を払う必要がなくなることはメリットとしてありますが、ANY()で括らなければならないというデメリットが新たに生じており、結果として便利になったかは微妙な感じです。

おわりに

おわりです。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2