Можно ли поместить макрос в пространство имен в С++?
Мое приложение использует другой вывод, чем стандартный вывод для записи информации, поэтому я написал свои собственные функции Log()
, Error()
, Panic()
и Assert()
. Чтобы организовать вещи красиво, я прилагаю все материалы отладки в пространстве имен Debug
.
Для функции Assert()
было бы более полезно также предоставить исходный файл и номер строки, что возможно только с помощью макросов __LINE__
и __FILE__
. Тем не менее, это довольно неприятно, неэффективно и т.д. Всегда нужно указывать эти два параметра.
Итак, так будет выглядеть мой код:
namespace Debug {
void Assert (int condition, std::string message, std::string file, int line);
}
Мой вопрос: возможно ли разместить макрос, содержащий эти два параметра внутри пространства имен Debug
? Вот так:
namespace Debug {
void Assert_ (int condition, std::string message, std::string file, int line);
#define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)
}
// .... Somewhere where I call the function ....
Debug::Assert (some_condition, "Some_condition should be true");
// Output: Assertion failed on line 10 in file test.cpp:
// Some_condition should be true
Действительно ли это С++? Если нет, есть ли способ сделать эту работу?
Ответы
Ответ 1
#define
- это препроцессорная директива. Макросы заменяются раньше всего, кроме удаления комментариев (что означает, перед компиляцией). Поэтому в то время, когда макросы заменяются, компилятор ничего не знает о ваших пространствах имен.
Как утверждают другие люди, в вашем случае все будет хорошо. Однако, вот как вы можете получить проблемы:
namespace A
{
void Assert_ (int condition, std::string message, std::string file, int line)
{
std::cout << "A";
}
#define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)
}
namespace B
{
void Assert_ (int condition)
{
std::cout << "B";
}
#define Assert(a,b) Assert_(a)
}
int main(int argc, char *argv[])
{
A::Assert(0,"asdasd");
B::Assert(0,"asdasd");
}
Итак, хотя это выглядит так: "в пространствах имен" они не являются, а последний #define
всегда будет использоваться, что в этом случае приведет к ошибке времени компиляции, поскольку код в main будет заменено на:
A::Assert(0);
B::Assert(0);
вместо
A::Assert(0,"asdasd", _FILE_, _LINE_);
B::Assert(0);
Ответ 2
namespace Debug
{
void Assert_(int condition, std::string message, std::string file, int line);
#define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)
}
// .... Somewhere where I call the function ....
Debug::Assert (some_condition, "Some_condition should be true");
Это конкретное использование будет делать именно то, что вы хотите, но макрос Assert никоим образом не является частью пространства имен Debug... он точно так же, как если бы вы сделали:
namespace Debug
{
void Assert_(int condition, std::string message, std::string file, int line);
}
#define Assert(a,b) Assert_(a, b, __FILE__, __LINE__)
// .... Somewhere where I call the function ....
Debug::Assert (some_condition, "Some_condition should be true");
Здесь подстановка работает не потому, что Assert
находилась в пространстве имен Debug
(это не в вашем коде или этом коде, а препроцессор не знает, что такое пространства имен) - он работает, потому что Assert
распознается как идентификатор макроса, выполняется подстановка Assert_
, а затем, собственно, сам компилятор обнаруживает там Debug::Assert_
Итак, скажите, что у вас где-то позже в вашем блоке перевода есть совершенно не связанный код:
my_object.Assert(my_functor);
Макрозависимость все равно будет использоваться для создания ошибки времени компиляции, говорящей, что у вас неправильное количество аргументов для макроса. Скажем, что не связанный код:
my_object.Assert(my_functor, "some text");
Тогда это будет заменено на:
my_object.Assert_(my_functor, "some text", __FILE__, __LINE__);
(Отдельно стандартная практика не использовать строчные буквы в именах макросов препроцессора).
Ответ 3
Нет, препроцессор вообще не интересуется пространствами имен. На самом деле препроцессор работает, по крайней мере, концептуально, прежде чем компилятор ничего не увидит.
Для себя я просто делаю стандартный макрос ASSERT и ожидаю, что никакое "здравомысленное пространство имен" не имеет что-то, называемое ASSERT. Задача решена. Должен ли я потребовать библиотеку, которая имеет собственный ASSERT, тогда я все же могу решить, как с этим бороться; однако единственная библиотека, которую я сейчас использую с ее собственным "assert", называет ее BOOST_ASSERT или что-то в этом роде...
Ответ 4
Да, и ваш макрос будет расширяться точно так, как вы ожидаете.
Debug::Assert (some_condition, "Some_condition should be true");
будет заменено на
Debug::Assert_(some_condition, "Some_condition should be true", __FILE__, __LINE__)
Ответ 5
Вы можете попробовать макрос __PRETTY_FUNCTION __ для печати всех пространств имен, включая аргументы функции.