Вопрос по c++, random – Как генерировать случайные имена переменных в C ++ с помощью макросов?

15

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

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

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

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

-- Редактировать --

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

int unique_variable_name;
...
int unique_variable_name;

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

Я немного озадачен тем, как это было бы полезно. Получит ли тогда программист какое-нибудь использование ссылок my_variable_ * позже? getCurrentTime () имеет некоторый полезный побочный эффект? SingleNegationElimination
Конечно, вы имеете в виду уникальные имена переменных, а не случайные? anon

Ваш Ответ

8   ответов
11

ности с сохранением состояния и может успешно смешиваться с макросами CPP. Вероятно, это не стандартный способ генерирования уникальных имен в среде Си, хотя я смог успешно использовать его таким образом.

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

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

не могли бы вы #define A_VAR UNIQUE_VAR_MACRO () \ n int A_VAR = 1; printf ("% x", A_VAR); .... #UNDEF A_VAR ??
Ого, ТАК теперь есть форматирование комментариев! В любом случае, это действительно должно быть COUNTER с двумя подчеркиваниями перед и после него.
Также естьCOUNTER макрос, который генерирует новое целое число каждый раз, когда он вызывается, но это нестандартно.
Это не сработает для меня, поскольку я могу использовать макрос более одного раза в одном и том же файле и ссылаться на него позже в другом макросе. & Quot; СЧЕТЧИК __ & quot; (Я знаю, что все вместе) может работать, но мне нужно знать текущее значение счетчика, не увеличивая его. freitass
@freitass - посмотрите на ответ Дэйва Допсона ниже. Я хотел использовать__LINE__ в моем макросе и при вставке токена## это просто вставлено__LINE__, Однако это можно взломать. Мой пример оттуда, однако, можно решить без этого взлома, с помощью#define FOR(ii, ll, uu) for(int ii##lim = (uu), ii = (ll); ii < ii##lim; ++ii), - но это C ++ или C99 (для определения объема). ПохожийFORМакро для старого C потребует технологии Дейва.
3

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

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

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

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

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

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

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

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

На самом деле я не хочу "хочу" это макрос, но проблема, которую нужно решить, - это макрос. Ваш ответ дал мне идею, я создал класс для хранения значений (управление списком вместо объявления переменной каждый раз). freitass
1

когда у меня не было никаких инструментов профилирования, но я хотел подсчитать, сколько потоков было в определенном блоке кода, а также количество времени (тиков), потраченного в этом блоке кода каждым В этом случае каждому блоку требовалась уникальная статическая переменная, доступная для всех потоков, и мне нужно было позднее ссылаться на эту переменную в 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);
}

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

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

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

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

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

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

Use leading underscores or similar naming conventions for symbols defined within the macro. This will prevent problems associated with a parameter using the same symbol from occurring. Only use the input parameters once and always surround them with parentheses. This is the only way to make macros work with expressions as input. Use the do { ... } while (0) idiom to ensure that the macro is only used as a statement and to avoid other textual replacement problems.
Хорошей идеей является локальная область, в которой есть локально уникальные имена
В самом деле. Возможно, что пользователь макроса может захотеть использовать имя, такое как _tNow. Я предлагаю использовать полное имя макроса в качестве префикса для имен, используемых макросом, в данном случае CALC_TIME_SINCE_tNow
Использование начальных подчеркиваний не является хорошей идеей, поскольку сгенерированные таким образом имена могут конфликтовать с зарезервированными для реализации именами и в любом случае зарезервированы сами.
10

__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
Это не работает, __COUNTER__ не раскрывается. Выходные данные препроцессора: static bool sb___COUNTER__ = false;
1

#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;
22

// 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__ вместо этого и до тех пор, пока вы не вызываете макрос более одного раза в строке или разделяете имена между единицами компиляции, все будет в порядке.

@ Александр Малахов Я играл со всеми видами __COUNTER - 1, я мог себе представить. Эксперименты с Godbolt (с ключом -E gcc) приводят к выводу, что это просто невозможно: нет способа заставить препроцессор сжать математическое выражение (оно не преобразует «10 - 1» в «9»). ;, например). В любом случае, спасибо за вашу помощь.
почему нам нужно косвенное обращение в макросе, прочитайтеstackoverflow.com/a/13301627/264047
@ Александр Малахов Спасибо за ссылку, я безуспешно пробовал разные способы обращения. Было бы замечательно, если бы вы могли поделиться вариантом UNIQUE_PREV_NAME (base), если вы можете сделать его.
А как ссылаться на переменную с только что сформированным уникальным именем? Скажем, у меня есть этот код: int UNIQUE_NAME (nTest) = 100 ;. Как запросить эту переменную nTest0 позже в коде? PP_CAT (base, __COUNTER - 1) не работает. Благодарю.
Это выглядит безумно, но на самом деле это работает. У меня была именно эта проблема:__LINE__ расширяясь на себя, а не на число. Я оставилPP_и теперь я могу делать такие вещи, как:#define FOR(ii, ll, uu) int CAT(FORlim, __LINE__) = (uu); for(int ii = (ll); ii < CAT(FORlim, __LINE__); ++ii) - которые невозможны с любымwhile(0)-hacks. Этот ответ должен быть намного выше.

Похожие вопросы