Могу ли я рано вернуться из функции с переменным аргументом? - PullRequest
2 голосов
/ 01 декабря 2010

Предположим, у меня есть две функции C ++ для вывода отладки:

void Trace( const wchar_t* format, ... )
{
    va_list args;
    va_start( args, format );
    VarArgTrace( format, args );
    va_end( args );
}

void VarArgTrace( const wchar_t* format, va_list args )
{
    WCHAR buffer[1024];
    //use ::_vsnwprintf_s to format the string
    ::OutputDebugStringW( buffer );
}

вышеупомянутый использует Win32 OutputDebugStringW(), но это не имеет значения. Теперь я хочу оптимизировать форматирование, чтобы при отсутствии отладчика форматирование не выполнялось (я измерял - ускорение значительно):

void Trace( const wchar_t* format, ... )
{
    if( !IsDebuggerPresent() ) {
        return;
    }
    //proceed as previously
    va_list args;
    .....
 }

Повлияет ли тот факт, что я вернусь рано после того, как IsDebuggerPresent() вернет null, повлияет на что-нибудь, кроме того, что форматирование будет пропущено?

Я имею в виду, я больше не звоню va_start и va_end - будет ли это иметь значение? Будет ли пропуск va_start и va_end вызывать неожиданные изменения поведения?

Ответы [ 5 ]

6 голосов
/ 01 декабря 2010

Нет, нет необходимости использовать va_start в функции varargs.

Если вы не используете va_start, вы не можете использовать va_end; если вы используете va_start, вы должны использовать va_end, независимо от того, как возвращает функция.

1 голос
/ 01 декабря 2010

Это довольно безопасно, потому что в языках Си работа по «очистке» переданных параметров из стека всегда выполняется вызывающей функцией, а не вызываемой.

Некоторые языки (на ум приходит Паскаль) могут делать это наоборот. Это потенциально более эффективно, но работает только потому, что не существует понятия переменного числа аргументов.

1 голос
/ 01 декабря 2010

Единственное требование при досрочном возврате: если вы использовали (выполнили) va_start(), вы должны использовать va_end() перед возвратом.

Если вы нарушите это правило, вы уйдетес ним на большинстве систем, но некоторым системам где-то нужна va_end(), так что не рискуйте опустить ее.Пропускать это неопределенное поведение.

Кроме этого правила, вам решать, как вы справитесь с возвращением.Ваш предложенный досрочный возврат не является проблемой.

1 голос
/ 01 декабря 2010

Все эти макросы (по крайней мере, в Windows) являются манипулированием указателем для закладки в списке аргументов. Возвращение рано будет хорошо. Это зависит от компилятора, хотя я не могу представить, почему ранний возврат будет проблемой на других платформах.

От x86 vadefs.h:

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )
0 голосов
/ 01 декабря 2010

В varargs нет особых накладных расходов, обычно всего несколько манипуляций с указателями. Вероятно, немного больше накладных расходов при настройке стека для каждого вызова varargs, поэтому вместо

void Trace( const wchar_t* format, ... )
{
    if( !IsDebuggerPresent() ) {
        return;
    }
    //proceed as previously
    va_list args;
    .....
 }

вам, вероятно, лучше использовать препроцессор, чтобы предотвратить вызовы для отслеживания вещей в не отладочных сборках.

Засорение вашего кода

if( IsDebuggerPresent() ) Trace( stuff... );

кажется довольно отвратительным.

VC2010 уже получал вариационные макросы? :)

...