2016-03-05 4 views
1

Я использовал для входа в C с переменным количеством аргументов и форматирования, и я хочу, как я могу встретить это в C++.C++ переменное количество аргументов с форматированием для std :: cout

Через Q & A, как это (How to make a variadic macro for std::cout?), я знаю, как обрабатывать переменную сумму. Но то, что я до сих пор не знаю, как форматировать, потому что я не могу использовать такие методы, как «setbase» между аргументами сейчас.

Например:

// in C 
#define err(fmt, ...) (printf("[%s] "fmt"\n", __FUNCTION__, ##__VA_ARGS__)) 
#define FATAL(fmt, ...) do{\ 
    err(fmt, ##__VA_ARGS__);\ 
    CLEAN_UP;\ 
    exit(1);\ 
    }while(0) 

int main(){ 
    if(1) FATAL("Just a test: 0x%lX, %d", 1, 2); 
    return 0; 
} 

"FATAL" здесь, принимает переменное количество аргументов, с форматированием, напечатать их, и сделать некоторые дополнительные. Я не знаю, как объявить такой «FATAL» на C++.

+1

Не пытайтесь реализовать код стиля C на C++ .. В C++ вы должны полагаться на исключения, чтобы сообщать об ошибке. Затем макросы обычно следует избегать в C++ - предпочитают обычные функции. –

+0

Я хочу печатать переменные аргументы с форматированием без макросов, как это было в C, поэтому я прошу стиль в стиле C++. @MarinosK – hxpax

+0

Итак, вы хотите использовать методы, которые вы используете на C, даже если на C++ существуют различные методы, которые обычно рекомендуются, поскольку они безопасны по типу (среди прочего) и считаются предпочтительными? – Peter

ответ

0

Во-первых, даже это часто приводит к труднее поддерживать код, вы звоните всегда используют методы C в C++. stdio.h функции работают изначально на C++, и почти все макросы переведены одинаково.

Если вы хотите использовать плюсы C++ (лучший тип управления во время компиляции) ... вам придется забыть старые вариаторные функции C, особенно все xprintf. В любом случае, может быть одна интересная часть с шаблонами.

В любом случае пример, приведенный в ссылочном Q & A, - это все, что вам нужно. Инструкции форматирования просто вводятся в потоки одинаковыми значениями.

Но вот пример C++ 11, показывающий, что вы можете делать то, что хотите, без использования какого-либо макроса. Это гораздо больше, чем макро-версия C, но это выглядит формирует меня гораздо более ясно и расширяемым без уродливого do { ... } while 0 ИПЧХ:

#include <iostream> 
#include <string> 

// disp is a variadic templated function injecting any arguments to a stream 
// version for one single arg 
template <typename T> 
void disp(std::ostream& out, T arg) { 
    out << arg; 
} 

// recursively displays every arg 
template <typename T, typename ... U> 
void disp(std::ostream& out, T arg, U ... args) { 
    disp(out, arg) ; 
    disp(out, args...); 
} 

/* fatal displays its args to std::cout, preceded with "FATAL " and followed 
* by a newline. 
* It then does some cleanup and exits 
*/ 
template<typename ... T> 
void fatal(T ... args) { 
    std::cout << "FATAL "; 
    disp(std::cout, args...); 
    std::cout << std::endl; 

    // cleanup 
    exit(1); 
} 

int main() { 
    int i = 21; 
    int j = 32; 
    std::string s = "foo"; 
    if(1) fatal(1, " " , s, " ab ", i, " 0x", std::hex, j); 
    return 0; 
} 

выхода является

FATAL 1 foo ab 21 0x20 

Последнее, но не менее, вы бы лучше использовать throw FatalException(), где FatalException является подклассом std::exception вместо прямого использования exit(1). Вы могли бы даже написать в stringstream и передать полученную строку в исключение вместо записи в реальный поток, но тогда вы должны быть готовы иметь дело с исключениями bad_alloc.

+0

Работает, но немного многословно, чем макросы. – hxpax

1

C++ не является C! Хотя вы можете использовать код C-стиля (и часто C), это не рекомендуется. Во-первых, вы не должны обычно полагаться на макросы, поскольку они нарушают систему типов, вместо этого используют (возможно, встроенные или constexpr). Тогда вам не следует использовать технику обработки ошибок C-стиля, вместо этого используйте исключения. Я бы также рекомендовал против вариативных аргументов вообще и, наконец, вам не нужны методы форматирования строк в стиле C -> это C++, используйте строковые теги для форматирования вашего кода.

В вашем конкретном случае я хотел бы сделать что-то вроде этого:

#include <exception> 
#include <iostream> 
#include <sstream> 
#include <string> 

inline void fatal(std::string msg) { 
    // clean_up 
    throw std::runtime_error(msg); 
} 

int main(){ 
    std::ostringstream msg; 
    msg << "Just a test: " << 1 << 2; 
    if(1) fatal(msg.str()); 
    return 0; 
} 
+0

У этого есть затраты на форматирование, даже если нет ошибки. – Yakk

+0

Нет, это не глупый пример - в производственном коде вы должны форматировать свой код только в каком-либо предложении 'catch' или' if' и только при возникновении некоторой ошибки .. это ничем не отличается от форматирования внутри функции , –

+1

Если ваш пример глупо, напишите не глупый. – Yakk

1

