Кроме специфических макросов для работы с типами данных VMD имеет ряд общих макросов для разбора последовательностей.
При нормальном использовании Boost PP данные передаются в качестве аргументов на макрос в дискретных единицах так, что каждый параметр ожидает один тип данных. Типичный макрос может быть:
#define AMACRO(anumber,atuple,anidentifier) someoutput
где "субъект", имеющий форму (data1, data2, data3), сам может содержать различные типы элементов данных.
Это стандартный макроспроект, и внутренне это самый простой способ передавать макроданные туда и обратно. Библиотека Boost PP имеет богатый набор функциональных возможностей для работы со всеми ее высокоуровневыми типами данных, а вариадные данные с собственной более простой функциональностью также предлагают другую альтернативу представлению данных.
Иногда разработчики макросов, особенно для использования других программистов в конкретной библиотеке, выражали потребность в макропараметре, чтобы позволить больше C/C++. Например, синтаксис, где один параметр может имитировать вызов функции C++ или синтаксис модификации типа C, или другую более сложную конструкцию. Что-то вдоль линий:
areturn afunction ( aparameter1, aparameter2, aparameter3 )
или
( type ) data
и т.д. и т.д.
Другими словами, с синтаксического уровня при проектировании возможного макровхода можно проектировать данные параметров, чтобы они больше походили на C/C++. Когда макросы используются в библиотеке и все еще выполняют определенное количество препроцессорного метапрограммирования с таким смешанным входом токена?
VMD имеет функциональность, которая позволяет более чем одному типу токена предварительной обработки, за исключением «пустого» токена, который всегда относится к некоторому полному входу, быть частью одного параметра входных данных. Эти токены предварительной обработки в качестве одного параметра являются синтаксически последовательной серией данных. Единственным ограничением этой последовательной серии данных является то, что каждая верхняя часть данных этой серии имеет некоторый тип данных VMD. Это означает, что если некоторый ввод состоит из ряда типов данных, можно извлечь данные для каждого типа данных в этой серии.
На практике это означает, что, учитывая приведенные выше примеры, если «возвращение», «функция» и «данные» являются идентификаторами, можно было бы проанализировать любой из двух входов выше, чтобы можно было идентифицировать различные типы данных, участвующих и сделать препроцессорное метапрограммирование на основе этих результатов.
Я буду называть такие входные данные, которые состоят из всех типов данных верхнего уровня в серии, термином «последовательность». Каждый отдельный тип данных в последовательности называется «элементом». В этом определении последовательности мы можем иметь 0 или более элементов, так что последовательность является общим названием для любого ввода VMD. Таким образом, любая входная VMD может анализировать, будь то пустота, один элемент или более одного элемента в серии. Поэтому, когда мы говорим о макросах VMD, анализирующих входные данные, мы на самом деле говорим о макросах VMD, анализирующих последовательность. Таким образом, последовательность также может быть частью композитного типа данных Boost PP или вариадных данных, и VMD все еще может анализировать такую встроенную последовательность, если ее об этом попросят.
Парсирование последовательности означает, что VMD может последовательно проходить через каждый элемент последовательности, определять тип и данные каждого элемента, а затем переходить к следующему элементу. Парсинг является последовательным и может быть выполнен только в прямом направлении, но это может быть сделано любое количество раз. В терминах итератора C++ парсинг последовательности является передним итератором.
Работа с последовательностью эквивалентна использованию макросов VMD «генерично».
Прежде чем я объясню, как использовать последовательность с использованием общей функциональности VMD, я хотел бы сделать два замечания:
- Возможность работы с последовательностью, содержащей более одного типа данных, может быть легко использована. В общем, держать вещи простыми обычно лучше, чем делать вещи слишком сложными, когда дело доходит до синтаксической стороны вещей на компьютерном языке. Для использования должна быть понятна синтаксическая возможность макропараметра.
- Использование VMD для анализа отдельных типов данных последовательности занимает больше времени предварительной обработки, чем функциональность, предлагаемая с типами данных Boost PP, поскольку она основана на прямом доступе через каждый тип последовательности верхнего уровня.
Одним из ограничений в последовательности является то, что верхний уровень должен состоять из типов данных VMD, другими словами, препроцессорных токенов, которые понимает VMD. Под верхним уровнем подразумевается, что композитные данные Boost PP могут иметь элементы, которые VMD не может разобрать, но до тех пор, пока вход состоит из композитных типов данных, а не из внутренних неразборчивых элементов, VMD может разобрать вход. Поэтому, если препроцессорные данные являются одним из приведенных выше примеров, вы сможете успешно использовать VMD. Однако, если ваши данные препроцессора принимают форму:
&name identifier ( param )
или
identifier "string literal"
или
identifier + number
или
identifier += 4.3
и т.д. и т.д.
Вы не сможете анализировать данные с помощью VMD, поскольку «&», «string literal», «+», «+=» и «4.3» являются препроцессорными токенами, которые не являются типами данных верхнего уровня VMD, и поэтому VMD не может обрабатывать их на уровне разбора. Конечно, вы все еще можете передавать такие данные, как предварительная обработка ввода на макросы, но вы не можете использовать VMD для распознавания частей таких данных.
Это похоже на то, что VMD не может сказать вам, какой тип данных препроцессора в целом, используя любой из уже обсуждаемых макросов идентификации VMD, если тип не тот, который может обрабатывать VMD.
С другой стороны, вы все еще можете использовать VMD для анализа таких токенов во входе, если для этого вы используете типы данных Boost PP в качестве типов данных верхнего уровня. Например:
( &name ) identifier ( param )
или
identifier ( "string literal" )
или
identifier ( + ) number
или
identifier ( += ) 4 ( . ) 3
Последующие темы объясняют функциональность VMD для разбора последовательности для каждого отдельного типа данных VMD в этой последовательности.
Последовательность VMD можно рассматривать как один из трех общих типов:
- пустая последовательность
- Одиночная последовательность элементов
- Многоэлементная последовательность
Пустая последовательность — это просто пустой вход, который VMD называет «пустотой». Используйте ранее описанный макрос BOOST_VMD_IS_EMPTY для проверки пустой последовательности.
#include <boost/vmd/is_empty.hpp>
#define AN_EMPTY_SEQUENCE
BOOST_VMD_IS_EMPTY(AN_EMPTY_SEQUENCE) will return 1
Тип пустой последовательности BOOST_VMD_TYPE_EMPTY.
Одна последовательность элементов представляет собой один тип данных VMD. Это то, что мы ранее обсуждали в качестве данных, которые VMD может анализировать в этой документации с помощью наших идентифицирующих макросов. Вы можете использовать макрос BOOST_VMD_IS_UNARY для тестирования одной последовательности элементов.
#include <boost/vmd/is_unary.hpp>
#define A_SINGLE_ELEMENT_SEQUENCE (1,2)
BOOST_VMD_IS_UNARY(A_SINGLE_ELEMENT_SEQUENCE) will return 1
Тип последовательности одного элемента — это тип индивидуального типа данных. В нашем примере выше тип A_SINGLE_ELEMENT_SEQUENCE является BOOST_VMD_TYPE_TUPLE.
Многоэлементная последовательность состоит из более чем одного типа данных. Это «новый» тип, который может анализировать VMD. Вы можете использовать макрос BOOST_VMD_IS_MULTI для тестирования многоэлементной последовательности.
#define A_MULTI_ELEMENT_SEQUENCE (1,2) (1)(2) 45
A_MULTI_ELEMENT_SEQUENCE состоит из кортежа, за которым следует сек, за которым следует число.
#include <boost/vmd/is_multi.hpp>
BOOST_VMD_IS_MULTI(A_MULTI_ELEMENT_SEQUENCE) will return 1
Тип многоэлементной последовательности всегда BOOST_VMD_TYPE_SEQUENCE.
Тип последовательности можно получить с помощью макроса BOOST_VMD_GET_TYPE. Мы объясним это далее в документации.
К размеру любой последовательности можно получить доступ с помощью макроса BOOST_VMD_SIZE. Для пустой последовательности размер всегда 0. Для одной последовательности элементов размер всегда равен 1. Для многоэлементной последовательности размер представляет собой число отдельных типов данных верхнего уровня в последовательности.
#include <boost/vmd/size.hpp>
BOOST_VMD_SIZE(AN_EMPTY_SEQUENCE) will return 0
BOOST_VMD_SIZE(A_SINGLE_ELEMENT_SEQUENCE) will return 1
BOOST_VMD_SIZE(A_MULTI_ELEMENT_SEQUENCE) will return 3
Для последовательности VMD библиотека VMD предлагает два способа анализа отдельных типов данных:
- Последовательность может быть преобразована в любой из типов данных Boost PP или в вариадные данные, где каждый отдельный тип данных в последовательности становится отдельным элементом выбранного конкретного композитного типа данных. Преобразование в конкретный тип данных Boost PP или вариадные данные происходит медленно, поскольку оно основано на прямом доступе через каждый тип последовательности верхнего уровня, но после этого доступ к любому отдельному элементу происходит так же быстро, как доступ к любому элементу в типе данных Boost PP или среди вариадных данных.
- К последовательности можно получить доступ непосредственно через ее отдельные элементы. Это медленнее, чем доступ к элементу типа данных Boost PP или вариадным данным, но предлагает концептуальный доступ к исходной последовательности в виде ряда элементов.
Эти два метода будут обсуждаться в последующих темах.
Самый простой способ работы с последовательностью — преобразовать ее в тип данных Boost PP. Кроме того, вы также можете преобразовать последовательность в вариадные данные, даже если типы данных Boost PP имеют гораздо большую функциональность, чем вариадные данные в Boost PP.
Для преобразования последовательности в тип данных Boost PP или вариадные данные используются макросы:
- BOOST_VMD_TO_ARRAY (последовательность) для преобразования последовательности в массив
- BOOST_VMD_TO_LIST (последовательность) для преобразования последовательности в список
- BOOST_VMD_TO_SEQ (последовательность) для преобразования последовательности в seq
- BOOST_VMD_TO_TUPLE (последовательность) для преобразования последовательности в кортеж
- BOOST_VMD_ENUM (последовательность) для преобразования последовательности в вариадные данные
После преобразования элементы последовательности становятся элементами соответствующего композитного типа данных.
После преобразования элементов последовательности в элементы композитного типа данных для обработки каждого элемента может быть использована полная мощность этого композитного типа данных. Кроме того, программист может использовать VMD для определения типа отдельного элемента для дальнейшей обработки.
Для одиночных последовательностей элементов результатом всегда является одиночный элемент композитного типа данных. Для многоэлементных последовательностей результатом всегда является композитный тип данных, состоящий из более чем одного элемента.
Для последовательности, которая является пустой, результатом является пустота при преобразовании в данные seq, tuple или variadic; результатом является пустой массив или список при преобразовании в каждый из этих композитных типов данных соответственно.
#include <boost/vmd/enum.hpp>
#include <boost/vmd/to_array.hpp>
#include <boost/vmd/to_list.hpp>
#include <boost/vmd/to_seq.hpp>
#include <boost/vmd/to_tuple.hpp>
#define BOOST_VMD_REGISTER_ANID (ANID)
#define SEQUENCE_EMPTY
#define SEQUENCE_SINGLE 35
#define SEQUENCE_SINGLE_2 ANID
#define SEQUENCE_MULTI (0,1) (2)(3)(4)
#define SEQUENCE_MULTI_2 BOOST_VMD_TYPE_SEQ (2,(5,6))
BOOST_VMD_TO_ARRAY(SEQUENCE_EMPTY) will return an empty array '(0,())'
BOOST_VMD_TO_LIST(SEQUENCE_SINGLE) will return a one-element list '(35,BOOST_PP_NIL)'
BOOST_VMD_TO_SEQ(SEQUENCE_SINGLE_2) will return a one-element seq '(ANID)'
BOOST_VMD_TO_TUPLE(SEQUENCE_MULTI) will return a multi-element tuple '((0,1),(2)(3)(4))'
BOOST_VMD_ENUM(SEQUENCE_MULTI_2) will return multi-element variadic data 'BOOST_VMD_TYPE_SEQ,(2,(5,6))'
Вы можете использовать общий файл заголовка:
#include <boost/vmd/vmd.hpp>
Или вы можете использовать отдельные файлы заголовков для каждого из этих макросов. Отдельными файлами заголовка являются:
#include <boost/vmd/to_array.hpp>
#include <boost/vmd/to_list.hpp>
#include <boost/vmd/to_seq.hpp>
#include <boost/vmd/to_tuple.hpp>
#include <boost/vmd/enum.hpp>
Можно получить доступ к отдельному элементу последовательности. Это называется BOOST_VMD_ELEM. Макро принимает два необходимых параметра. Необходимыми параметрами являются номер элемента для доступа и последовательность в этом порядке. Число элементов является числом на основе 0, и его максимальное значение должно быть на единицу меньше размера последовательности.
Макрос BOOST_VMD_ELEM возвращает фактический элемент последовательности. Если первый требуемый параметр больше или равен размеру последовательности, макрос возвращает пустоту. Благодаря этому при использовании BOOST_VMD_ELEM на пустой последовательности, размер которой равен 0, всегда возвращается пустота.
#include <boost/vmd/elem.hpp>
#define BOOST_VMD_REGISTER_ANAME (ANAME)
#define A_SEQUENCE (1,2,3) 46 (list_data1,(list_data2,BOOST_PP_NIL)) BOOST_VMD_TYPE_SEQ ANAME
#define AN_EMPTY_SEQUENCE
BOOST_VMD_ELEM(0,A_SEQUENCE) will return (1,2,3)
BOOST_VMD_ELEM(1,A_SEQUENCE) will return 46
BOOST_VMD_ELEM(2,A_SEQUENCE) will return (list_data1,(list_data2,BOOST_PP_NIL))
BOOST_VMD_ELEM(3,A_SEQUENCE) will return BOOST_VMD_TYPE_SEQ
BOOST_VMD_ELEM(4,A_SEQUENCE) will return ANAME
BOOST_VMD_ELEM(5,A_SEQUENCE) will return emptiness
BOOST_VMD_ELEM(0,AN_EMPTY_SEQUENCE) will return emptiness
Прямой доступ к элементу последовательности происходит медленнее, чем доступ к элементу типа данных Boost PP или даже к вариадным данным, поскольку каждый доступ должен напрямую проходить через каждый элемент последовательности, чтобы добраться до того, к которому осуществляется доступ. Процесс последовательного разбора каждого элемента каждый раз медленнее, чем доступ к элементу типа данных Boost PP.
Вы можете использовать общий файл заголовка:
#include <boost/vmd/vmd.hpp>
Вы можете использовать индивидуальный файл заголовка:
#include <boost/vmd/elem.hpp>