создать экземпляр в макросе #define - PullRequest
0 голосов
/ 16 января 2019

Я не хочу что-то вроде: #define some_func(a) some_func(a, create_foo())

и затем при использовании:

 void loop() {
     some_func(3);
     some_func(40);
 }

экземпляр Foo должен создаваться только один раз для каждой строки. Так и в приведенном выше случае 2 раза. И когда цикл снова запускается, он не должен снова создавать экземпляры Foo.

Возможна ли такая вещь?

Вот полная нерабочая программа: Выход должен быть 3, 40, 6, 80, 9, 120, 12, 160, ...

typedef struct {
  int a;
} Foo;

Foo create_foo() {
  return {0};
}

void some_func(int a, Foo &f) {
  f.a += a;
  Serial.println(f.a);
}

#define some_func(a) some_func(a, create_foo())


void setup() {
  Serial.begin(9600);
}

void loop() {
  some_func(3); // 3, 6, 9, 12
  some_func(40); // 40, 80, 120, 160
}

Edit.

Я пытался свести пример к минимуму, но сейчас я стреляю себе в ногу. На самом деле, у меня нет void в качестве возвращаемого типа, но boolean.

Итак, я попробую что-то вроде этого:

#define debounce(val) for(static auto d = create_debounce(); debounce(d, val), false;)

Но это, конечно, не помогает при использовании с: int state = debounce(digitalRead(BUTTON_FIRE));

Причина, по которой макрос не возвращает значение, поэтому присвоение невозможно.

Так что мне нужно что-то вроде: #define debounce(val) true; for(static auto d = create_debounce(); debounce(d, val), false;)

где true - результат функции create_debounce.

Так может отравить это еще больше, чтобы сделать это возможным? Вот полный код:

// ----------------- L I B R A R Y .  S T U F F -------------------------

#define debounce_delay 50

typedef struct {
    int state;
    int last_state;
    unsigned long last_state_change_time;    
} Debounce;

Debounce create_debounce() {
    return {0, 0, 0L};
}

boolean debounce(Debounce &deb, int val) {

    if (val != deb.last_state) {
        deb.last_state_change_time = millis();
        deb.last_state = val;
    }
    else if ((millis() - deb.last_state_change_time) > debounce_delay) {
        deb.state = val;
    }
    return deb.state;
}



//#define debounce(val) for(static auto d = create_debounce(); debounce(d, val), false;)
#define debounce(val) true; for(static auto d = create_debounce(); debounce(d, val), false;)


// ----------------- S K E T C H -------------------------

#define BUTTON_FIRE 7


void setup() {
  Serial.begin(9600);
}


void loop() {

  int state = debounce(digitalRead(BUTTON_FIRE));

  if (state == HIGH) {
    Serial.println("HIGH");
  }
  else {
    Serial.println("LOW");
  }

}

Ответы [ 4 ]

0 голосов
/ 16 января 2019

Первое, что нужно отметить, это то, что ваше состояние логическое. Это сэкономит вам несколько байтов оперативной памяти.

Следующее, на что следует обратить внимание, это то, что вы хотите игнорировать изменения на входе в течение определенного периода времени; это означает, что вам не нужно хранить «текущее» состояние; только последнее состояние ... которое в конечном итоге будет таким же. Это не может вас спасти, так как 2 логических значения и 1 логическое значение, скорее всего, будут занимать байт; но это дает компилятору шанс и, что самое важное, упрощает работу.

С этими 2 довольно незначительными улучшениями мы доберемся до более крупных. Не используйте макросы, если вы действительно не знаете, что делаете; и даже потом пересмотреть.

Пример кода Arduino имеет тенденцию предлагать их, потому что кто-то думал, что это облегчит изучение; но, честно говоря, нет. Они не являются функцией, и ваше использование этого действительно не делает то, что вы думаете, что делает. Arduino предлагает ограниченные способы его отладки, так что вы не можете сказать, что ваше состояние ВСЕГДА будет высоким, потому что расширение макроса таково:

int state = true; 
for(static auto d = create_debounce(); 
    debounce(d, val), 
    false;);
//New lines added for clarity.

Переместить его в функцию; позвольте компилятору оптимизировать код, потому что он ВСЕГДА будет работать лучше, чем вы, если вы будете писать код так, как это возможно.

0 голосов
/ 16 января 2019

Помимо того, что ваш макрос плохо сформирован, он бесполезен для того, что вы хотите, потому что вы вызываете create_foo при каждом вызове.

Вы можете использовать статические переменные:

void loop() {
    static Foo f1, f2;
    some_func(3, f1);
    some_func(40, f2);
}
0 голосов
/ 16 января 2019

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

Вы можете определить макрос следующим образом:

#define some_func(a) for(static auto f = create_foo(); some_func(a, f), false;)

Да, это будет работать. В стандартном C ++ предложение init цикла for может содержать объявление статической переменной. Таким образом, переменная будет инициализирована только один раз. Тогда «условие» - это фактический вызов some_func, за которым следует оператор запятой с false, поэтому функция выполняется только один раз при каждом входе в цикл for.

Адаптация вашего кода из Arduino к стандартному C ++ и симуляция четырех циклов привели к тому же результату, который вы хотели. Посмотри вживую .


В качестве альтернативы, если вы хотите казаться немного менее загадочным (но с чего бы это?), Вы можете выбрать это:

#define some_func(a) do {static auto f = create_foo(); some_func(a, f); } while(0)

То же самое на самом деле.


Хорошо, применение его к вашей актуальной проблеме требует чего-то другого:

#define debounce(a) [](int v){static Debounce d = create_debounce(); \
                              return debounce(d, v); }(a)

Это определяет и немедленно вызывает лямбду. Поскольку лямбда-выражение создает уникальный тип замыкания везде, где он появляется в программе, это создаст уникальный статический объект для каждого выражения, в которое вы записываете debounce(...). Альтернативой является специальное выражение оператора GCC. Но в отличие от лямбды, это расширение. Который вы можете или не можете использовать, YMMV.

0 голосов
/ 16 января 2019

Когда цикл запускается снова, экземпляры Foo создаются снова, они не восстанавливаются с предыдущего запуска.

Я подозреваю, что вы хотите использовать набор статических переменных. Или рефакторинг вашего кода для ясности.

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

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