c++ - all - anlyticsed
¿Cómo generar nombres de variables aleatorias en C++ usando macros? (8)
Estoy creando una macro en C ++ que declara una variable y le asigna algún valor. Dependiendo de cómo se use la macro, la segunda aparición de la macro puede anular el valor de la primera variable. Por ejemplo:
#define MY_MACRO int my_variable_[random-number-here] = getCurrentTime();
La otra motivación para usar eso es evitar seleccionar cierto nombre para la variable para que sea igual a un nombre finalmente elegido por el desarrollador que usa la macro.
¿Hay alguna forma de generar nombres de variables aleatorias dentro de una macro en C ++?
- Editar -
Quiero decir que es único pero también aleatorio una vez que puedo usar mi macro dos veces en un bloque y en este caso generará algo así como:
int unique_variable_name;
...
int unique_variable_name;
En este caso, para ser únicos, ambos nombres de variables deben generarse aleatoriamente.
Agrega M4 a tu flujo de compilación? Este lenguaje macro tiene algunas capacidades con estado, y puede mezclarse con éxito con las macros CPP. Probablemente esta no sea una forma estándar de generar nombres únicos en un entorno C, aunque he podido usarlo de manera exitosa.
Es probable que no desee al azar, por cierto, en función de la forma en que planteó su pregunta. Quieres único .
Podría usar __FILE__
y __LINE__
en la macro expansión para obtener la singularidad que usted parece estar buscando ... esas metavariables se definen dentro del contexto del archivo de origen, así que tenga cuidado de asegurarse de obtener lo que está buscando (por ejemplo, peligros de más de una macro en la misma línea).
Aquí hay una definición macro sucinta para generar el patrón singleton anterior.
#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;
Aunque no creo que sea posible, deberías considerar seriamente hacer una clase de esto.
Si desea que un elemento aleatorio de una matriz aleatoria contenga un cierto valor, puede hacer esto:
std::vector< std::vector<int> > m_vec;
Luego envuélvalo en una clase, de modo que el desarrollador solo pueda establecer un número:
void set(int foo)
{
m_vec[random()][random()] = foo;
}
¿Hay alguna razón por la que quieres que sea una macro? El nombre de la variable aleatoria parece peligroso, ¿y si elige algo que ya está definido en otro lugar del código?
En lugar de hacer que el preprocesador cree un nombre, posiblemente pueda dejar que el usuario macro le dé un nombre.
#define MY_MACRO(varname) int varname = getCurrentTime();
Generar nombres únicos en el preprocesador es difícil. Lo más cerca que puede llegar es __FILE__
y __LINE__
en el símbolo como lo sugiere popcnt . Si realmente necesita generar nombres únicos de símbolos globales, entonces seguiría su sugerencia de usar algo como M4 o un script Perl en su sistema de compilación.
Es posible que no necesites nombres únicos. Si su macro puede imponer un nuevo ámbito, puede usar el mismo nombre, ya que simplemente sombreará otras definiciones. Por lo general, sigo el consejo común de envolver macros en do { ... } while (0)
bucles. Esto solo funciona para macros que son declaraciones, no expresiones. La macro puede actualizar variables usando parámetros de salida . Por ejemplo:
#define CALC_TIME_SINCE(t0, OUT) do { /
std::time_t _tNow = std::time(NULL); /
(OUT) = _tNow - (t0); /
} while (0)
Si sigues algunas reglas , por lo general eres bastante seguro:
- Use subrayados iniciales o convenciones de nomenclatura similares para los símbolos definidos dentro de la macro. Esto evitará que se produzcan problemas asociados con un parámetro que utiliza el mismo símbolo.
- Utilice solo los parámetros de entrada una vez y siempre colóquelos con paréntesis. Esta es la única forma de hacer que las macros funcionen con expresiones como entrada.
- Use la expresión
do { ... } while (0)
para asegurarse de que la macro solo se use como una declaración y para evitar otros problemas de reemplazo de texto.
Necesitaba algo similar para un caso en el que no tenía ninguna herramienta de creación de perfiles, pero quería contar cuántos subprocesos había dentro de un bloque de código en particular, así como la cantidad de tiempo (tics) gastados en ese bloque de código por cada uno thread, en este caso, cada bloque necesitaba una variable estática única accesible para todos los hilos, y necesitaba hacer referencia posterior a esa variable en incr (utilicé una API de registro en lugar de printf en el código real, pero esto también funciona). Al principio, pensé que era muy inteligente al hacer lo siguiente:
#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)++;
Pero luego me di cuenta de que esto es una tontería y el compilador de C simplemente hará esto por usted, siempre y cuando cada declaración "estática" sea su propio bloque:
#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--; /
}
Tenga en cuenta los corchetes de abrir / cerrar en cada macro. Esto no es estrictamente seguro para subprocesos, pero para mi perfil, podría suponer que las operaciones incr y decr fueron atómicas. Aquí hay una muestra de recursión que usa las macros
#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);
}
Sugerencia adicional, el programa anterior es una pregunta de entrevista común. Extracto de "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
Pruebe lo siguiente:
// 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__
se rumorea que tiene problemas de portabilidad. Si es así, puede usar __LINE__
lugar y siempre que no llame a la macro más de una vez por línea o comparta los nombres en las unidades de compilación, estará bien.
use __COUNTER__
(funciona en gcc4.8, clang 3.5 e 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