Jak mogę wygenerować unikalne wartości w preprocesorze C?

Piszę kilka powiązanych makr preprocesora, z których jedno generuje etykiety, do których drugie przeskakuje. Używam ich w ten sposób:

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL();
  MAKE_LABEL();
)

Potrzebuję sposobu na wygenerowanie unikalnych etykiet, po jednej dla każdego wewnętrznego wywołania MAKE_LABEL, za pomocą preprocesora. Próbowałem użyć __LINE__, ale ponieważ wywołuję MAKE_LABEL wewnątrz innego makra, wszystkie mają tę samą linię I etykiety zderzają się.

To, co chciałbym rozwinąć, to coś w stylu:

MAKE_FUNNY_JUMPING_LOOP(
  my_cool_label_1:  // from first inner macro
  ...
  my_cool_label_2:  // from second inner macro
  ...
)

czy istnieje sposób na wygenerowanie hashes czy auto-incrementing integers z preprocesorem?

Author: Brian Tompsett - 汤莱恩, 2009-07-15

6 answers

Jak zauważyli inni, __COUNTER__ jest łatwym, ale niestandardowym sposobem na to.

Jeśli potrzebujesz dodatkowej przenośności lub innych fajnych sztuczek preprocesora, biblioteka preprocesoraBoost (która działa zarówno w C, jak i C++) będzie działać. Na przykład poniższy plik nagłówkowy wyświetli unikalną Etykietę gdziekolwiek jest dołączona.

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/slot/slot.hpp>

#if !defined(UNIQUE_LABEL)
#define UNIQUE_LABEL
#define BOOST_PP_VALUE 1
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#else
#define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1))
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#endif


BOOST_PP_CAT(my_cool_label_, BOOST_PP_SLOT(1)):

Próbka:

int main(int argc, char *argv[]) {
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    return 0;
}

Preprocesuje do

int main(int argc, char *argv[]) {
    my_cool_label_1:
    printf("%x\n", 1234);
    my_cool_label_2:
    printf("%x\n", 1234);
    my_cool_label_3:
    return 0;
}
 15
Author: Josh Kelley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 19:28:24

Jeśli używasz GCC lub MSVC, jest __COUNTER__.

Poza tym mógłbyś zrobić coś godnego wymiocin, np.:

#ifndef USED_1
#define USED_1
1
#else
#ifndef USED_2
#define USED_2
2
/* many many more */
#endif
#endif
 17
Author: derobert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 17:46:36

Używam tego:

#define MERGE_(a,b)  a##b
#define LABEL_(a) MERGE_(unique_name_, a)
#define UNIQUE_NAME LABEL_(__LINE__)

int main()
{
    int UNIQUE_NAME = 1;
    return 0;
}

... i uzyskać następujące:

int main()
{
    int unique_name_8 = 1;
    return 0;
}
 16
Author: Magnus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 14:14:53

Nie mogę wymyślić sposobu na ich automatyczne generowanie, ale możesz przekazać parametr do MAKE_LABEL:

#define MAKE_LABEL(n) my_cool_label_##n:
Więc...
MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL(0);
  MAKE_LABEL(1);
)
 7
Author: Jesse Vogt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 17:54:37

Możesz to zrobić:

#define MAKE_LABEL() \
do {                 \   
my_cool_label:       \
/* some stuff */;    \
goto my_cool_label;  \
/* other stuff */;   \
} while (0) 

To zachowuje zasięg etykiety lokalnie, pozwalając na dowolną ich liczbę wewnątrz głównego makra.

Jeśli chcesz, aby etykiety były dostępne bardziej globalnie, nie jest jasne, w jaki sposób twoje Makro "MAKE_FUNNY_JUMPING_LOOP" odwołuje się do tych etykiet. Możesz to wyjaśnić?

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-16 12:32:53

Nie wydaje się to możliwe w przypadku standardowego preprocesora, chociaż można to sfałszować, umieszczając parametry w MAKE_LABEL lub MAKE_FUNNY_JUMPING_LOOP i używając wklejania tokenów do utworzenia etykiety.

Nic nie stoi na przeszkodzie, aby zrobić własny skrypt wstępnego przetwarzania, który robi automatyczny przyrost dla Ciebie. Jednak w takim przypadku nie będzie to standardowy plik C/C++.

Lista dostępnych poleceń: http://www.cppreference.com/wiki/preprocessor/start

 -2
Author: Raymond Martineau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-15 17:36:15