Использование одного параметра в макросе как целой инструкции C ++ - PullRequest
3 голосов
/ 22 февраля 2012

РЕДАКТИРОВАТЬ: код, помеченный как «не работает» на самом деле работает. Это было из-за синтаксических проблем в моих тестах, не обнаруженных компилятором. Так что вопрос уже решен, спасибо.

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

Сначала о контексте. Я использую C ++ для разработки на микроконтроллере (на базе Arduino, AVR microcontroller ), поэтому я не использую STL , printf-подобные функции, следует избегать new / malloc и C ++ тоже.

У меня есть объект с именем Serial, похожий на Cost ioutream C ++, для связи с микроконтроллером через последовательный интерфейс. Я перегружен оператор «<<» класса, из которого <code>Serial является экземпляром, поэтому я могу сделать что-то вроде этого:

Serial << "debug " << "value is " << 3 << endl;

// Whithout the << operator it would be:
Serial.print("debug ");
Serial.print("value is ");
Serial.println(3);

Я хотел бы создать функцию (или макрос), которая разрешает этот тип строки, только если включена отладка, и которая автоматически добавляет строку «отладка» и добавляет значение «endl» в конце.

Так что-то в этом роде (предупреждение, код не работает, потому что «данные» не могут раскрываться как целая инструкция C ++):

#ifdef DEBUG
    #define PRINT_DEBUG(data) do {Serial << "debug " << data << endl;} while(0)
#else
    #define PRINT_DEBUG(data) do {} while(0)
#endif

// This code works
PRINT_DEBUG("hello world");

// This code does not work
int value1 = 3;
char * value2 = "this is a string";
PRINT_DEBUG("sensor1 value:" << value1 << " other sensor value " << value2);

Этот тип функции / макроса позволил бы мне легко выводить строки на моем последовательном интерфейсе с определенным «строковым протоколом» без необходимости повторять строку «отладки» в начале. Это также позволило бы мне легко отключить печать сообщения отладки, не устанавливая макрос DEBUG. У меня также есть только один "#ifdef DEBUG" вместо нескольких в моем коде.

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

void __rawSend(char * args, ...) {
    Serial.print(args);
    va_list paramList;
    va_start (paramList, args);
    while(true) {
        char * next = va_arg(paramList, char*);
        if (next == NULL) {
            break;
        }
        Serial.print(" ");
        Serial.print(next);
    }
    Serial.println();
    va_end(paramList);
}

#ifdef DEBUG
    #define printDebug(...) do {__rawSend(OUTPUT_DEBUG, __VA_ARGS__, NULL);} while(0)
#else
    #define printDebug(...) do {} while(0)
#endif

int intValue = 1;
char * stringValue = "data";

// This works
printDebug("hello",stringValue);
// This does not works
printDebug("data is", intValue);

Как я могу это сделать? Возможно ли это с помощью макросов (избегая вариационных аргументов и смешивая разные типы)? Есть ли лучшее решение?

Ответы [ 3 ]

1 голос
/ 22 февраля 2012

Извините, код, помеченный как "не работающий", действительно работает.Это было из-за синтаксических проблем в моих тестах, которые не были обнаружены компилятором.

В любом случае, мое решение может принести пользу другим людям, работающим с Arduino, так как я видел решения, использующие printf или пытающиеся воссоздать printf.

Я использовал оператор «<<» из <a href="http://arduiniana.org/libraries/streaming/" rel="nofollow">http://arduiniana.org/libraries/streaming/

1 голос
/ 22 февраля 2012

Я стараюсь избегать макросов для такого рода вещей и вместо этого использую классы и статический полиморфизм:

// Define different types providing a stream interface
struct DebugStream
{
  template <typename T>
  std::ostream & operator<< (const T & x) const {
    return Serial << "debug " << x;
  }

  // This one is for stream manipulators
  std::ostream & operator<< (std::ostream& (*x) (std::ostream&)) const {
    return Serial << "debug " << x;
  }
};


// This type also provides a stream-like interface but does nothing
struct NoStream
{
  template <class T>
  const NoStream & operator<< (const T & x) const { 
    return *this;
  }

  const NoStream & operator<< (std::ostream& (*x) (std::ostream&)) const {
    return *this;
  }
};


// Instanciate a debug object having one of the previously defined types
// 
// Make sure to declare debug in a common .hxx file included everywhere else
// but to define it only once in a .cxx file.
#ifdef DEBUG
DebugStream debug;
#else
NoStream debug;
#endif 


// Use it like you would use the Serial iostream
debug << "value is " << 3 << std::endl;

Поскольку все встроено и точные типы известны во время компиляции, компилятор может оптимизировать все ненужные операции с экземплярами NoStream.

0 голосов
/ 22 февраля 2012

Если я правильно понимаю ваши проблемы ... Похоже, вам нужно перегрузить оператор << для всех типов, которые вы собираетесь отправить в интерфейс отладки. </p>

  • Макрос var args должен иметь возможность определять типы своих аргументов.То, как вы это реализовали, ожидает всех строк типа C.Вам было бы лучше с printf или библиотекой типа fastformat .

  • Если ваш operator<< не возвращает ссылку на класс, который позволяет operator<<чтобы быть в цепочке, вы получите ошибки типа "У меня есть следующая ошибка для строки" DEBUG ("hello" << "" << "world"); ": недопустимые операнды типов 'const char [6]' и'const char [2]' to bin 'operator <<' ".Я не верю, что <code>DEBUG("hello" << " " << "world"); можно заставить работать.DEBUG( "hello", "world"); может работать.

...