Можно ли поместить макрос в пространство имен в С++?

Мое приложение использует другой вывод, чем стандартный вывод для записи информации, поэтому я написал свои собственные функции 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 __ для печати всех пространств имен, включая аргументы функции.