Preprocesor, bląd w wyrazeniach w parametrach makra

0

Dlaczego po użyciu operatorów modyfikujących na zmiennych przekazywanych jako parametry makra np. definiuje

#define kwadrat(x) x*x

i wywoluje (dla z=2 i t=3) kwadrat(z+t) w mainie to zwraca mi literal 11 a nie 25.

1

No a rozwiń sobie to sam - zamiast x wstaw z + t i zobacz co wyjdzie.

0

no z+t dla z=2 i t=3 rowna sie 5
kwadrat(2+3)
powinno mi zwrócić 25 a tu 11. Dlaczego?

2

Nie, nie. Rozpisz mi to makro ręcznie na tych zmiennych, tak jak by to zrobił kompilator.

0

Ok chyba rozumiem. z+tz+t = 2+23+3 = 2+6+3 = 11

Mieli racje ze latwiej z cpp na jave niz z javy na cpp :D
A jeszcze jedno pytanie do czego moze to byc przydatne.
Jedyna opcja na razie jaką widze jest definiowanie makrofunkcji bez typowania ale co poza tym??

3

@johnybrawo75

  1. Konwencja jest taka, że każdą zmienną w takiej marko-definicji jak i całą makro-definicję bierzemy w nawias. Powinieneś już wiedzieć dlaczego.
    #define kwadrat(x) ((x)*(x))

  2. Konwencja jest też taka, że makra są złe. Bo są złe.

Jedyna opcja na razie jaką widze jest definiowanie makrofunkcji bez typowania ale co poza tym??

Nie. Od tego są templaty, które potrafią znacznie więcej niż makra.

  1. Jedyne słuszne zastosowanie makr to sterowanie procesem kompilacji, tj np:
    #define DEBUG
    #define WIN32
0

Generalnie odchodzi się od tego. Powoduje to wiele problemów. A gdy już idzie do debugowania takiego kodu... Ale zdarza się, że makra są niezastąpione. Jak działa makro? To, co tam wpiszesz znajduje się BEZPOŚREDNIO w kodzie. To znaczy, że, tworząc funkcję podnoszącą do kwadratu masz coś takiego:

 
int power(int x)
{
  return x * x;
}

void main()
{
  int result = power(5);
}

I to jest to, co "widzi kompilator". Czyli: "wykonaj funkcję power z parametrem 5"

A przerabiając to na makro:

 
#define POWER(x) x*x

void main()
{
  int result = POWER(5);
}

Najpierw do pracy rusza preprocesor i zmienia Twój kod w taki sposób:

 
void main()
{
  int result = 5*5; //lub od razu 25, nie jestem pewien
}

Czyli preprocesor "przekleja" Ci definicję tego makra w odpowiednie miejsca w kodzie. Ma to swoje różne zastosowania. Np. chcesz mieć klasy, które się nazywają w zależności od danych jakie przechowują:

 
#define DECLARE_CLASS(x) \
class x_container \
{ \
  public: \
    x getData(int index); \
  private: \
    std::vector<x> m_data; \
} \

DECLARE_CLASS(int);
DECLARE_CLASS(double);
DECLARE_CLASS(std::string);

void main()
{
  int_container c;
  int value = c.getData(0);
}

To jest taki przykład w pseudokodzie. Oczywiście dzisiaj do takich celów używamy template'ów, a nie makr. Makra były kiedyś używane na potęgę (głównie w C).

Ciężko mi teraz wymyślić jakiś sensowny przykład, gdzie makro jest faktycznie niezastąpione. Na pewno to będzie coś ze stringami lub jakimiś mechanizmami RTTI. Np:

 
#define REGISTER_CLASS(name) \
class name \
{ \
public: \
  std::string getClassName() \
  { \
    return #name; \
  } \
} \

(to się pewnie nie skompiluje, bo pewnie coś jest nie tak tutaj z hashem i zwracaniem tej nazwy).
Coś takiego może służyć do "rejestrowania" klas w jakimś mechanizmie RTTI. To tylko bezużyteczny przykład, ale myślę, że sporo Ci powie. Generalnie staraj się unikać makr jak ognia.

I jak napisał poprzednik, sensowne jest ich wykorzystanie właśnie w takim celu jak sprawdzanie, czy to wersja debug, czy nie itp.

1 użytkowników online, w tym zalogowanych: 0, gości: 1