Библиотека VMD обеспечивает возможность создания макроса, который принимает различные типы параметров и, следовательно, может генерировать различные выходные данные в зависимости от типов параметров, а также их значений.
Это эквивалентно тому, как перегруженные функции обеспечивают возможность для одноимённой функции предоставлять различные функции в зависимости от типов параметров.
В случае макросов, где не допускается более одного макроса с одинаковым названием, но разное макрорасширение, одно макроимя может создавать разные расширения.
Как простой пример:
#include <boost/vmd/is_seq.hpp>
#include <boost/vmd/is_tuple.hpp>
#define AMACRO(param) \
BOOST_PP_IIF \
( \
BOOST_VMD_IS_SEQ(param), \
Seq, \
BOOST_PP_IIF \
( \
BOOST_VMD_IS_TUPLE(param), \
Tuple, \
Unknown \
) \
)
Если пройденный парам является seq, то выход макроса — «Seq». Если пройденный парам является кортежем, то выход макроса — «Tuple». В противном случае выход макроса «неизвестен».
Очевидно, что могут быть созданы гораздо более сложные случаи, в которых типы и значения различных параметров анализируются для получения переменного макровых выходов в зависимости от входа. Использование вариадных макросов, макросов с переменными числами и типами аргументов дает макропрограммисту еще большую свободу в разработке макросов с гибкостью.
Еще одной особенностью библиотеки VMD является возможность разбора идентификаторов. Создана система регистрации идентификаторов, которую может распознать VMD. После регистрации идентификатора VMD может распознать его как часть макросъемки в качестве идентификатора и вернуть идентификатор. Кроме того, VMD может сравнивать идентификаторы для равенства или неравенства после предварительного обнаружения идентификатора с использованием системы VMD для предварительного обнаружения идентификаторов.
В качестве другого простого примера:
#include <boost/vmd/is_identifier.hpp>
#define BOOST_VMD_REGISTER_NAME (NAME)
#define BOOST_VMD_REGISTER_ADDRESS (ADDRESS)
#define AMACRO1(param) \
BOOST_PP_IIF \
( \
BOOST_VMD_IS_IDENTIFIER(param), \
AMACRO1_IDENTIFIER, \
AMACRO1_NO_IDENTIFIER \
) \
(param)
#define AMACRO1_IDENTIFIER(param) AMACRO1_ ## param
#define AMACRO1_NO_IDENTIFIER(param) Parameter is not an identifier
#define AMACRO1_NAME Identifier is a NAME
#define AMACRO1_ADDRESS Identifier is an ADDRESS
Здесь мы используем систему регистрации идентификаторов VMD для определения и обработки конкретного идентификатора, который мы можем ожидать в качестве макропараметра. Если входом в "AMACRO1" является "NAME", то выходом является "Identifier is a NAME". Если входом в «AMACRO1» является «ADDRESS», то выходом является «Identifier is an ADDRESS». В противном случае на выходе будет указано: «Параметр не является идентификатором».
Предварительное обнаружение идентификатора делает вещи более ясными, позволяя нам обнаруживать в VMD, соответствует ли макросъемка определенному идентификатору. Используя ту же настройку, что и в предыдущем примере, но с предварительным обнаружением идентификатора:
#include <boost/vmd/is_identifier.hpp>
#define BOOST_VMD_REGISTER_NAME (NAME)
#define BOOST_VMD_DETECT_NAME_NAME
#define BOOST_VMD_REGISTER_ADDRESS (ADDRESS)
#define BOOST_VMD_DETECT_ADDRESS_ADDRESS
#define AMACRO2(param) \
BOOST_PP_IIF \
( \
BOOST_VMD_IS_IDENTIFIER(param,NAME), \
AMACRO2_NAME, \
BOOST_PP_IIF \
( \
BOOST_VMD_IS_IDENTIFIER(param,ADDRESS), \
AMACRO2_ADDRESS, \
AMACRO2_NO_IDENTIFIER \
) \
) \
(param)
#define AMACRO2_NO_IDENTIFIER(param) Parameter is not a NAME or ADDRESS identifier
#define AMACRO2_NAME(param) Identifier is a NAME
#define AMACRO2_ADDRESS(param) Identifier is an ADDRESS
Если входом в "AMACRO2" является "NAME", то выходом является "Identifier is a NAME". Если входом в «AMACRO2» является «ADDRESS», то выходом является «Identifier is an ADDRESS». В противном случае на выходе будет указано: «Параметр не является идентификатором имени или адреса».
Библиотека VMD также имеет 2 различных подтипа идентификаторов, которые всегда могут быть распознаны. Первыми являются числа, эквивалентные числу в Boost PP, числовые значения с диапазоном 0-256. Вторыми являются v-типы, которые представляют собой идентификаторы, начинающиеся с BOOST_VMD_TYPE_, за которыми следует имя для типа данных. В качестве примера, v-типом PP-кортежа является BOOST_VMD_TYPE_TUPLE, а v-типом самого v-типа является BOOST_VMD_TYPE_TYPE. Все типы данных имеют свой собственный идентификатор типа v; типы распознаются макросами VMD и могут передаваться в качестве входных данных, как и любые другие типы данных, распознаваемых VMD.
Идентификационная система VMD позволяет конечному пользователю создавать собственные идентификаторы подтипов.
Еще одна причина использования VMD заключается в том, что VMD понимает «последовательности» типов данных VMD. Вы можете иметь последовательность типов данных, и VMD может преобразовать последовательность в любой из типов данных Boost PP или получить доступ к любому отдельному типу данных в последовательности.
#include <boost/vmd/elem.hpp>
#include <boost/vmd/to_tuple.hpp>
#define BOOST_VMD_REGISTER_NAME (NAME)
#define ASEQUENCE (1,2) NAME 147 BOOST_VMD_TYPE_NUMBER (a)(b)
BOOST_VMD_TO_TUPLE(ASEQUENCE)
BOOST_VMD_ELEM(2,ASEQUENCE)
Наше первое расширение возвращает кортеж:
((1,2),NAME,147,BOOST_VMD_TYPE_NUMBER,(a)(b))
Второе расширение возвращает элемент последовательности:
147
Последовательности дают макропрограммисту возможность принимать входные данные от пользователя, которые могут более точно имитировать конструкции C++.
Другая причина использования VMD заключается в том, что VMD понимает типы данных. Помимо вопроса о том, является ли конкретный вход определенным типом данных, вы можете использовать макрос. BOOST_VMD_GET_TYPE для получения данных типа VMD.
#include <boost/vmd/get_type.hpp>
BOOST_VMD_GET_TYPE((1,2))
BOOST_VMD_GET_TYPE(235)
и т.д.
Есть еще много функциональных возможностей VMD, но, надеюсь, это краткое введение того, что может сделать VMD, заинтересует вас, чтобы вы прочитали дальше, чтобы понять функциональность VMD для макропрограммиста.