Как я могу получить список всех значений параметров функций и связать их с классом журнала в Visual С++?
У нас есть огромный набор функций, открытых как внешний 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 <<
создаются в зависимости от типа параметра.
Отлично, за исключением того, что мы должны поддерживать список параметров, полностью синхронных с параметром окружающей функции.
Есть ли какой-то способ просто сразу взять все параметры - что-то вроде этого:
TRACE_ALL_PARAMETERS( UNIVERSAL_MAGIC )
так что нам не нужно явно указывать каждый параметр?
Ответы
Ответ 1
В C/С++ нет способа сделать это, но это может быть выполнено с помощью внешнего генерации кода.
- Программист при написании функции ставит
TRACE_ALL_PARAMETERS()
(с пустым списком аргументов), когда это необходимо.
- A script анализирует источник, чтобы найти все экземпляры
/^\s*TRACE_ALL_PARAMETERS\(/
, анализирует ближайший список имен параметров и заменяет все в круглых скобках, следующих за TRACE_ALL_PARAMETERS
, извлеченным списком параметров.
Этот script может быть вызван как шаг пользовательской сборки или как макрос IDE.
Тем не менее, мне не кажется целесообразным вложение времени и усложнение процесса управления кодом. Из моего личного опыта редко требуется просто распечатать все параметры функции. Обычно нужно указать произвольное форматирование для некоторых параметров, особым образом получить доступ к некоторым параметрам (например, нет смысла печатать MyStruct*
в качестве указателя, обычно я хотел бы печатать p_my_struct->field_1
и p_my_struct->field_2
вместо этого), и т.д.
Итак, я бы продолжил ручную регистрацию и использовал внешний инструмент для оптовой трассировки, если это необходимо. Например. в Windows, WinAPIOverride32, но с дополнительной работой для описания функций в формате, который он понимает (я сделал это один раз, используя GCC-XML для генерации описаний функций).
Ответ 2
Если это действительно проблема, одна из идей может заключаться в том, чтобы добавить проверку времени выполнения для ваших модульных тестов.
Как отмечает Nawaz в одном комментарии, существует макрос __FUNCSIG__
, в котором перечислены все типы пареметров. Например, для моей основной версии она расширяется до "int __cdecl main(int,char *[])"
.
Теперь, что вы можете сделать, это использовать эту строку для выполнения проверки времени выполнения (возможно, даже типов) параметров, зарегистрированных в журнале, и если у вас есть несоответствие, используйте какой-либо механизм вашей тестовой среды, чтобы сигнализировать, что регистрация функций "сломанный".
Что касается проблемы с невозможностью разделения передаваемых параметров на макрос регистрации: я бы попробовал что-то вроде этого:
#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]);
...
То есть вы также проверяете переданные параметры как <<
разделяемую строку.
Ответ 3
Нет стандартного подтверждающего способа делать то, что вы хотите.
Ответ 4
Вы можете добавить проверку времени компиляции для параметров, переданных в журнал.
#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 из типа функции.
Ответ 5
Вы можете попробовать использовать нестандартный макрос 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' );
}