2014-02-12 14 views
2

Я хочу создать несколько протоколирования, и я создал класс. Однако у меня есть проблема с передачей ему аргументов.Почему va_start терпит неудачу?

Класс:

namespace debug 
{ 
    class log 
    { 
     private: // Members 
      const std::string context; 
      int Type; 
     public:  // Methods 
      void message(int Type, const std::string& message, ...); 

     public:  // Constructor, Destructor 
      log(const std::string& context, int Type); 
      ~log(); 
    };//class log 
}//namespace debug  

namespace debug 
{ 
    void log::message(int Type, const std::string& message, ...) 
    { 
     va_list args; 
     int len; 

     char *buffer; 

     va_start(args, message); 

     len = _vscprintf(message.c_str(), args) + 1; // _vscprintf doesn't count terminating '\0' 
     buffer = (char*)malloc(len * sizeof(char)); 

     vsprintf_s(buffer, len, message.c_str(), args); 

     va_end(args); 

    }//log::message 
}//namespace debug 

У меня есть два макроса определены:

#define DEBUG_METHOD(Type) debug::log _debugLog(__FUNCTION__, Type); 
#define DEBUG_MESSAGE(Type, debug_message, ...) { _debugLog.message(Type, debug_message, ##__VA_ARGS__); } 

и я использую их в функции, как это:

BOOL test(BOOL *bTestVal) 
{ 
    DEBUG_METHOD(INFO); 
    DEBUG_MESSAGE(INFO, "Argument1 = '%s'", BoolToString((BOOL)*bTestVal)); 

     //here comes some work... 
} 

К сожалению, я всегда получаю сообщение об ошибке. Эта строка len = _vscprintf(message.c_str(), args) + 1; всегда выдает ошибку. Я думаю, va_start вызывает это, потому что args имеет значение + args 0x0052eed8 "ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ... char *

Может кто-нибудь, пожалуйста, помогите мне, что я делаю неправильно?

Заранее благодарен!

+0

Что возвращает ваш BoolToString()? И вы уверены, что ваш компилятор поддерживает конструкцию '## __ VA_ARGS__'? – nos

+0

Это BoolToString: 'char * const BoolToString (BOOL b) { \t return b? «Правда»: «Неверно»; } // BoolToString' – kampi

+0

@nos: нет, я не уверен. Я до сих пор не использовал этот вид макроса. Я использую Visual Studio 2013 Express. – kampi

ответ

6

18,10/3 ... Параметр parmN является идентификатором крайнего правого параметра в списке переменных параметров определения функции (один раз перед тем, ...). Если параметр parmN объявлен функцией, массивом или ссылочным типом или с типом, который несовместим с типом, который возникает при передаче аргумента, для которого нет параметра, поведение не определено.

Emphasis mine.

+0

Примечание: с параметром parmN параметр funtion 'va_start' является элементом. 'void va_start (va_list ap, paramN);' –

2

Используйте символ const char * как параметр перед эллипсами. Как указано в другом ответе, поведение не определено.

Кроме того, похоже, что функция log :: message имеет утечку памяти. Вы используете malloc() без вызова free(), а также не проверяете NULL на возврат malloc() (почему вы используете malloc() в программе на C++)

2

Игорь уже предоставил вам официальную цитату, и здесь это фрагмент кода, который показывает, что происходит точно, с Visual C++. Дело в том, что va_start должен указать указатель, чтобы пропустить параметр, с которого он запускается, и в то время как ссылка эффективно передается как указатель, размер пропуска принимается как полный размер аргумента. Это продвигает указатель намного дальше, чем вы ожидаете.

Сравнить с использованием va_start против A* и A& ниже:

class A 
{ 
public: 
    char B[1024]; 
}; 

void C(int f, A* a, ...) 
{ 
    va_list Arguments; 
    va_start(Arguments, a); 
    int d = va_arg(Arguments, int); 
    _tprintf(_T("0x%p, 0x%p, %d, %d\n"), &f, Arguments, (char*) &f - (char*) Arguments, d); 
} 

void E(int f, A& a, ...) 
{ 
    va_list Arguments; 
    va_start(Arguments, a); 
    int d = va_arg(Arguments, int); 
    _tprintf(_T("0x%p, 0x%p, %d, %d\n"), &f, Arguments, (char*) &f - (char*) Arguments, d); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    A a; 
    C(0, &a, 1234); 
    E(0, a, 1234); 

Выход вы получите об этом:

0x0018F9E0, 0x0018F9EC, -12, 1234 
0x0018F9E0, 0x0018FEC0, -1248, -858993460 

Обе функции имеют свое начало стека кадров на тот же адрес (ожидаемо) и то результат va_start перемещает указатель на другое расстояние, которое коррелирует с размером A класса (12 против 1248).

Вот почему вы не можете использовать va_start с аргументом ссылочного типа: он не может правильно пропустить переменную и начать обрабатывать ваши эллипсы в правильном месте памяти.

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