Я бился головой, пытаясь написать правильное #define
функциональный макрос, но я застреваю. Вот пример, с которым я работаю:
#include <iostream>
#define pMAKE(x,y,z,dest)\
(dest).(x) = (x);\
(dest).(y) = (y);\
(dest).(z) = (z);
struct pt {
double x, y, z;
};
int main() {
pt p;
pMAKE(0,1,2,p);
return 0;
}
И ошибки, которые я получаю:
A.cpp: In function ‘int main()’:
A.cpp:13: error: expected unqualified-id before ‘(’ token
A.cpp:13: error: expected unqualified-id before ‘(’ token
A.cpp:13: error: expected unqualified-id before ‘(’ token
Что означают ошибки и почему я их получаю? Мне удалось заставить следующее работать так, как я хотел, но честно говоря, мне просто повезло, и я серьезно не понимаю, что происходит.
#define pMAKE(X,Y,Z,dest)\
dest.x = (X);\
dest.y = (Y);\
dest.z = (Z);
Я ценю каждую помощь!
С первой версией вашего макроса, pMAKE(0,1,2,p);
расширяется до
p.0 = 0;
p.1 = 1;
p.2 = 2;;
Другими словами, вы предварительно обработали ссылки на x
, y
, z
Члены pt
используя x
, y
, z
как имена переменных (слева от назначений), так и метки, подлежащие замене препроцессором (справа от назначений).
(Как отмечает Конрад Рудольф) К сожалению, в вашем макросе все еще есть серьезная ошибка. Рассмотрим код формы
pt p;
if (foo)
pMAKE(0,1,2,p);
который расширяется до
pt p;
if (foo)
p.x = 0;
p.y = 1; // happens regardless of 'foo' test
p.z = 2;; // happens regardless of 'foo' test
Вы можете исправить это (и избавиться от этой надоедливой дополнительной точки с запятой), изменив макрос на
#define pMAKE(X,Y,Z,dest)\
do { \
(dest).x = (X);\
(dest).y = (Y);\
(dest).z = (Z);\
} while (0)
struct pt {
double x, y, z;
pt(double _x, double _y, double _z) : x(_x), y(_y), z(_z) { }
};
pt p (0.0, 1.0, 2.0);
Не используйте макросы в C ++.
Или, как предложил @potatoswatter, сделайте все правильно сразу в C ++ 11
struct pt { double x, y, z; };
pt p = { 0.0, 1.0, 2.0 };
Что означают ошибки и почему я их получаю?
Макрос заменяет все вхождения его параметров с аргументами; так
(dest).(x) = (x)
становится
(dest).(0) = (0)
это ерунда. Компилятор ожидает имя члена (идентификатор, называемый «неквалифицированным идентификатором» в синтаксисе языка), но видит (0)
вместо.
Во второй версии x
не имя параметра, поэтому dest.x
не изменился.
Лучшим подходом было бы избегать макросов; использовать функцию:
pt make_pt(double x, double y, double z) {
pt p;
p.x = x;
p.y = y;
p.z = z;
return p;
}
int main() {
pt p = make_pt(0,1,2);
}
или просто используйте агрегатную инициализацию:
int main() {
pt p = {0,1,2};
}
и в C ++ 11 присваивание из списка инициализаторов:
p = {4,5,6};
Макросы работают как текстовый замена. Везде, где параметр макроса появляется в списке замены, он текстуально заменяется аргументом. Так что в вашем случае, pMAKE(0, 1, 2, p);
расширяется до:
(p).(0) = (0);
(p).(1) = (1);
(p).(2) = (2);;
(На самом деле все было бы в одной строке, поскольку макросы не сохраняют новые строки, но для ясности я поместил их в три строки).
В вашем случае вам не нужен макрос. Вы хотите встроенную функцию (вероятно, функцию-член или конструктор pt
). Используйте макросы только в том случае, если вам нужна их текстовая природа (например, объединение токенов в идентификаторы и т. Д.). Для всего остального используйте (встроенные) функции. Как это:
struct pt
{
double x, y, z;
pt(double ax, double ay, double az) : x(ax), y(ay), z(az) {}
};
int main()
{
pt p(0, 1, 2);
}
Используйте встроенную функцию,
pt& pMake(int x, int y, int z, pt&p){
p.x=x;
p.y=y;
p.z=z;
return p;
};
.(x)
элемент доступа случайно использует имя параметра x
, (Кроме того, второй операнд доступа к члену не является выражением, поэтому круглые скобки не принадлежат.) Рекомендуется сохранять все имена макросов и параметры макросов во всех заглавных буквах, чтобы избежать конфликтов имен. И избегайте односимвольных идентификаторов тоже.
#define POINT_MAKE(X,Y,Z,DEST)\
(DEST).x = (X);\
(DEST).y = (Y);\
(DEST).z = (Z);