Как генерировать имена случайных переменных в С++ с помощью макросов?

Я создаю макрос в С++, который объявляет переменную и присваивает ей некоторое значение. В зависимости от того, как используется макрос, второе вхождение макроса может переопределить значение первой переменной. Например:

#define MY_MACRO int my_variable_[random-number-here] = getCurrentTime();

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

Есть ли способ генерировать имена случайных переменных внутри макроса в С++?

- Изменить -

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

int unique_variable_name;
...
int unique_variable_name;

В этом случае, чтобы быть уникальным, имена переменных должны быть случайными.

Ответы

Ответ 1

Добавьте M4 в поток сборки? Этот макроязык имеет некоторые возможности с точки зрения состояния и может быть смешано с макросами CPP. Это, вероятно, не стандартный способ генерации уникальных имен в среде C, хотя я смог успешно использовать его таким образом.

Вы, вероятно, не хотите, чтобы случайный, BTW, основывался на том, как вы задавали свой вопрос. Вы хотите уникального.

Вы можете использовать __FILE__ и __LINE__ в расширении макроса, чтобы получить уникальность, о которой вы, похоже, собираетесь... эти метапарабелы определяются в контексте исходного файла, поэтому будьте осторожны, чтобы убедиться, что вы получаете то, что вы ищете (например, опасности более одного макроса в одной строке).

Ответ 2

Попробуйте следующее:

// This is some crazy magic that helps produce __BASE__247
// Vanilla interpolation of __BASE__##__LINE__ would produce __BASE____LINE__
// I still can't figure out why it works, but it has to do with macro resolution ordering
#define PP_CAT(a, b) PP_CAT_I(a, b)
#define PP_CAT_I(a, b) PP_CAT_II(~, a ## b)
#define PP_CAT_II(p, res) res

#define UNIQUE_NAME(base) PP_CAT(base, __COUNTER__)

__COUNTER__, по слухам, имеет проблемы с переносимостью. Если это так, вы можете использовать __LINE__ вместо этого и до тех пор, пока вы не вызываете макрос более одного раза в строке или не разделяете имена между единицами компиляции, вы будете в порядке.

Ответ 3

использовать __COUNTER__ (работает на gcc4.8, clang 3.5 и Intel icc v13, MSVC 2015)

#define CONCAT_(x,y) x##y
#define CONCAT(x,y) CONCAT_(x,y)
#define uniquename static bool CONCAT(sb_, __COUNTER__) = false

Ответ 4

Создание уникальных имен в препроцессоре сложно. Самое близкое, что вы можете получить, - это использовать __FILE__ и __LINE__ в качестве символа как popcnt. Если вам действительно нужно генерировать уникальные глобальные имена символов, тогда я последую его предложению об использовании чего-то вроде M4 или Perl script в вашей сборке.

Вам могут не понадобиться уникальные имена. Если ваш макрос может наложить новую область, то вы можете использовать одно и то же имя, поскольку оно просто затеняет другие определения. Обычно я следую общим советам обертывания макросов в циклах do { ... } while (0). Это работает только для макросов, которые являются утверждениями, а не выражениями. Макрос может обновлять переменные с использованием выходных параметров. Например:

#define CALC_TIME_SINCE(t0, OUT) do { \
     std::time_t _tNow = std::time(NULL); \
     (OUT) = _tNow - (t0); \
} while (0)

Если вы следуете нескольким правилам, вы обычно довольно безопасны:

  • Используйте символы подчеркивания или аналогичные соглашения об именах для символов, определенных в макросе. Это предотвратит возникновение проблем, связанных с параметром с использованием того же символа.
  • Используйте только входные параметры один раз и всегда окружайте их круглыми скобками. Это единственный способ заставить макросы работать с выражениями как входные данные.
  • Используйте idiom do { ... } while (0), чтобы убедиться, что макрос используется только как оператор и чтобы избежать других проблем с текстовой заменой.

Ответ 5

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

#define MY_MACRO(varname) int varname = getCurrentTime();

Ответ 6

