Макро функция с несколькими строками для параметра? - PullRequest
13 голосов
/ 06 апреля 2011

В C ++ мне нужно определить макрос.Этот макрос будет принимать в качестве параметра «блок» кода.

Можем ли мы безопасно использовать несколько строк кода в качестве параметра макрофункции?

Я спрашивал себяif:

  1. является ли следующий код действительным, определенным как действительный стандартом, как в "кроссплатформенном"?
  2. Есть ли лучший способ сделать то же самое (я могуздесь не используется шаблонная функция, потому что мне нужен контекст).

#define MY_MACRO( expr ) DOSOMETHING( (expr) ); DOANOTHERTHING( (expr) ); // etc...

int my_function() {
    int o = RandomNumber();
    MY_MACRO( 
        int k = AFunction();
        k++;
        AnotherFunction( k + o ); // here I need to keep the context of the call
    ); 
}

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

Ответы [ 6 ]

9 голосов
/ 06 апреля 2011

16,3 / 9:

в последовательности предварительной обработки токены, составляющие вызов функциональный макрос, новая строка считается нормальным пробелом характер.

Так что многострочный вызов макроса в целом нормален. Конечно, если DOSOMETHING и DOANOTHERTHING не вводят фигурные скобки для области, то ваш конкретный пример переопределит k.

Edit:

Мы не можем использовать функторы, потому что нам нужно иметь доступ к контексту вызов. Мы не можем использовать лямбду (сниф), потому что мы используем старый компилятор

Обычный способ - захватывать любые переменные, которые вам нужны в функторе, как лямбда. Единственное, что лямбда может сделать, что функтор не может, - это «захватить все» без необходимости печатать ее, но тот, кто пишет лямбду, может видеть, какие переменные они используют, так что это просто удобство, они могут напечатать их все, если должен был. В вашем примере:

struct MyFunctor {
    int o;
    MyFunctor(int o) : o(o) {}
    void operator()() const {  // probably not void in practice
        int k = AFunction();
        k++;
        AnotherFunction( k + o );
    }
};

template<typename F>
void DoThings(const F &f) {
    DOSOMETHING(f());
    DOANOTHERTHING(f());
}

int my_function() {
    int o = RandomNumber();
    DoBothThings(MyFunctor(o));
}

Вы также можете захватывать переменные по ссылке (обычно используя указатель в качестве элемента данных, а не ссылку, так что функтор может быть назначен для копирования).

Если под «контекстом» вы подразумеваете, например, что аргумент макроса и / или тело макроса могут содержать break или goto и, следовательно, должны находиться внутри лексической области действия вызывающей стороны, тогда вы можете ' не использовать функтор или лямбду. К стыду; -)

3 голосов
/ 24 июня 2016

способ заставить его работать (по крайней мере, для gcc версии 4.8.1 (Ubuntu / Linaro 4.8.1-10ubuntu9)), это использовать фигурные скобки {}, содержащие ваши действительные значения для макроса.

Полезный пример:

#ifdef DEBUG
#define MYDEBUG(X) (X)
#else
#define MYDEBUG(X)
#endif

MYDEBUG({
  if (shit_happens) {
     cerr << "help!" << endl;
     ....
  }
});
1 голос
/ 06 апреля 2011

В C ++ вы должны использовать функтор!;)

struct ninja
{
  void operator()() const
  {
    int k = AFunction();
    k++;
    AnotherFunction( k );    
  }
};

template <typename Functor>
void do_something(Functor const& f)
{
  f();
}

template <typename Functor>
void do_otherthing(Functor const& f)
{
  f();
}

int my_function()
{
  ninja foo;
  do_something(foo);
  do_otherthing(foo);
}
1 голос
/ 06 апреля 2011

Я думаю, вам нужно будет использовать дополнительные скобки, чтобы ваши выражения выглядели как единый аргумент, который не будет разбит препроцессором, то есть сделайте это примерно так:

#define MY_MACRO( (expr) ) DOSOMETHING( (expr) ); DOANOTHERTHING( (expr) ); // etc...

int my_function() {
    MY_MACRO( 
        (int k = AFunction();
        k++;
        AnotherFunction( k );)
    ); 
}

хотя яна самом деле не пробовал.

0 голосов
/ 12 июня 2015

Макрос с несколькими строками для параметра подходит и в случае нескольких аргументов, он даже допускает «запятые» внутри, но я настоятельно рекомендую использовать «запятые», потому что если это не неоднозначность для машины, это, безусловно, неоднозначность для человека :

#include <iostream>
using namespace std;

#define MACRO2FUN( X, Y) x; y;
void function(int a, int b, int c){
    std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}

int main() {
    MACRO2FUN(
      function(3,4,5), 
      function(6,7,8)
      )
      return 0;
}
0 голосов
/ 06 апреля 2011

Основная проблема, которую я вижу, состоит в том, что expr вовсе не является выражением.Он даже содержит декларацию.Очевидно, у вас возникнет проблема с двумя переменными k, определенными в my_function.

Если вы можете использовать C ++ 0x (например, VS2010, GCC4.6), тогда вы можете использовать лямбда-выражения длязахватить контекст.Не то чтобы вам нужно было захватывать контекст для такого простого случая, а также вам не нужны шаблоны, ваш макрос просто нуждается в std::function<void(void)>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...