Определите тип объявления

Я пишу макрос, который принимает объявление в качестве единственного аргумента. Можно ли определить тип объявления внутри макроса, не разделяя один аргумент на отдельные аргументы type и identifier?

#define M(declaration) \
    declaration;       \
    static_assert(sizeof(/* deduce type of 'declaration' */) == 4, "!")

M(int i);
M(double d{3.14});
M(std::string s{"Hello, world!"});

Следующая реализация будет работать, но она менее удобна для пользователя (imo):

#define M(type, identifier) \
    type identifier;        \
    static_assert(sizeof(type) == 4, "!")

M(int, i);
M(double, d{3.14});
M(std::string, s{"Hello, world!"});

Если возможно, я бы предпочел принять декларацию в качестве одного аргумента.


Связанный вопрос: Макрос для получения типа выражение; но мне не удалось заставить этот код работать в моем примере (ошибка компилятора: ожидаемый спецификатор вложенного имени).


person Maarten Bamelis    schedule 30.10.2017    source источник
comment
Вы намерены, чтобы статическое утверждение печатало идентификатор в конце? Потому что, если нет, есть решение, которое также отключает препроцессор.   -  person StoryTeller - Unslander Monica    schedule 30.10.2017
comment
@StoryTeller Мне не обязательно нужен идентификатор. Но если вы можете придумать несколько решений, мне будут интересны все из них.   -  person Maarten Bamelis    schedule 30.10.2017


Ответы (2)


Если ваше статическое сообщение подтверждения действительно такое простое "!"1, я предлагаю вам отказаться от препроцессора. Вместо этого заставьте систему типов работать на вас:

namespace detail {
  template<typename T>
  struct check_declared_type {
    using type = T;
    static_assert(sizeof(type) == 4, "!");
  };
}

template<typename T>
using M = typename detail::check_declared_type<T>::type;

// .. Later

int main() {
  M<int> i;
  M<double> d{3.14};
  M<std::string> s{"Hello, world!"};
}

1 — В частности, если вам не нужно, чтобы препроцессор что-то застраивал за вас.

person StoryTeller - Unslander Monica    schedule 30.10.2017
comment
Это решает мою проблему и позволяет избежать макросов, спасибо! У вас также есть решение, если я хочу использовать идентификатор в static_assert? - person Maarten Bamelis; 30.10.2017
comment
@MaartenBamelis - для этого потребуется вернуть препроцессор обратно. И это вернет вас к квадрату 1. - person StoryTeller - Unslander Monica; 30.10.2017
comment
Итак, могу ли я сделать вывод, что невозможно вывести тип объявления в C++? Даже с умной комбинацией decltype и других трюков во время компиляции? - person Maarten Bamelis; 30.10.2017
comment
@MaartenBamelis - насколько мне известно, нет. Объявление не является выражением, поэтому оно не может появляться в невычисленном контексте. Возможно, препроцессор может разобрать его на части. Но я не знаком с техникой. - person StoryTeller - Unslander Monica; 30.10.2017

Этот макрос должен работать для всех ваших примеров, но у него есть неприятная проблема:

#define M(declaration) \
    declaration;       \
    do { \
        struct dummy__ { declaration; }; \
        static_assert(sizeof(dummy__) == 4, "!"); \
    } while (false)

Проблема в том, что инициализатор в определении класса должен использовать либо токен =, либо список инициализации в фигурных скобках на верхнем уровне, а не круглые скобки на верхнем уровне. Так, например, M(SomeClass obj(true, 3)); не будет компилироваться, даже если sizeof(SomeClass)==4. Поскольку инициализаторы в фигурных скобках не полностью эквивалентны инициализаторам в скобках, это означает, что некоторые объявления невозможно использовать с макросом.

person aschepler    schedule 30.10.2017