VMD может определить, является ли предварительная обработка ввода данным типа Boost PP. Макросы VMD для этого:
- BOOST_VMD_IS_ARRAY для массива
- BOOST_VMD_IS_LIST Для списка
- BOOST_VMD_IS_SEQ для сек
- BOOST_VMD_IS_TUPLE для кортежа
Каждый из этих макросов берет один параметр в качестве ввода и возвращает 1, если параметр является соответствующим типом данных, и 0, если это не так.
И массив, и непустый список также являются кортежом. Если у вас есть:
#define ANARRAY (3,(a,b,c))
#define ALIST (a,(b,(c,BOOST_PP_NIL)))
#define ATUPLE (a,b,c)
#define ASEQ (a)(b)(c)
затем
#include <boost/vmd/is_tuple.hpp>
BOOST_VMD_IS_TUPLE(ANARRAY) returns 1
BOOST_VMD_IS_TUPLE(ALIST) returns 1
BOOST_VMD_IS_TUPLE(ATUPLE) returns 1
BOOST_VMD_IS_TUPLE(ASEQ) returns 0
Список, первый элемент которого является числом 2, а второй элемент не является маркером конца списка BOOST_PP_ NIL также является массивом. Если у вас есть:
#define ALIST (2,(3,BOOST_PP_NIL))
#define ALIST2 (2,(3,(4,BOOST_PP_NIL)))
#define ALIST3 (2,BOOST_PP_NIL)
#include <boost/vmd/is_array.hpp>
#include <boost/vmd/is_list.hpp>
BOOST_VMD_IS_LIST(ALIST) returns 1
BOOST_VMD_IS_LIST(ALIST2) returns 1
BOOST_VMD_IS_LIST(ALIST3) returns 1
BOOST_VMD_IS_ARRAY(ALIST) returns 1
BOOST_VMD_IS_ARRAY(ALIST2) returns 1
BOOST_VMD_IS_ARRAY(ALIST3) returns 0
Одноэлементный кортеж также является одним элементом seq. Если у вас есть:
#define ASE_TUPLE (a)
затем
#include <boost/vmd/is_seq.hpp>
#include <boost/vmd/is_tuple.hpp>
BOOST_VMD_IS_TUPLE(ASE_TUPLE) returns 1
BOOST_VMD_IS_SEQ(ASE_TUPLE) returns 1
Форма массива представляет собой двухэлементный кортеж, где первый элемент представляет собой число, а второй элемент представляет собой кортеж. Число определяет размер кортежа. Поскольку при использовании вариадных макросов никогда не нужно указывать размер кортежа, массив в значительной степени устарел. Тем не менее, VMD поддерживает его.
Проблема при тестировании массива заключается в том, что если первый элемент не подчиняется ограничениям на тестирование для числа, вы получите UB.
#include <boost/vmd/is_array.hpp>
#include <boost/vmd/is_tuple.hpp>
#define A_TUPLE (&anything,(1,2))
BOOST_VMD_IS_ARRAY(A_TUPLE) will give UB due to the constraint
BOOST_VMD_IS_TUPLE(A_TUPLE) will return 1
Когда VMD пытается анализировать массив, как это происходит при использовании BOOST_VMD_IS_ARRAY, сначала нужно посмотреть, представляет ли синтаксис набор с двумя элементами. Затем он смотрит, является ли второй элемент сам по себе кортежом. Наконец, если он удовлетворен тем, что предыдущие проверки действительны, он проверяет, является ли первый элемент числом или нет. Именно в этом окончательном тесте первый элемент является действительным числом, где UB может происходить, как объясняется в теме «Числа».
Форма непустого списка представляет собой двухэлементный кортеж, где первый элемент является главой списка и может быть чем угодно, а второй элемент сам по себе является списком или идентификатором конца списка BOOST_PP_NIL.
Проблема при тестировании списка заключается в том, что если второй элемент не выполняет ограничение на тестирование идентификатора, поскольку BOOST_PP_NIL является идентификатором и тестируется как таковой, вы получите UB.
#include <boost/vmd/is_list.hpp>
#include <boost/vmd/is_tuple.hpp>
#define A_TUPLE (element,&anything)
BOOST_VMD_IS_LIST(A_TUPLE) will give UB due to the constraint
BOOST_VMD_IS_TUPLE(A_TUPLE) will return 1
Форма пустого списка — идентификатор BOOST_PP_NIL. Поэтому:
#include <boost/vmd/is_identifier.hpp>
#include <boost/vmd/is_list.hpp>
#define A_BAD_EMPTY_LIST &BOOST_PP_NIL
BOOST_VMD_IS_LIST(A_BAD_EMPTY_LIST) will give UB due to the constraint
BOOST_VMD_IS_IDENTIFIER(A_BAD_EMPTY_LIST) will give UB due to the constraint
Когда VMD пытается разобрать список, как это происходит при использовании BOOST_VMD_IS_LIST, сначала нужно посмотреть, представляет ли синтаксис набор с двумя элементами. Если это не кортеж с двумя элементами, он проверит конец списка. Если это кортеж с двумя элементами, он смотрит, является ли второй элемент списком. В обоих этих путях он всегда должен в конечном итоге проверить нотацию конца списка BOOST_PP_NIL, которая является идентификатором в VMD. Именно в этом окончательном тесте нотация в конце списка существует как идентификатор VMD, где UB может происходить, как объясняется в теме «Идентификаторы».
Как уже упоминалось ранее, один элемент крана также является одним элементом сек.
Однако, как будет обсуждаться позже в документации, когда VMD должна определить тип таких данных, она всегда возвращает их в виде кортежа (BOOST_VMD_TYPE_TUPLE).
Если наши данные состоят из более чем одного последовательного набора из одного элемента, данные представляют собой сек:
#include <boost/vmd/is_seq.hpp>
#include <boost/vmd/is_tuple.hpp>
#define ST_DATA (somedata)(some_other_data)
BOOST_VMD_IS_SEQ(ST_DATA) will return 1
BOOST_VMD_IS_TUPLE(ST_DATA) will return 0
Однако, если данные состоят из смеси, мы должны различать, как VMD анализирует данные. Правило заключается в том, что VMD всегда анализирует один элемент кортежа как кортеж, если за ним не следует один или несколько отдельных элементов кортежа, и в этом случае это сек.
#define ST_DATA (somedata)(element1,element2)
VMD анализирует вышеуказанные данные как 2 последовательных кортежа. Первый кортеж — это кортеж «(somedata)» одного элемента, а второй кортеж — это кортеж «(element1,element2)».
#define ST_DATA (element1,element2)(somedata)
VMD анализирует вышеуказанные данные как 2 последовательных кортежа. Первый кортеж - это многоэлементный кортеж "(элемент1, элемент2)", а второй кортеж - это одноэлементный кортеж "(некоторые данные)".
#define ST_DATA (somedata)(some_other_data)(element1,element2)
VMD анализирует вышеуказанные данные как seq, за которым следует кортеж. Сек — это «(somedata)(some_other_data)», а кортеж — «(element1,element2)».
Массив и список могут быть пустыми.
Пустая матрица имеет форму '(0,())' и является совершенно допустимой.
Вы можете проверить пустой массив с помощью макроса BOOST_VMD_IS_EMPTY_ARRAY.
#include <boost/vmd/is_array.hpp>
#include <boost/vmd/is_empty_array.hpp>
#define AN_ARRAY (1,(1))
#define AN_EMPTY_ARRAY (0,())
BOOST_VMD_IS_ARRAY(AN_ARRAY) will return 1
BOOST_VMD_IS_ARRAY(AN_EMPTY_ARRAY) will return 1
BOOST_VMD_IS_EMPTY_ARRAY(AN_EMPTY_ARRAY) will return 1
BOOST_VMD_IS_EMPTY_ARRAY() will return 0
BOOST_VMD_IS_EMPTY_ARRAY(AN_ARRAY) will return 0
Пустой список имеет форму «BOOST_PP_NIL» и является абсолютно действительным списком.
Вы можете проверить пустой список с помощью макроса BOOST_VMD_IS_EMPTY_LIST.
#include <boost/vmd/is_empty_list.hpp>
#include <boost/vmd/is_list.hpp>
#define A_LIST (1,BOOST_PP_NIL)
#define AN_EMPTY_LIST BOOST_PP_NIL
BOOST_VMD_IS_LIST(A_LIST) will return 1
BOOST_VMD_IS_LIST(AN_EMPTY_LIST) will return 1
BOOST_VMD_IS_EMPTY_LIST(AN_EMPTY_LIST) will return 1
BOOST_VMD_IS_EMPTY_LIST() will return 0
BOOST_VMD_IS_EMPTY_LIST(A_LIST) will return 0
Ни сек, ни кортежи не могут быть пустыми при использовании Boost PP. Из-за этого, если вы преобразуете пустой массив или список в seq или tuple с помощью макросов Boost PP, вы получите неопределенное поведение.
Синтаксис '()', который называется пустой скобкой, не является ни секвой нулевого элемента, ни кортежом, состоящим из элементов. Скорее, это либо одноэлементный сек, содержание которого является пустотой, либо одноэлементный кортеж, содержание которого является пустотой.
VMD поддерживает синтаксис пустого скобка. Вы можете проверить это с помощью макроса BOOST_VMD_IS_PARENS_EMPTY
#include <boost/vmd/is_parens_empty.hpp>
#include <boost/vmd/is_seq.hpp>
#include <boost/vmd/is_tuple.hpp>
#define EMPTY_PARENS ()
#define TUPLE (0)
#define SEQ (0)(1)
BOOST_VMD_IS_TUPLE(EMPTY_PARENS) will return 1
BOOST_VMD_IS_SEQ(EMPTY_PARENS) will return 1
BOOST_VMD_IS_PARENS_EMPTY(EMPTY_PARENS) will return 1
BOOST_VMD_IS_PARENS_EMPTY() will return 0
BOOST_VMD_IS_PARENS_EMPTY(TUPLE) will return 0
BOOST_VMD_IS_PARENS_EMPTY(SEQ) will return 0
Компилятор VC++8 (Visual Studio 2005), который является самой старой версией VC++, поддерживаемой VMD, имеет проблемы с работой с синтаксисом пустых скобок. Поэтому, если вам нужно использовать VC++8, избегайте его использования, иначе вы должны быть в порядке, если хотите.
При использовании вариадных макросов тот факт, что массив может быть пустым, является его единственным преимуществом перед кортежем. В противном случае использование кортежа всегда проще, так как синтаксис проще; вам никогда не придется отмечать размер кортежа.
Поскольку VMD полностью поддерживает прохождение и возврат пустоты, вы можете использовать кортеж вместо массива во всех ситуациях и просто пройти или вернуть пустоту, чтобы представить «пустой» кортеж и эквивалент пустого массива.
Это понятие использования пустоты для представления «пустого» кортежа также может быть расширено на использование пустоты для представления «пустого» сек. Однако функциональность Boost PP не распознает пустоту как пустой кортеж или сек, и вы не можете работать с пустотой, чтобы представлять пустой кортеж или пустой сек, используя функциональность Boost PP для кортежа или сек. Для решения с использованием пустоты для представления «пустого» кортежа или «пустого» сек VMD имеет функциональность, которая будет объяснена, когда мы посмотрим на нашу последнюю область функциональности в VMD, полезные вариадные макросы не в Boost PP.
Вы можете использовать общий файл заголовка:
#include <boost/vmd/vmd.hpp>
Или вы можете использовать отдельные файлы заголовков для каждого из этих макросов. Отдельными файлами заголовка являются:
#include <boost/vmd/is_array.hpp>
#include <boost/vmd/is_list.hpp>
#include <boost/vmd/is_seq.hpp>
#include <boost/vmd/is_tuple.hpp>
#include <boost/vmd/is_empty_array.hpp>
#include <boost/vmd/is_empty_list.hpp>
#include <boost/vmd/is_parens_empty.hpp>