От безысходности можно и C++. Но не представляю как. Задача такова: чтоб в функцию аналогичную printf() в строку формата не подсунули указатель на RAM. Или хотелось бы в runtime отличать (char*) и (char*) указатели приведённые к (const char*). Для -- см. ниже, какую хреновину изобрёл ((C) мой, получение патентов ожидается...):
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <wchar.h>
#include <locale.h>
uint_fast8_t ptr_is_const(const void *p)
{
return 1;
}
enum e_types {
T_ERROR=0,
T_INT, T_LONG,T_DOUBLE,
#ifndef __NO_LONG_LONG
T_LLONG,
#endif
#ifndef __NO_LONG_DOUBLE_MATH
T_LDOUBLE,
#endif
T_PVOID, T_INTMAX, T_PTRDIFF, T_SIZE,
T_PCHAR, T_WCHAR, T_PWCHAR,
T_STRERR, T_NCHARS
};
static enum e_types parse_type(const char **f)
{
enum {
_none=0,
_h, _hh, _l, _ll, _L, _q, _j, _z, _t
} mod = 0;
while (*++*f!=0) {
switch (**f) {
/* flag chars, filled width and precision */
case '#': case '-': case '+': case ' ': case '\'':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '*': case '$': case '.':
continue;
/* length modifiers */
case 'h': switch (mod) {
case 0: mod=_h; continue;
case _h: mod=_hh; continue;
default: return T_ERROR;
}
case 'l': switch (mod) {
case 0: mod=_l; continue;
case _l: mod=_ll; continue;
default: return T_ERROR;
}
case 'L': if (mod) return T_ERROR;
mod=_L; continue;
case 'q': if (mod) return T_ERROR;
mod=_q; continue;
case 'j': if (mod) return T_ERROR;
mod=_j; continue;
case 'z': case 'Z':
if (mod) return T_ERROR;
mod=_z; continue;
case 't': if (mod) return T_ERROR;
mod=_t; continue;
/* type argument */
case 'd': case 'i':
case 'o': case 'u': case 'x': case 'X':
switch (mod) {
case 0: case _h: case _hh: return T_INT;
case _l: return T_LONG;
#ifndef __NO_LONG_LONG
case _ll: case _q: return T_LLONG;
#endif
case _j: return T_INTMAX;
case _z: return T_SIZE;
case _t: return T_PTRDIFF;
default: return T_ERROR; /* not recognized */
}
case 'p': if (!mod) return T_PVOID; else return T_ERROR;
case 'e': case 'E': case 'f': case 'F':
case 'g': case 'G': case 'a': case 'A':
switch (mod) {
case 0: return T_DOUBLE;
#ifndef __NO_LONG_DOUBLE_MATH
case _L: return T_LDOUBLE;
#endif
default: return T_ERROR;
}
case 'c': switch (mod) {
case 0: return T_INT;
case _l: return T_WCHAR;
default: return T_ERROR;
}
case 'C': if (!mod) return T_WCHAR; else return T_ERROR;
case 's': switch (mod) {
case 0: return T_PCHAR;
case _l: return T_PWCHAR;
default: return T_ERROR;
}
case 'S': if (!mod) return T_PWCHAR; else return T_ERROR;
case 'n': if (!mod) return T_NCHARS; else return T_ERROR;
case 'm': if (!mod) return T_STRERR; else return T_ERROR;
default: /* not supported! */
break;
}
}
return T_ERROR;
}
#define MINBUFSIZE 256
static wchar_t *read_wchar(FILE *ifile)
{
wchar_t *buf, *nb, *ws;
size_t size=MINBUFSIZE;
int i;
buf=malloc(size);
if (buf==NULL) return NULL;
ws=buf;
do {
if ((ws-buf)*sizeof(wchar_t) >= size) {
nb=realloc(buf, size+=MINBUFSIZE);
if (nb==NULL) {
free(buf); return NULL;
}
buf=nb, size+=MINBUFSIZE;
}
i=getw(ifile), *ws++=i;
} while (i!=EOF && i!=0);
if (i!=0) {
free(buf); return NULL;
}
nb=realloc(buf, (ws-buf)*sizeof(wchar_t));
if (nb==NULL) {
free(buf); return NULL;
}
return nb;
}
static char *read_char(FILE *ifile)
{
char *buf, *nb, *s;
size_t size=MINBUFSIZE;
int i;
buf=malloc(size);
if (buf==NULL) return NULL;
s=buf;
do {
if (s-buf >= size) {
nb=realloc(buf, size+=MINBUFSIZE);
if (nb==NULL) {
free(buf); return NULL;
}
buf=nb, size+=MINBUFSIZE;
}
i=getc(ifile), *s++=i;
} while (i>0);
if (i<0) {
free(buf); return NULL;
}
nb=realloc(buf, s-buf);
if (nb==NULL) {
free(buf); return NULL;
}
return nb;
}
int clog_read(FILE *ifile, FILE *ofile)
{
const char *fmt, *p;
char *afmt;
size_t len;
enum e_types type;
#define PSCALAR(type) do { \
type var; \
fread(&var, sizeof(var), 1, ifile); \
len+=fprintf(ofile, sfmt, var); \
} while(0)
/* read format */
fread(&fmt, sizeof(const char*), 1, ifile);
if (fmt==NULL) {
fmt=afmt=read_char(ifile);
if (fmt==NULL) return -1;
}
else afmt=NULL;
while (*fmt) {
char *sfmt; size_t slen;
if (*fmt!='%') {
fputc(*fmt++, ofile), len++; continue;
}
/* parse format spec. */
type=parse_type((p=fmt, &fmt));
slen=fmt-p+1, sfmt=malloc(slen+1);
if (sfmt==NULL) {
free(afmt); return -1;
}
memcpy(sfmt, p, slen), sfmt[slen]=0;
switch (type) {
case T_INT: PSCALAR(int); break;
case T_LONG: PSCALAR(long); break;
case T_DOUBLE: PSCALAR(double); break;
#ifndef __NO_LONG_LONG
case T_LLONG: PSCALAR(long long); break;
#endif
#ifndef __NO_LONG_DOUBLE_MATH
case T_LDOUBLE: PSCALAR(long double); break;
#endif
case T_PVOID: PSCALAR(const void *); break;
case T_INTMAX: PSCALAR(intmax_t); break;
case T_PTRDIFF: PSCALAR(ptrdiff_t); break;
case T_SIZE: PSCALAR(size_t); break;
case T_WCHAR: PSCALAR(wint_t); break;
case T_NCHARS: /* skip */
case T_STRERR: {
int err; const char *s;
fread(&err, sizeof(err), 1, ifile);
s=strerror(err);
len+=strlen(s);
fputs(s, ofile);
} break;
case T_PCHAR: {
char *s=read_char(ifile);
if (s==NULL) {
free(sfmt), free(afmt); return -1;
}
len+=fprintf(ofile, sfmt, s);
free(s);
} break;
case T_PWCHAR: {
wchar_t *ws=read_wchar(ifile);
if (ws==NULL) {
free(sfmt), free(afmt); return -1;
}
len=fprintf(ofile, sfmt, ws);
free(ws);
} break;
default: /* unknown format sequence -- skip... */
break;
}
free(sfmt);
fmt++;
}
free(afmt);
return len;
}
int clog_file(FILE *file, const char *fmt, ...)
{
va_list args;
size_t len;
enum e_types type;
#define SCALAR(type) do { \
type var = va_arg(args, type); \
len+=fwrite(&var, sizeof(var), 1, file); \
} while(0)
assert(fmt!=NULL);
{ /* write format first */
const char *p;
if (ptr_is_const(fmt)) p=fmt; else p=NULL;
len=fwrite(&p, sizeof(p), 1, file);
if (p==NULL) len+=fwrite(fmt, strlen(fmt)+1, 1, file);
}
va_start(args, fmt);
while (*fmt) {
if (*fmt!='%') {
fmt++; continue;
}
/* parse format spec. */
type=parse_type(&fmt);
switch (type) {
case T_INT: SCALAR(int); break;
case T_LONG: SCALAR(long); break;
case T_DOUBLE: SCALAR(double); break;
#ifndef __NO_LONG_LONG
case T_LLONG: SCALAR(long long); break;
#endif
#ifndef __NO_LONG_DOUBLE_MATH
case T_LDOUBLE: SCALAR(long double); break;
#endif
case T_PVOID: SCALAR(const void*); break;
case T_INTMAX: SCALAR(intmax_t); break;
case T_PTRDIFF: SCALAR(ptrdiff_t); break;
case T_SIZE: SCALAR(size_t); break;
case T_WCHAR: SCALAR(wint_t); break;
case T_NCHARS: break;
case T_STRERR:
len+=fwrite(&errno, sizeof(errno), 1, file);
break;
case T_PCHAR: { /* zero-terminated string */
const char *s = va_arg(args, const char *);
len+=fwrite(s, strlen(s)+1, 1, file);
} break;
case T_PWCHAR: {
const wchar_t *ws = va_arg(args, const wchar_t *);
len+=fwrite(ws, sizeof(wchar_t), wcslen(ws)+1, file);
} break;
default:
fprintf(stderr, "bad format spec. %s\n", fmt);
return -1;
}
fmt++;
}
return len;
#undef SCALAR
}
#if 0
void writelog(int prio, char *fmt, ...)
{
abort();
}
#endif
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
if (argc<2) {
clog_file(stdout, "test u=%u s=%s f=%LF ws=%ls\n", 12, "TEST",
(long double)0.15, L"русский");
} else {
clog_read(stdin, stdout);
}
return 0;
}
[ZX]
-
- На С++ не получилось - всегда вызывается вариант без const. А в качестве грязного хака я бы предложил смотреть на адрес аргумента - rw и ro данные лежат в разных сегментах. - vmp(18.05.2011 21:33)
- так и делаю. Те, кто используют виртуализацию, сейчас нас заплюют - koyodza(18.05.2011 22:16)
- Плевать не буду, а лишь замечу, что квалификатор const вовсе не гарантирует принадлежность к определенному (RO) сегменту. - rezident(18.05.2011 22:19)
- А это и не важно. Если const char* лежит в ОЗУ, то после выключения/включения питания оно там снова окажется, должно по крайней мере... - fk0(19.05.2011 12:50)
- то само собой. Но топикстартер вроде хочет откидывать любые указатели на RAM, независимо от того на const они указывают или на ещё что-то - koyodza(18.05.2011 22:26)
- Плевать не буду, а лишь замечу, что квалификатор const вовсе не гарантирует принадлежность к определенному (RO) сегменту. - rezident(18.05.2011 22:19)
- так и делаю. Те, кто используют виртуализацию, сейчас нас заплюют - koyodza(18.05.2011 22:16)
- На С++ не получилось - всегда вызывается вариант без const. А в качестве грязного хака я бы предложил смотреть на адрес аргумента - rw и ro данные лежат в разных сегментах. - vmp(18.05.2011 21:33)