2012-02-08 6 views
2

У меня есть Qt/C++ acpplication, которая использует библиотеку C++.Как читать стандартную ошибку библиотеки?

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

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

Имея вместо этого возможность изменить его, что может быть хорошей практикой для переноса этих сообщений до приложения?

ответ

2

запись в STDERR на самом деле системный вызов:

write(2, "blahblah ..."); 

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

close(2);      // close old stderr 
int redirect_target = open(...); // open a file where you want to redirect to 
           // or use pipe, socket whatever you like 
dup2(redirect_target, 2);  // copy the redirect_target fd to fd number 2 
close(redirect_target); 

в вашей ситуации, вы будете нуждаться труба.

close(2); 
int pipefd[2]; 
pipe2(pipefd); 
dup2(pipefd[1], 2); 
close(pipefd[1]); 

затем, все записи в поток ошибок можно получить, прочитав трубы [0]:

read(pipe[0], buffer, ...); 
+0

Обратите внимание, что это очень легко получить затор при записи и чтения из тех же труб в том же потоке. 'Read' должен быть в цикле в другом потоке (или другом процессе), чтобы это было надежным. –

+0

И, конечно же, это чистый Unix. Я не думаю, что он может быть использован под Windows (но, может быть ... Windows предоставляет большую часть интерфейса Unix). –

+0

Я просто использую linux ... он может работать тогда ... – Stefano

4

Это очень плохой дизайн библиотеки. Однако ...

Как написать стандартную ошибку. Если outputing к std::cerr, , то вы можете изменить streambuf используется std::cerr, что-то вроде:

std::filebuf logStream; 
if (~logStream.open("logfile.txt")) 
    // Error handling... 
std::streambuf* originalCErrStream = std::cerr.rdbuf(); 
std::cerr.rdbuf(&logStream); 
// Processing here, with calls to library 
std::cerr.rdbuf(originalCErrStream); // Using RAII would be better. 

Только не забудьте восстановить первоначальный streambuf; оставляя std::cerr , указывая на filebuf, который был разрушен не хорошая идея.

Если они используют FILE*, есть функция freopen в C (и по включению в C++), которую вы можете использовать.

Если они используют выходной уровень системы (write под Unix, WriteFile под Windows), то вы будете иметь, чтобы использовать какой-то системный код на уровне изменить выход. (open на новый файл, close на Fd STDERR_FILENO и dup2 установить STDERR_FILENO использовать вновь открытый файл под Unix. Я не уверен, что это возможно под Windows, — может быть что-то с ReOpenFile или некоторая комбинация CloseHandle с последующим по CreateFile)

EDIT:.

Я просто заметил, что вы на самом деле хотите вывести в окно Qt. Это означает, что вам, вероятно, нужна строка, а не файл.Если в библиотеке используется std::cerr, вы можете использовать std::stringbuf вместо a std::filebuf; вы можете, по сути, создать собственный streambuf, , чтобы забрать звонки на sync (который обычно вызывается после каждого << на std::cerr). Если библиотека использует один из других методов, Единственное, что я могу придумать, это периодически читать файл, чтобы увидеть , если что-либо было добавлено. (Я хотел бы использовать read() в Unix, ReadFile() в Windows, для этого, для того, чтобы быть уверенными в том, способно отличить чтения нулевых байт, ничего не было написано после последнего чтения, и состояния ошибки из-за. FILE* и функции iostream лечения чтения нулевых байтов в конец файла, а не читать дальше.)

+0

библиотека печатает сообщение следующим образом: std: : cerr << os.str(). c_str() << std :: endl; – Stefano

+0

i never user stringbuf ... мне не очень понятно, как мое приложение будет знать, что что-то было отправлено ... у меня есть какое-то событие? – Stefano

+0

@Stefano Если вы выводите строку stringbuf, вам нужно использовать какой-то опрос. В таких случаях я обычно использую пользовательский 'streambuf'. ('std :: cerr' инициализируется так, что каждый' << 'приводит к' sync', вызываемому в streambuf.) –

1

Если они используют вызовы std::cerr, вы можете перенаправить это на std::ostringstream.

#include <iostream> 
#include <sstream> 

class cerr_redirector 
{ 
public: 
    cerr_redirector(std::ostream& os) 
     :backup_(std::cerr.rdbuf()) 
     ,sbuf_(os.rdbuf()) 
    { 
     std::cerr.rdbuf(sbuf_); 
    } 

    ~cerr_redirector() 
    { 
     std::cerr.rdbuf(backup_); 
    } 

private: 
    cerr_redirector(); 
    cerr_redirector(const cerr_redirector& copy); 
    cerr_redirector& operator =(const cerr_redirector& assign); 

    std::streambuf* backup_; 
    std::streambuf* sbuf_; 
}; 

Вы можете перехватывать вывод с помощью:

std::ostringstream os; 
cerr_redirector red(os); 
std::cerr << "This is written to the stream" << std::endl; 

std::cout будут затронуты:

std::cout << "This is written to stdout" << std::endl; 

Таким образом, вы можете проверить ваш захват работает:

std::cout << "and now: " << os.str() << std::endl; 

Или j ust добавьте содержимое os.str() в окно Qt.

Demonstration at ideone.

+1

Очень простое решение ... моя проблема заключается в том, как я знаю на стороне клиента, что что-то было написано? Должен ли я продолжать опрос или у меня есть какой-то «триггер»? – Stefano

+0

Вам, наверное, придется опросить. – Johnsyweb

+0

:(соб ... соб ... – Stefano

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