はじめに
@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()
で括らなければならないというデメリットが新たに生じており、結果として便利になったかは微妙な感じです。
おわりに
おわりです。