Трюк: заполнение значений массива с помощью макросов (генерация кода) - PullRequest
11 голосов
/ 21 мая 2011

Являются ли шаблоны C ++ просто замаскированными макросами?

Я читал вышеупомянутую тему, и вдруг мне пришла в голову эта идея: почему бы не попробовать написать несколько хитрых макросов, которые можно использоватьв нашем реальном коде (не только в виде головоломок, которые бесполезны в реальной жизни)?

Итак, первое, что пришло в голову: заполнить значения массива макросами:

int f(int &i) { return ++i; }

#define e100     r5(m20)
#define m20      m5,m5,m5,m5
#define m5       r5(e1)
#define e1       f(i)  //avoiding ++i right here, to avoid UB!
#define r5(e)    e,e,e,e,e

int main() {
        int i=0;           //this is used in the macro e1
        int a[] = {e100};  //filling array values with macros!
        int n  = sizeof(a)/sizeof(int);
        cout << "count = " << n << endl;
        for(int i = 0 ; i < n ; i++ ) 
            cout << a[i] << endl;
        return 0;
}

Вывод:

count = 100
1
2
3
4
.
.
.
100

Демонстрация в Интернете: http://www.ideone.com/nUYrq

Можем ли мы еще улучшить это решение с точки зрения компактности или универсальности (возможно, и того и другого)?Можем ли мы избавиться от переменной i, которая нам нужна в макросе?Или любое другое улучшение?

Я также хотел бы знать, является ли этот код допустимым как в C ++, так и в C (конечно, игнорируя часть печати)?

EDIT:

Iпонял, что порядок звонков на f() кажется еще неопределенным .Я не уверен, хотя, как я думаю, запятая в инициализации массива не , вероятно, такой же, как оператор запятой (в целом).Но если это так, можем ли мы этого избежать и какая часть стандарта говорит, что не указано ?

Ответы [ 5 ]

5 голосов
/ 21 мая 2011

Если вы хотите углубиться в программирование препроцессора, я могу рекомендовать только библиотеку Boost.Preprocessor в качестве строительного блока, вам не придется переписывать вещи с нуля.

ДляНапример, чтобы создать вашу таблицу, я бы использовал ( ideone ):

#include <iostream>

#include <boost/preprocessor/repetition/enum.hpp>

#define ORDER(z, n, text) n

int main() {
  int const a[] = { BOOST_PP_ENUM(100, ORDER, ~) };
  std::size_t const n = sizeof(a)/sizeof(int);

  std::cout << "count = " << n << "\n";

  for(std::size_t i = 0 ; i != n ; ++i ) 
    std::cout << a[i] << "\n";

  return 0;
}

И оставил бы все в Boost :): 1010 *

Примечание: это перечисляет от 0 до 99, а не от 1 до 100, есть другие операции, доступные для выполнения арифметики;)

РЕДАКТИРОВАТЬ: Как это работает?

Во-первых, я могу рекомендовать только запись документа для BOOST_PP_ENUM

BOOST_PP_ENUM - это макрос, который принимает 3 аргумента: (n, MACRO, data)

  • n: целое число
  • MACRO: макрос, принимающий 3 аргумента: (z, i, data)
  • data: некоторые данные для вашего удобства будут переданы в macro

Затем он будет заменен на n последовательных вызовов MACRO, разделенных запятыми:

MACRO(z, 0, data), MACRO(z, 1, data), ... , MACRO(z, n-1, data)

Вам решать, что делатьЕсли вы хотите с вашим MACRO.

Боюсь, я никогда не использовал аргумент z, он используется внутри, и теоретически вы можете использовать его для ускорения процесса.

5 голосов
/ 21 мая 2011

P99 имеет макрос, который делает именно то, что вам нужно

#include "p99_map.h"

int Ara[] = { P99_POSS(100) };

Преимущество этого метода в том, что он полностью компилируется, не требует динамической инициализации с функциями и т. Д.

Для вас, вероятно, недостатком является использование функций C99, в частности макросов с аргументами переменной длины.

3 голосов
/ 21 мая 2011

Нет, это недействительный код; поведение все еще не определено. Поскольку между элементами инициализации массива нет точек последовательности, вызов f () может происходить в любом порядке.

Возможно генерировать последовательности. Boost.Preprocessor делает это и использует такие последовательности для создания гораздо более интересных вещей.

2 голосов
/ 21 мая 2011

Я думаю, что все же template обеспечит превосходное решение, которое будет определенным и менее подверженным ошибкам.Смотрите следующий код;многие вещи рассчитываются только во время компиляции, и он должен генерировать эффективный код.

template<int VALUE, int INDEX, int SIZE, bool ALLOW>
struct Assign
{
  static void Element (int *p) 
  {
    Assign<VALUE + 1, INDEX + 1, SIZE, (INDEX < SIZE)>::Element(p);
    p[INDEX] = VALUE;
  }
};
template<int VALUE, int INDEX, int SIZE>
struct Assign<VALUE, INDEX, SIZE, false>
{
  static void Element (int *p) { p[INDEX] = VALUE; }
};

template<int START, int SIZE>
void Initialize (int (&a)[SIZE])
{
  Assign<START, 0, SIZE, true>::Element(a);
}

На первый взгляд это может быть немного сложно, но понятно.Это можно сделать еще более общим.Использование будет так просто:

int a[100];
Initialize<1>(a);  // '1' is the starting value

Это можно использовать для любого int a[N].Вот вывод кода .

1 голос
/ 21 мая 2011

Как насчет простой генерации кода.

#include <fstream> 

int main() {

    std::ofstream fout("sequence_macros.hpp");

    for(int i=1; i<=100; ++i)
    {
        fout << "#define e" << i << "(a) ";
        fout << "(a+0)";
        for(int j=1; j<i; ++j)
        {
            fout << ",(a+" << j << ")";
        }
        fout << '\n';
    }
}

Затем его можно использовать:

#include <iostream>
#include "sequence_macros.hpp"

int main()
{
   // Create an array with 157 elements, in
   // sequence, starting at 25
   int x[] = {e100(25),e50(25+100),e7(25+100+50)};
   int sz = sizeof(x) / sizeof(*x);
   for(int i=0; i<sz; ++i)
      std::cout << x[i] << '\n';
}

Генерация кода: http://www.ideone.com/iQjrj

Использование кода: http://ideone.com/SQikz

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...