Использование переключателя для отображения указателей на функции в строках - PullRequest
2 голосов
/ 26 сентября 2019

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

У меня есть функция (скажем, function_caller), которая, помимо прочего, вызывает другую функцию, на которую она получает указательв качестве аргумента.Я хотел бы, чтобы мой регистратор уведомлял, какую функцию function_caller вызывает.

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

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

Единственная существенная проблема, которую я вижу, заключается в том, что каждый разработчик, решивший передать указатель функции нового типа на function_caller, будетнужно как-то знать, чтобы обновить switch, иначе это не удастся.

Я что-то пропустил?Или, может быть, я должен рассмотреть другой подход?

Ответы [ 4 ]

3 голосов
/ 26 сентября 2019

Как насчет этого?Вместо переключателя храните таблицу функций и их именные строки.Таблицу можно даже динамически обновлять, в отличие от случая переключения.Вам также не нужно будет идти по краю стандарта!

#include <stdio.h>

typedef void (*callback_t) (void);

void first (void) { printf("%d", 1); };
void second (void) { printf("%d", 2); };
void third (void) { printf("%d", 3); };

typedef struct fntable_t
{
    callback_t fn;
    char *name;
} fntable_t;

fntable_t fntable[] =
{
    { first, "first" },
    { second, "second" },
    { third, "third" }
};

char* log_str(callback_t c)
{
    for(int i = 0; i < sizeof(fntable) / sizeof(fntable_t); i++)
    {
        if(fntable[i].fn == c)
            return fntable[i].name;    
    }
    return "unknown";
}

void function_caller(callback_t c)
{
    printf("%s",log_str(c));
    c();
}

int main(void) 
{
    function_caller(first);
    function_caller(second);
    function_caller(third);
    return 0;
}
2 голосов
/ 26 сентября 2019

Если вы можете изменить API функций, рассмотрите возможность использования __func__ для получения текстового имени каждой функции.Если у вас может быть тип указателя на функцию:

typedef void func_t (const char** name);

Тогда вы можете заставить каждую функцию возвращать вызывающему ее имя.

void foo (const char** name)
{
  /* do foo stuff here */
  *name = __func__;
}

void bar (const char** name)
{
  /* do bar stuff here */
  *name = __func__;
}

Пример:

#include <stdio.h>

typedef void func_t (const char** name);

void foo (const char** name)
{
  /* do foo stuff here */
  *name = __func__;
}

void bar (const char** name)
{
  /* do bar stuff here */
  *name = __func__;
}

const char* function_caller (func_t* func, const char** name)
{
  func(name);
  return *name;
}

int main(void)
{
  static func_t*const func [] = 
  {
    foo,
    bar,
  };
  const char* name;

  for(size_t i=0; i<sizeof func/sizeof *func; i++)
  {
    puts( function_caller(func[i], &name) );
  }
}
1 голос
/ 26 сентября 2019

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

Это работает, только если function_caller всегда вызывается с именем функции, а не с переменной-указателем функции.

Пример:

#include <stdio.h>

static void funcA(void)
{
  printf("This is funcA\n");
}

static void funcB(void)
{
  printf("This is funcB\n");
}

/* renamed function gets an additional string argument */
static void function_caller_internal(void (*func)(void), const char *name)
{
   printf("calling %s\n", name);
   func();
}

/* wrapper macro stringifies the function name to pass it the additional argument */
#define function_caller(func) function_caller_internal(func, #func)

int main(void)
{
   /* unchanged calls */
   function_caller(funcA);
   function_caller(funcB);
   return 0;
}

Эта печать

calling funcA
This is funcA
calling funcB
This is funcB
1 голос
/ 26 сентября 2019

Предполагая, что ваша кодовая база имеет вменяемые имена переменных и имен функций, вы можете добавить аргумент char * к вызывающей функции:

void function_caller(char *name, int fpnt());

и затем предоставить макрос:

#define function_caller_autoname(fpnt) function_caller(#fpnt, fpnt)

(Или, для спагетти-кода, вы можете предоставить макрос с тем же именем, что и у функции).

#fpnt будет расширен обработчиком до строкового литерала с именем функции.

Затем, когда ваша кодовая база вызвала:

function_caller(some_function)

, измените его на:

function_caller_autoname(some_function)
# will be expanded to by the processor:
# function_caller("some_function", some_function)

или измените его вручную, чтобы предоставить имя / идентификатор / описание функции:

function_caller("Some function: ", some_function)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...