2017-01-22 6 views
4

создать простую библиотеку журналирования в CI есть три файлавина Сегментация на vsprintf с использованием макросов в C

Мой пример файла:

  • example.c

Библиотека файлов:

  • loglib.h
  • logl ib.c

Основная функция моего регистратора называется logme. Я определил различные макросы в обертках для обозначения различных уровней журнала:

например

  • WARN
  • ERROR
  • и т.д ..

В example.c я называю макросы:

int int_arg = 55; 
WARN(1, "Warn message with level 1"); 
WARN(1, "Warn message with level %d", int_arg); 

макрос определяется в loglib.h:

#define WARN(LEVEL, ...) \ 
    logme(LEVEL, 8, " <%s:%d> inside %s() -- "__VA_ARGS__, __FILE__, __LINE__, __func__); 

И, наконец, вот logme функция:

void slog(int level, int flag, const char *msg, ...) 
{ 
    char string[10000]; 
    bzero(string, sizeof(string)); 
    va_list args; 
    va_start(args, msg); 
    vsprintf(string, msg, args); 
    va_end(args); 

    // .. do other things 
} 

Когда я запускаю пример файла это то, что я получаю:

inside main() -- Warn message with level 1

Segmentation fault (core dumped)

Я получаю когда я вызываю WARN со сформированными строками.

Неисправность сегментации появляется в vsprintf(string, msg, args);

Это что-то не так с моим макро?

Вот сделайте файл моего Lib:

CFLAGS = -g -O2 -Wall -lpthread 
LIB = -lrt 
OBJS = loglib.o 

LIBINSTALL = /usr/local/lib 
HEADINSTALL = /usr/local/include 

.c.o: 
    $(CC) $(CFLAGS) -c $< $(LIB) 

libslog.a: $(OBJS) 
    $(AR) rcs liblog.a $(OBJS) 
    @echo [-] Syncing static library 
    sync 

install: 
    @test -d $(LIBINSTALL) || mkdir $(LIBINSTALL) 
    @test -d $(HEADINSTALL) || mkdir $(HEADINSTALL) 
    @install -m 0664 liblog.a $(LIBINSTALL)/ 
    @install -m 0664 loglib.h $(HEADINSTALL)/ 
    @echo [-] Done 

loglib.o: loglib.h 

.PHONY: clean 
clean: 
    $(RM) liblog.a $(OBJS) 

ответ

0

Вот рабочий раствор:

/* 
* SOURCE_THROW_LOCATION macro returns string which 
* points to the file, as well as, the corresponding line 
* of the caller function. 
*/ 
#define LVL1(x) #x 
#define LVL2(x) LVL1(x) 
#define SOURCE_THROW_LOCATION "<"__FILE__":"LVL2(__LINE__)"> -- " 

#define WARN(LEVEL, ...) \ 
    logme(LEVEL, FWARN, SOURCE_THROW_LOCATION __VA_ARGS__); 
1

Это не может работать, потому что вы пытаетесь конкатенации из __VA_ARGS__ здесь:

#define WARN(LEVEL, ...) \ 
logme(LEVEL, 8, " <%s:%d> inside %s() -- "__VA_ARGS__, __FILE__, __LINE__, __func__); 

Это портит вашу последовательность параметров: __FILE__ не будет идти с <%s и т. д. Вы должны переопределить свой WARN() макрос, чтобы __VA_ARGS__ был последним. Вам нужно будет написать правильную вариационную функцию, если вы хотите добавить всю эту информацию. Обратите внимание: __func__ не является строковым литералом, он ссылается на соответствующую строку.

+0

пожалуйста, вы можете показать мне, как сделать это? Я не мог понять это, и документация, которую я нашел, не дает такого подробного примера. –

0

Выполнение заказа неверно, когда оно попадает в функцию журнала. Вы можете сделать что-то вроде следующего.

#include <strings.h> 
#include <string.h> 
#include <stdio.h> 
#include <stdarg.h> 

void logme(int level, int flag, const char *msg_preamble, const char* file, int line, const char* function, const char* msg, ...) 
{ 
    char buffer1[10000]; 
    bzero(buffer1, sizeof(buffer1)); 
    sprintf(buffer1, msg_preamble, file, line, function); 

    char buffer2[10000]; 
    bzero(buffer2, sizeof(buffer2)); 

    va_list args; 
    va_start(args, msg); 
    vsprintf(buffer2, msg, args); 
    va_end(args); 

    strcat(buffer1, buffer2); 
    printf("%s\n", buffer1); 
} 

#define WARN(LEVEL, ...)     \ 
    logme(LEVEL, 8, " <%s:%d> inside %s() -- ", __FILE__, __LINE__, __func__, __VA_ARGS__); 

int main(int argc, char** argv) 
{ 
    int int_arg = 55; 
    WARN(1, "Warn message with level 1"); 
    WARN(1, "Warn message with level %d", int_arg); 
} 
+0

приятное решение, но есть макросы, которые просто печатают строки и не показывают строку файла. В вашем случае я должен каждый раз передавать файловую строку в 'logme'. Что делать, если у меня есть макрос, скажем, 'INFO', который печатает только форматированный текст? –

+0

Затем добавьте #define INFO (LEVEL, ...) \ logme (LEVEL, 8, "", "", 0, "", __VA_ARGS__); Элемент управления находится в строке формата в макросе «уровень»: – ccpgh

+0

@ George. это разрешило проблему? любые другие вопросы? спасибо – ccpgh

0

Порядок еправильности, поэтому формат %s не соответствует фактическому аргументу при условии, и программа вызывает неопределенное поведение.

Вот исправленная версия WARN макроса:

#define WARN(LEVEL, ...) \ 
    logme(LEVEL, 8, __FILE__, __LINE__, __func__, __VA_ARGS__); 

И соответствующая logme функция, используя vsnprintf() вместо vsprintf(), чтобы избежать возможного переполнения буфера:

void slog(int level, int flag, const char *filename, in lineno, 
      const char *funcname, const char *msg, ...) { 
    char string[10000]; 
    int prefix; 
    va_list args; 

    // check level, flag.. 
    ... 

    // format the message if needed 
    prefix = snprintf(string, sizeof string, " <%s:%d> inside %s() -- ", 
         filename, lineno, funcname); 

    va_start(args, msg); 
    vsnprintf(string + prefix, sizeof(string) - prefix, msg, args); 
    va_end(args); 

    // do other things 
    ... 
} 
+0

хороший код, но что произойдет, если мы просто хотим передать простую строку? Я получаю ошибку компиляции 'error: ожидаемое выражение перед ')' token' –

+0

@GeorgeGkas: этот случай сложный ... Я изменил ответ другим способом. Мне пришлось изменить API для функции 'logme'. – chqrlie

+0

Это действительно хороший ответ. Я просто не принял его, потому что нашел лучший подход, не используя прототип функции. Конечно, я не разрешаю переменную '__func__', но это не проблема. –

 Смежные вопросы

  • Нет связанных вопросов^_^