Передача вызывающего __FILE__ __LINE__ функции без использования макроса
Я привык к этому:
class Db {
_Commit(char *file, int line) {
Log("Commit called from %s:%d", file, line);
}
};
#define Commit() _Commit(__FILE__, __LINE__)
но большая проблема в том, что я переопределяю слово Commit
глобально, а в инфраструктуре приложений 400 тыс. строк это проблема. И я не хочу использовать определенное слово, например DbCommit
: мне не нравятся сокращения, например db->DbCommit()
, или для передачи значений вручную везде: db->Commit(__FILE__, __LINE__)
хуже.
Итак, любой совет?
Ответы
Ответ 1
Итак, вы хотите сделать запись (или что-то еще) с информацией о файле и строке, и вы предпочитаете не использовать макросы, правильно?
В конце дня его просто невозможно сделать на С++. Независимо от того, какой механизм вы выбрали - будь то встроенные функции, шаблоны, параметры по умолчанию или что-то еще - если вы не используете макрос, вы просто получите имя файла и номер строки функции ведения журнала, а не точка вызова.
Использовать макросы. Это одно место, где они действительно не подлежат замене.
EDIT:
Даже С++ часто задаваемые вопросы говорит, что макросы иногда представляют собой меньшее количество двух зол.
EDIT2:
Как говорит Натон в комментариях ниже, в тех случаях, когда вы используете макросы, лучше всего говорить об этом. Укажите макрокоманды макросов, например COMMIT()
, а не COMMIT()
. Это позволит сторонам и отладчикам понять, что происходит макросообщение, и в большинстве случаев это должно помочь избежать столкновений. Обе хорошие вещи.
Ответ 2
Вы можете использовать комбинацию параметра по умолчанию и трюк препроцессора для передачи файла вызывающего абонента в функции. Это следующее:
-
Объявление функции:
static const char *db_caller_file = CALLER_FILE;
class Db {
_Commit(const char *file = db_caller_file) {
Log("Commit called from %s", file);
}
};
-
Объявить переменную db_caller_file
в файле заголовка класса.
Каждая единица перевода будет иметь const char *db_caller_file
. Он статичен, поэтому он не будет мешать между единицами перевода. (Нет нескольких объявлений).
-
Теперь объект CALLER_FILE
, это макрос и будет создан из параметров командной строки gcc. Фактически, если вы используете автоматическую систему Make, где есть общее правило для исходных файлов, это намного проще: вы можете добавить правило для определения макроса с именем файла в качестве значения. Например:
CFLAGS= -MMD -MF $(DEPS_DIR)/$<.d -Wall -D'CALLER_FILE="$<"'
-D
определяет макрос перед компиляцией этого файла.
$<
Производит подстановку для имени предпосылки для правила, которое в этом случае является именем исходного файла. Таким образом, каждая единица перевода будет иметь свою собственную переменную db_caller_file
со значением строка, содержащую имя файла.
Та же идея не может быть применена для строки вызывающего абонента, так как каждый вызов в одной и той же системе переводов должен иметь разные номера строк.