Esta pergunta fornece um exemplo claro de como você pode usar mal as macros. Para ver outros exemplos (e se divertir), veja esta pergunta .
Dito isso, darei exemplos do mundo real do que considero uma boa incorporação de macros.
O primeiro exemplo aparece no CppUnit , que é uma estrutura de teste de unidade. Como qualquer outra estrutura de teste padrão, você cria uma classe de teste e precisa especificar, de alguma forma, quais métodos devem ser executados como parte do teste.
#include <cppunit/extensions/HelperMacros.h>
class ComplexNumberTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( ComplexNumberTest );
CPPUNIT_TEST( testEquality );
CPPUNIT_TEST( testAddition );
CPPUNIT_TEST_SUITE_END();
private:
Complex *m_10_1, *m_1_1, *m_11_2;
public:
void setUp();
void tearDown();
void testEquality();
void testAddition();
}
Como você pode ver, a classe possui um bloco de macros como seu primeiro elemento. Se eu adicionei um novo método testSubtraction
, é óbvio o que você precisa fazer para incluí-lo no teste.
Esses blocos de macro se expandem para algo assim:
public:
static CppUnit::Test *suite()
{
CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite( "ComplexNumberTest" );
suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTest>(
"testEquality",
&ComplexNumberTest::testEquality ) );
suiteOfTests->addTest( new CppUnit::TestCaller<ComplexNumberTest>(
"testAddition",
&ComplexNumberTest::testAddition ) );
return suiteOfTests;
}
Qual você prefere ler e manter?
Outro exemplo está na estrutura do Microsoft MFC, onde você mapeia funções para mensagens:
BEGIN_MESSAGE_MAP( CMyWnd, CMyParentWndClass )
ON_MESSAGE( WM_MYMESSAGE, OnMyMessage )
ON_COMMAND_RANGE(ID_FILE_MENUITEM1, ID_FILE_MENUITEM3, OnFileMenuItems)
// ... Possibly more entries to handle additional messages
END_MESSAGE_MAP( )
Então, quais são as coisas que distinguem "Boas Macros" do tipo maligno horrível?
Eles executam uma tarefa que não pode ser simplificada de nenhuma outra maneira. Escrever uma macro para determinar o máximo entre dois elementos está errado, porque você pode obter o mesmo usando um método de modelo. Mas existem algumas tarefas complexas (por exemplo, mapear códigos de mensagens para funções-membro) com as quais a linguagem C ++ simplesmente não lida com elegância.
Eles têm um uso formal extremamente rigoroso. Nos dois exemplos, os blocos de macro são anunciados iniciando e finalizando macros, e as macros entre elas só aparecerão dentro desses blocos. Você tem C ++ normal, se desculpa brevemente com um bloco de macros e volta ao normal novamente. Nos exemplos de "macros malignas", as macros estão espalhadas por todo o código e o infeliz leitor não tem como saber quando as regras do C ++ se aplicam e quando não.