Мне понадобилось нечто подобное для случая, когда у меня не было каких-либо профилирующих инструментов, но я хотел рассчитать, сколько потоков было внутри определенного блока кода, а также количество времени (тиков), потраченное в этом блоке код в каждом потоке. В этом случае для каждого блока нужна уникальная статическая переменная, доступная для всех потоков, и мне нужно было позже ссылаться на эту переменную на incr (я использовал API протоколирования, а не printf в фактическом коде, но это также работает), Сначала я подумал, что я очень умный, делая следующее:

#define PROF_START { \
    static volatile int entry_count##___FUNCTION__##__LINE__ = 0; int *ptc = &entry_count##___FUNCTION__##__LINE__; \
    clock_t start, end; \
    start = times(0); \
    (*ptc)++;

Но потом я понял, что это просто глупо, и компилятор C просто сделает это за вас, если каждое "статическое" объявление является его собственным блоком:

#include <stdio.h>
#include <sys/times.h>

#define PROF_START { \
    static int entry_count = 0; \
    clock_t start, end; \
    start = times(0); \
    entry_count++;


#define PROF_END \
    end = times(0); \
    printf("[%s:%d] TIMER: %ld:%d\n" , __FUNCTION__, __LINE__, end-start, entry_count); \
    entry_count--; \
    }

Обратите внимание на скобки открытия/закрытия в каждом макросе. Это не строго потокобезопасно, но для моих целей профилирования я мог предположить, что операции incr и decr были атомарными. Здесь образец рекурсии, который использует макросы

#define ITEM_COUNT 5

struct node {
   int data;
   struct node *next;
 };

revsort(struct node **head)
{
  struct node *current = *head;
  struct node *next_item;

  while (current->next)
  {
PROF_START
    next_item = current->next;
    current->next = next_item->next;
    next_item->next = *head;
    *head = next_item;
PROF_END
  }
}

rrevsort(struct node **head)
{
  struct node *current = *head;
  struct node *next_item = current->next;

PROF_START
  current->next = 0;
  if (next_item)
  {
   *head = next_item;
    rrevsort(head);
    next_item->next = current;
  }
PROF_END

}

printnode(struct node *head)
{
  if (head)
  {
    printf("%d ", head->data);
    printnode(head->next);
  }
  else
    printf("\n");

}

main()
{

  struct node node_list[ITEM_COUNT];
  struct node *head = &node_list[0];
  int i;

  for (i=0; i < ITEM_COUNT - 1; i++)
  {
PROF_START
      node_list[i].data = i;
      node_list[i].next = &node_list[i+1];
PROF_END
  }
  node_list[i].data = i;
  node_list[i].next = 0;

  printf("before\n");
  printnode(head);
  revsort(&head);
  printf("after\n");
  printnode(head);
  rrevsort(&head);
  printf("before\n");
  printnode(head);
}

Дополнительный намек, вышеупомянутая программа является общим вопросом для интервью. Выдержка из "nm -A":

macro:0804a034 b entry_count.1715
macro:0804a030 b entry_count.1739
macro:0804a028 b entry_count.1768
macro:0804a02c b entry_count.1775

Ответ 7

Вот краткое определение макроса для генерации одноэлементного шаблона выше.

#define SINGLETON_IMPLIMENTATION(CLASS_NAME) static CLASS_NAME *g##CLASS_NAME = nil; + (CLASS_NAME *)instance { @synchronized(self) { if (g##CLASS_NAME == nil) g##CLASS_NAME = [self new]; } return g##CLASS_NAME; }

#define SINGLETON_DECLARATION(CLASS_NAME) + (CLASS_NAME *)instance;

Ответ 8

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

Если вы хотите, чтобы случайный элемент в случайном массиве содержал определенное значение, вы можете сделать это:

std::vector< std::vector<int> > m_vec;

Затем заверните его в класс, чтобы разработчик мог установить только номер:

void set(int foo)
{
    m_vec[random()][random()] = foo;
}

Есть ли причина, по которой вам нужен макрос? Случайное имя переменной звучит опасно, что, если он выбирает что-то уже определенное где-то еще в коде?