Будет работать следующий код:
#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)
#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
#define MYLIST(XX) \
DELAYED_CALL(XX, 1, hello) \
DELAYED_CALL(XX, 2, world)
int foo = EVAL2(MYLIST(AA)) 0;
Вывод: int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
К сожалению, у меня нет глубокого понимания , почему это работает; Я только что попробовал несколько трюков, которые, как правило, помогают в подобных случаях. Но я могу объяснить кое-что из этого.
Иногда макрос помечается как «не подлежащий дальнейшему расширению». Флаг обычно устанавливается, когда вы начинаете расширять его, и сбрасывается, когда расширение заканчивается. Это имеет тенденцию предотвращать рекурсию.
Когда макрос разворачивается в токены, которые обычно были бы похожими на функции вызовами макросов, иногда мы уже прошли этап, на котором они были бы расширены.
Мы можем обойти первую проблему, отложив расширение макроса до точки, где флаг не вызовет никаких проблем, добавив второй макрос в , создавая вызов макроса при его оценке. Вот что делает DELAYED_CALL
. Но при этом мы сталкиваемся со второй проблемой, поэтому нам нужно добавить несколько вызовов в EVAL
для повторного сканирования макроса (аргументы к функциональному макросу всегда сканируются, поэтому передается последовательность токенов в функциональный макрос, который просто выводит свои аргументы, вызывает повторное сканирование).
1117 Иногда нам нужно пару повторных проверок, чтобы все работало. EVAL2(X)
это просто сокращение от EVAL(EVAL(X))
. Иногда потребуется больше уловок.
Приведенный ниже код немного разъясняет, что происходит. Обратите внимание, что версии MYLIST2 требуется на один меньше EVAL; это потому, что AA вызывает MYLIST, а MYLIST2 - тот, у которого установлен флаг нарушителя.
#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EVAL3(...) EVAL2(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)
#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
#define MYLIST(XX) \
DELAYED_CALL(XX, 1, hello) \
DELAYED_CALL(XX, 2, world)
#define MYLIST2(XX) \
XX(1, hello) \
XX(2, world)
% MYLIST
int foo = MYLIST(AA) 0;
int foo = EVAL(MYLIST(AA)) 0;
int foo = EVAL2(MYLIST(AA)) 0;
% MYLIST2
int foo = MYLIST2(AA) 0;
int foo = EVAL(MYLIST2(AA)) 0;
int foo = EVAL2(MYLIST2(AA)) 0;
Вывод этого будет:
% MYLIST
int foo = AA (1, hello) AA (2, world) 0;
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
% MYLIST2
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
(Знаки% не являются чем-то особенным. Я просто хотел, чтобы комментарии отображались в выводе, а комментарии в стиле C удалить во время предварительной обработки.)
Дополнительная литература . Автор статьи понимает это гораздо лучше, чем я.
Результаты не имеют смысла.
С аппаратной точки зрения, < = с loopNuumber-1 представит одно дополнительное вычисление, чтобы сделать loopNumber-1 на повторение. Таким образом, я принимаю это < займет меньше времени, если не то же количество времени, чем < =
Преждевременная оптимизация является корнем всего зла. Пойдите с удобочитаемостью, если нет действительно серьезное основание взволновать приблизительно <
более чем <=
.
Никакое различие в скорости, но < более вероятно, будет корректно на языке с массивами на основе 0. Кроме того, если Вы хотите выполнить итерации вниз вместо, можно сказать:
for (i = 7; --i >= 0; ) ...
Я следую первому методу, поскольку та идиома часто повторяется.
для (интервал индексируют = 0; индекс < array.length; я ++)
Строка s = oldString.substring (0, numChars);
и т.д.
я привык к верхней границе, исключаемой, и предпочел бы сохранять ее тем путем, если нет серьезное основание изменить ее. (пример - 1 основанная индексация)
Это не должно быть различие в производительности, по крайней мере, с x86 компиляторами. JL и JLE работают то же время, как только я знаю. И что касается удобочитаемости, с помощью "< 7 дюймов для массива семи элементов имеют смысл.
Пойдите для удобочитаемости сначала, оптимизируйте позже (хотя вполне честно я не могу вообразить различие, которое было бы заметным).
знать, что 0-> K форма является идиомой C, перенесенной в C# при наличии массивов быть 0 базирующимися. Следуйте за идиомой и не нарушайте принципал наименьшего количества удивления.
Я предпочитаю это:
for (int i = 0; i < 7; i++)
Однако (это - просто мысль), удобочитаемости ее, возможно, придется сделать, на основе ли массивы 0 (C#, Java) или на основе 1 (VB.NET). Я говорю это, потому что, когда Вы работаете с массивами на основе 0, Вы входите в мышление, которое 0-6 работало бы 7 раз. Я думаю 0-6, более интуитивно, чем 1-7. С другой стороны я происхожу из C++, Java, фона C#.
Я бы сказал, что это должно быть <.
Зачем использовать много слов, когда несколько подойдут. Один тест легче понять, чем два. Следовательно, в будущем легче проводить модульное тестирование и модифицировать.
Разница небольшая? Да. Но зачем добавлять какие-либо сложности, если это не оправдано.
Наконец, вы не полагаетесь ни на какой оптимизатор или реализацию интерпретатора, когда код оптимизируется с самого начала. По словам Эйнштейна, «делайте это как можно проще, но не проще».