Как получить список значений всех параметров функции и связать их с классом журналирования в Visual C ++? - PullRequest
10 голосов
/ 14 марта 2011

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

 void SomeApiFunction( bool parameter1, const wchar_t* parameter2 )
 {
     LOG_PARAMETERS( parameter1 << parameter2 )
     //function payload
 }

Макрос расширяется следующим образом:

 #define LOG_PARAMETERS( x ) GlobalLoggerObject << x;

и GlobalLoggerObject имеет тип class CLogger, который имеет operator << перегружен для всех возможных типов параметров - что-то вроде этого:

template<class TypeToLog>
CLogger& operator << (const TypeToLog& parameter)
{
   //logging code
   return *this;
}

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

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

Isесть какой-то способ просто захватить все параметры одновременно - что-то вроде этого:

TRACE_ALL_PARAMETERS( UNIVERSAL_MAGIC )

, чтобы нам не приходилось перечислять каждый параметр явно?

Ответы [ 5 ]

4 голосов
/ 14 марта 2011

Нет способа сделать это в C / C ++, но это можно сделать с помощью генерации внешнего кода.

  • Программист при написании функции помещает TRACE_ALL_PARAMETERS() (с пустым списком аргументов) при необходимости.
  • Сценарий анализирует источник, чтобы найти все экземпляры /^\s*TRACE_ALL_PARAMETERS\(/, анализирует ближайший список имен параметров и заменяет все в скобках, следующих за TRACE_ALL_PARAMETERS, извлеченным списком параметров.

Этот скрипт может быть вызван как шаг пользовательской сборки или как макрос IDE.

Тем не менее, мне не кажется, что стоит потратить время и усложнить процесс управления кодом. Исходя из моего личного опыта, редко требуется просто напечатать все параметры функции. Обычно для некоторых параметров необходимо указать пользовательское форматирование, получить доступ к некоторым параметрам особым образом (например, нет смысла печатать MyStruct* в качестве указателя, обычно я хотел бы вместо этого печатать p_my_struct->field_1 и p_my_struct->field_2) и т. д.

Так что я бы вел ручную регистрацию и использовал внешний инструмент для отслеживания оптовых продаж, если это необходимо. Например. в Windows можно использовать WinAPIOverride32 , хотя с дополнительной работой по описанию функций в понятном формате (я сделал это один раз, используя GCC-XML для генерации описаний функций).

1 голос
/ 14 марта 2011

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

Как упоминает Наваз в одном комментарии, существует макрос __FUNCSIG__, который перечисляет все типы параметров,Например, для моего основного он расширяется до "int __cdecl main(int,char *[])".

Теперь, что вы можете сделать, это использовать эту строку, чтобы выполнить проверку во время выполнения количества (возможно, даже типов) зарегистрированных параметров, и если у вас естьнесоответствие использует некоторый механизм вашей тестовой структуры, чтобы сигнализировать, что регистрация функции «нарушена».

Что касается проблемы с невозможностью разделения параметров, передаваемых макросу регистрации: я бы попробовал что-то вродеthis:

#define LOG_WITH_CHECK(ValidationString, Params)                          \
    cout << "Validate [" ValidationString << "] vs. [" << #Params << "]\n"; \
    cout << "Now feed params to logging: {" << Params << "}";               \
/**/

int main(int argc, char* argv[])
{
  LOG_WITH_CHECK(__FUNCSIG__, argc << argv[0]);
  ...

То есть вы также проверяете переданные параметры как << отдельную строку.

0 голосов
/ 14 марта 2011

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

#include <iostream>
#include <boost/type_traits.hpp>

template<typename F>
void log(F*
    , typename boost::function_traits<F>::arg1_type p1
    )
{ std::cout << p1 << std::endl; }

template<typename F>
void log(F*
    , typename boost::function_traits<F>::arg1_type p1
    , typename boost::function_traits<F>::arg2_type p2
    )
{ std::cout << p1 << p2 << std::endl; }

// ... more overloads

void foo(int x)
{
    log(&foo, x);
}

int main()
{
    foo(42);
}

Или вы можете избежать этих перегрузок, выводящих тип boost.fusion из типа функции.

0 голосов
/ 14 марта 2011

Не существует стандартного способа подтверждения того, что вы хотите.

0 голосов
/ 14 марта 2011

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

Очень простой подход состоит в том, чтобы «зачеркнуть» все аргументы и вывести их.С некоторой креативностью, я уверен, что вы можете придумать что-то лучше:

#include <iostream>
#define ALLARGS( ... ) std::cout << #__VA_ARGS__  << std::endl;

int main( int argc, char* argv[] )
{
    ALLARGS( "two arguments", 1.0 );
    ALLARGS( "five arguments", 1.0, 1.0, true, 'c' );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...