Щоб відповісти на ваші запитання, подумайте, для яких макросів переважно використовуються (Увага: код, складений мозком).
- Макроси, що використовуються для визначення символічних констант
#define X 100
Це легко замінити на: const int X = 100;
- Макроси, що використовуються для визначення (по суті) вбудованих типово-агностичних функцій
#define max(X,Y) (X>Y?X:Y)
У будь-якій мові, яка підтримує перевантаження функцій, це може бути емульовано набагато більш безпечним для типу способом, перевантаживши функції правильного типу або, мовою, яка підтримує дженерики, загальною функцією. Макрос із задоволенням намагатиметься порівняти будь-що, включаючи вказівники чи рядки, які можуть скластись, але майже точно не те, що ви хотіли. З іншого боку, якщо ви зробили безпечні для макросу типи, вони не надають жодних переваг та зручності над перевантаженими функціями.
- Макроси, що використовуються для вказівки ярликів до часто використовуваних елементів.
#define p printf
Це легко замінюється функцією, p()
яка робить те саме. Це досить задіяно в C (вимагає використання va_arg()
сімейства функцій), але в багатьох інших мовах, які підтримують змінну кількість аргументів функції, це набагато простіше.
Підтримка цих функцій в рамках мови , а не в спеціальному макромові простіше, менш схильна до помилок і набагато менш заплутане інше читанні коди. Насправді я не можу придумати єдиного випадку використання для макросів, які не можна легко дублювати іншим способом. Тільки місце , де макроси дійсно корисні, коли вони прив'язані до умовної компіляції конструкцій , таких як #if
( і т.д.).
З цього питання я не буду сперечатися з вами, оскільки вважаю, що непроцесорні рішення умовної компіляції в популярних мовах надзвичайно громіздкі (як введення байт-коду в Java). Але такі мови, як D, придумали рішення, які не потребують препроцесора і не є більш громіздкими, ніж використання умовних умов препроцесора, при цьому набагато менш схильні до помилок.