[C++] Sklejacz nazw i dyrektywa define

Czesc…

Napisalem taki kod:

#include 

using namespace std;

// ---- Nasze dyrektywy ----

#define KWADR(a) ((a) * (a)) // równie dobrze może być - KWADR(c) ((c) * (c))

#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) )

// ---- ---- ---- ---- ----

int main()

{

	int b = 10;

	cout << "KWADR(b) = " << KWADR(b); // 10 * 10 = 100


	cout << "\n\n";


	int i = 5;

	int x = 10;


	cout << MAX(i,x); //cout << 10


}

Jak widzimy, w np. makrodefinicji KWADR jako argument umieściłem zmienną o wartości 10 - KWADR(b). Wszystko pięknie działa. Jednak tutaj już nie :expressionless:

#include 

using namespace std;

#define NAZWA(skala) int pomiar ## skala ## ;

int main()

{


	int bbb = 56;


	NAZWA(bbb);


	//int pomiar56;

	pomiar56 = 5666;


	cout << pomiar56;


}

Jeżeli zmienię argument bbb na 56 to wszystko działa. Jako błąd wyświetla mi się oczywiście, że nigdzie nie było definicji pomiar56… Dlaczego tak się dzieje - ktos wie ?

Ponieważ preprocesor pracuje na tokenach i to przed właściwą kompilacją, wysyłasz mu token bbb i go używa, preprocesor nic nie wie o zmiennych.

W pierwszym przypadku

MAX(i,x) // zamieni się w

( ((i) > (x)) ? (i) : (x) )

Gdybyś ręcznie wpisał ten tekst, który wyprodukuje preprocesor, będzie to poprawne z punktu widzenia tego kodu źródłowego. Natomiast w drugim przykładzie:

NAZWA(bbb); // zamienia się w

int pomiarbbb;

Zostały zwyczajnie połączone tokeny, identycznie jak w pierwszym przykładzie, z tą różnicą, że nie o to Ci chodziło. Musiałbyś bezpośrednio użyć NAZWA(56), wtedy to by działało.

Tzn. to ?

( ((i) > (x)) ? (i) : (x) )

Fakt, zapomniałem o tym, że dyrektywy wykonują się przed kompilacją. Więc czemu w ogóle działa takie podstawianie zmiennych jak w tym 1-szym przykładzie, a 2-gim już nie…

Proszę o wyrozumiałość bo na razie raczkuję dopiero w C++ :oops:

No właśnie chodzi mi o to, że gdybyś ręcznie “zastąpił” preprocesor i sam zmienił MAX(i,x) na ( ((i) > (x)) ? (i) : (x) ) to nadal jest to poprawny kod, zresztą taki sam dostanie kompilator. Kiedy tak samo zastąpisz preprocesor i ręcznie zamienisz NAZWA(bbb) na int pomiarbbb; od razu zauważysz, że nie o to Ci chodziło.

Jeszcze jedna uwaga, jeśli NAZWA(skala) zmieniane jest w “int pomiarskala;” to niepotrzebnie stawiasz średnik po NAZWA() i w efekcie dostajesz dwa średniki w kodzie wyjściowym. W twoim przypadku nic to nie zmienia, ale mimo wszystko warto pamiętać o szczegółach.

Wyobraź sobie w programie takie instrukcje:

cout << ( ((i) > (x)) ? (i) : (x) );

int pomiarbbb;

W tamtej na górze, kompilator zamieni nazwy zmiennych na wartości. Natomiast w 2 - gim przypadku nie zamieni. Przecież to musiało by być równoznaczne z takim kodem:

int bbb = 56;

// teraz wpisujemy

int pomiarbbb = 100; 

// ale kompilator sobie zamieni to i tak na

int pomiar56 = 100;

W rzeczywistości przecież, kompilator nigdy by nie zamienił zmiennej pomiarbbb , na pomiar56 tylko dlatego, że innej zmiennej bbb odpowiada ciąg 56…

No właśnie, musisz pamiętać, że NAZWA(skala) to nie instrukcja , ale ciąg znaków, który kompilator zamieni sobie na inny…


Nie zmieni bo i tak program jest błędem - gdyby wszystko było dobrze to wtedy byłby to błąd i znowu program by się nie skompilował.

Może ten problem będzie rozwiązany przy następującym podejściu:

#define NAZWA(skala) int pomiar##skala##

#define bbb 56


  {

   NAZWA(bbb)=100;

   cout<
  }