Я также должен отметить, что C++ и C два разных языка с различными узорами и идиомы. C++ имеет лучшие альтернативы для многих конструкций C, которые являются более безопасными по типу и, следовательно, предпочтительнее. В вашем случае я бы выбрал исключение в этом случае. Если вы заблокируете catch(...) в своем коде, это прекратит вашу программу. Когда исключение распространяется, компилятор также вызывает деструкторы объектов и, таким образом, выполняет очистку. Если вы этого не сделали, я рекомендую вам ознакомиться с инициализацией-инициализацией ресурсов (RAII). Поскольку похоже, что вы переходите с C на C++, я рекомендую прочитать the tour of C++, который показывает основные принципы C++. Для RAII суть состоит в том, чтобы управлять ресурсами в специальных объектах-обработчиках, которые выделяются в конструкторе и освобождаются в деструкторе, и реализуют семантику перемещения. Таким образом, вы не можете утечка ресурсов. Примеры реализации: std::vector, std::unique_ptr или std::iostream. В качестве другого примера рассмотрим мьютекса запирание/отпирание:

class Mutex { 
public: 
    void lock() { ... } 
    void unlock() { ... } 
}; 

Когда вы используете его, то легко забыть разблокировку в вашем коде, особенно при внесении изменений в существующий код. Кроме того, в случае исключений вам нужны блоки try/catch, чтобы разблокировать все время. Вместо этого, определить MutexLocker класс:

class MutexLocker 
{ 
public: 
    MutexLocker(std::mullptr_t) = delete; 
    MutexLocker(Mutex* m): mutex_(m) {mutex_->lock();} 
    MutexLocker(MutexLocker const&) = delete; 
    MutexLocker& operator=(MutexLocker const&) = delete; 
    MutexLocker(MutexLocker&& l): mutex_(l.mutex_) {l.mutex_ = nullptr;} 
    MutexLocker& operator=(MutexLocker&& l) 
    { 
     mutex_ = l.mutex_, 
     l.mutex_ = nullptr; 
     return *this; 
    } 

    ~MutexLocker() {if (mutex_) {mutex_->unlock()} }; 
private: 
    Mutex* mutex_; 
}; 

Теперь, вы никогда не сможете забыть разблокировать мьютекс. Объект MutexLocker не может быть скопирован, но вы можете передать право собственности. Это превосходит все, что вы можете сделать в C.

Для вывода форматирования вы можете использовать «variadic template printf» в google, который должен дать вам несколько примеров, например. on Wikipedia:

void printf(const char *s) 
{ 
    while (*s) { 
     if (*s == '%') { 
      if (*(s + 1) == '%') { 
       ++s; 
      } 
      else { 
       throw std::runtime_error("invalid format string: missing arguments"); 
      } 
     } 
     std::cout << *s++; 
    } 
} 

template<typename T, typename... Args> 
void printf(const char *s, T value, Args... args) 
{ 
    while (*s) { 
     if (*s == '%') { 
      if (*(s + 1) == '%') { 
       ++s; 
      } 
      else { 
       std::cout << value; 
       s += 2; // this only works on 2 characters format strings (%d, %f, etc). Fails miserably with %5.4f 
       printf(s, args...); // call even when *s == 0 to detect extra arguments 
       return; 
      } 
     } 
     std::cout << *s++; 
    }  
} 

Или вы можете использовать библиотеку, например, boost::format или, возможно, тысячи других реализаций. Если это только для ведения журнала, вы можете взглянуть на структуру ведения журнала, например. boost.log.

+0

Макро "FATAL" здесь не главное. Я просто хочу напечатать переменное количество аргументов и форматировать некоторые из них в случаях. – hxpax

+0

@hxpax См. Второй абзац для ответа. – Jens

+0

@hxpax Было бы неплохо приспособить ваш пример к тому, что вы действительно хотите, потому что это сбивает с толку. Разделите его до минимального фрагмента кода без FATAL и т. Д. – Jens

2

Вы можете достичь этого, используя operator<< и пользовательский деструктор на объекте ad-hoc ведения журнала.

class log_error 
{ 
public: 
    log_error() = default; 
    log_error(log_error&& other) = default; 
    ~log_error() 
    { 
     // Do whatever you want with the input 
     // Add a timestamp, process/thread id 
     // Write it to a file, send it to a server ... 
     std::cerr << "[ERROR] " << ss.str() << std::endl; 
     throw std::runtime_error(ss.str()); 
    } 

    std::stringstream ss; 
}; 

template<typename T> 
log_error operator<<(log_error&& le, const T& t) 
{ 
    le.ss << t; 
    return std::move(le); 
} 

Я включил в себя основы для основного использования. Для более сложного использования вы хотите рассмотреть вариант копирования ctor/operator<<.

Использование очень идиоматическое C++. Но вы должны помнить ():

log_error() << "Ooops " << 23 << ", 0x" << std::setbase(16) << 23; 

Эта строка выведет сообщение и сгенерирует исключение.

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

Live example

+0

Спасибо, это хорошо, хотя не то, что я хочу. – hxpax

+0

@hxpax вы можете объяснить, как это не удовлетворяет вашим требованиям? – Zulan

+0

Он застрял с Исключением. Иногда я просто хочу регистрировать сообщения об ошибках; иногда я хочу регистрировать сообщения и выполнять некоторые дополнительные действия, кроме выхода, например {log; ACT1; act2;}, и это может быть вызвано много раз, поэтому я хочу объединить их. – hxpax

Смежные вопросы