Переопределение для циклов с препроцессором - PullRequest
4 голосов
/ 06 марта 2012

Я хочу сделать что-то злое. Я хочу переопределить циклы, чтобы изменить условные выражения. Есть ли способ сделать это?

Из документации GCC поддерживается переопределение ключевых слов: http://gcc.gnu.org/onlinedocs/cpp/Macros.html

Я пытаюсь создать «вероятностную» обертку для C ++, чтобы узнать, смогу ли я сделать с ней что-нибудь интересное.

#include <iostream>
#include <cstdlib>

#define P 0.85

#define RANDOM_FLOAT  ((float)rand()/(float)RAND_MAX)

#define RANDOM_CHANCE (RANDOM_FLOAT < P)

#define if(a) if((a) && RANDOM_CHANCE)

#define while(a) while((a) && RANDOM_CHANCE)


// No more for loops or do-while loops
#define do
#define goto

// Doesn't work :(
//#define for(a) for(a) if(!RANDOM_CHANCE) { break; } 


int main() {
    srand(time(NULL));

    //Should output a random list of Y's and N's
    for(int i=0; i < 100; ++i) {
        if(i < 100) {
            std::cout << "Y";
        } else {
            std::cout << "N";
        }
    }

    std::cout << std::endl;

    // Will loop for a while then terminate
    int j = 0;
    while(j < 100) {
        ++j;
        std::cout << j << "\n";
    }

   std::cout << std::endl;

   return 0;
}

Возможно, более законное использование будет подсчитывать количество итераций цикла в работающей программе, например, путем отображения

for(int i=0; i < 10; ++i)

до

for(int i=0; i < 10; ++i, ++global_counter)

Можно ли выполнить то, что я пытаюсь сделать?


РЕДАКТИРОВАТЬ: Спасибо за ответы - я действительно ценю это!

Одна из вещей, которые я могу сделать с этим, - симулировать честную монету (фактически заставить P быть равной 0,5), заметив, что если у вас есть предвзятая монета, Pr (HT) == Pr (TH). Я не обнаружил этот трюк, но он полезен. Это означает, что вы можете аппроксимировать любое распределение вероятностей для P.

bool coin() {
  bool c1 = false;
  bool c2 = false;

  if(true) {
    c1 = true;
  }
  if(true) {
    c2 = true;
  }

  //If they have different faces.
  bool valid = (c1 && !c2) || (!c1 && c2);
  bool result = c1;

  if(valid) { return result; }

  return coin();
}

Ответы [ 3 ]

8 голосов
/ 06 марта 2012

Вы пытались добавить else в ваш for цикл?Это должно заставить его работать.

#define for(a) for(a) if(!RANDOM_CHANCE) { break; } else

for (int i = 0; i < 10; ++i)
{
    // do something
}

/* should roughly compile to:
for (int i = 0; i < 10; ++i) if(!RANDOM_CHANCE) { break; } else
{
    // do something
}

or (differing only in whitespace):

for (int i = 0; i < 10; ++i)
    if(!RANDOM_CHANCE)
    {
        break;
    }
    else
    {
        // do something
    }
*/
4 голосов
/ 06 марта 2012

Вы уже показали это. Вот скомпилированный пример:

#include <iostream>

unsigned globalCounter = 0;

#define for(x)  for(x, ++globalCounter)

int main () {
    for (int i=0; i<10; ++i);
    for (int i=0; i<10; ++i);

    std::cout << globalCounter << '\n';
}

Это выводит 20. Однако существующий код, такой как

for (int i=0; i<x; ++i, foobar+=20)

прервется, поскольку теперь он передает два аргумента макросу for.

Что вам понадобится, так это перегрузка макросов или макросы с переменным числом аргументов. Последнее поддерживается в C99 и C ++ 11 выводит его, поэтому, если вы хотите, чтобы зло случилось:

unsigned globalCounter = 0;
#define for(...)  for(__VA_ARGS__, ++globalCounter)

#include <iostream>

void main () {
    int another = 0;
    for (int i=0; i<10; ++i, ++another);
    for (int i=0; i<10; ++i, ++another);

    std::cout << globalCounter << ' ' << another << '\n';
}

1012 * тогда *

g++ --std=c++0x source.cc
./a.out
20 20

Я не поддерживаю это. Макросы могут навредить вашим детям.

1 голос
/ 06 марта 2012

Это не работает, потому что блок больше не принадлежит оператору for.Преобразование становится

for (int i=0; i < 100; ++i)
{
   if (!RANDOM_CHANCE)
   {
      break;
   }
}

// unrelated to the 'for' above.
{
    if(i < 100) {
        std::cout << "Y";
    } else {
        std::cout << "N";
    }
}

Вместо этого вы можете просто написать

#define for(...) for(__VA_ARGS__) if(RANDOM_CHANCE)

Таким образом, код становится

for (int i=0; i < 100; ++i)
    if (RANDOM_CHANCE)
    {
        if(i < 100) {
            std::cout << "Y";
        } else {
            std::cout << "N";
        }
    }

(обратите внимание, что я использовал variadicмакросы .)


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

#define for(...) for(__VA_ARGS__, ++ global_counter)

В противном случае (например,в коде есть for(;;), я не думаю, что это возможно.

...