Прямое перенаправления stdout в stdin
От: 0xff  
Дата: 13.08.07 00:08
Оценка:
Доброго времени суток,

У меня возникла следующая проблема — необходимо перенаправить стандартный вывод в стандартный ввод напрямую, т.е. чтобы при использовании printf() инфу тут же можно было считать с помощью scanf() и т.д. Приложение под OpenGL без текстовых консолей. Пишу на VC++ ED.

Единственный способ, который удалось найти
freopen("tmp.tmp","wt",stdout);
freopen("tmp.tmp","rt",stdin);

Но смущает следующее. Во-первых, создается файл на диске. Во-вторых, по мере поступления инфы, файл разрастается... не смещать же указатель на начало файла после каждого printf()??

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

Пробовал также использовать dup2() — не помогло. Такое впечатление, что он вообще не работает...

Думаю еще про вариант сделать перегрузку стандартного printf(), но это все какие-то костыли.


Кто-нибудь знает более элегантный способ?

Заранее спасибо.

PS Хочется обойтись стандартным C без Win API, т.к. позже планирую переносить код под *nix.
Re: Прямое перенаправления stdout в stdin
От: den123 Израиль http://den123.smugmug.com
Дата: 13.08.07 03:40
Оценка: +1
Здравствуйте, 0xff, Вы писали:

0>Доброго времени суток,


0>У меня возникла следующая проблема — необходимо перенаправить стандартный вывод в стандартный ввод напрямую, т.е. чтобы при использовании printf() инфу тут же можно было считать с помощью scanf() и т.д. Приложение под OpenGL без текстовых консолей. Пишу на VC++ ED.


0>Единственный способ, который удалось найти

0>
0>freopen("tmp.tmp","wt",stdout);
0>freopen("tmp.tmp","rt",stdin);
0>

0>Но смущает следующее. Во-первых, создается файл на диске. Во-вторых, по мере поступления инфы, файл разрастается... не смещать же указатель на начало файла после каждого printf()??

0>Вообще задача банальная — хочется иметь все вкусности printf(), при этом вывод производить на графическую консоль, либо в файл.


0>Пробовал также использовать dup2() — не помогло. Такое впечатление, что он вообще не работает...


0>Думаю еще про вариант сделать перегрузку стандартного printf(), но это все какие-то костыли.



0>Кто-нибудь знает более элегантный способ?


0>Заранее спасибо.


0>PS Хочется обойтись стандартным C без Win API, т.к. позже планирую переносить код под *nix.

Поищи на кл.слово pipe
WBR — Yuriy
Re: Прямое перенаправления stdout в stdin
От: Кодт Россия  
Дата: 13.08.07 08:38
Оценка:
Здравствуйте, 0xff, Вы писали:

0>Вообще задача банальная — хочется иметь все вкусности printf(), при этом вывод производить на графическую консоль, либо в файл.


Трубки (pipe) пробовал?
Хотя с ними тоже заморочек хватит. Придётся ведь как-то синхронизировать вывод в stdout (ну или в другой глобально доступный дескриптор файла) и чтение оттуда — вывод на экран. И в каком потоке это делать, тоже вопрос.

Ещё можно распотрошить структуру FILE и реализации тех же fputchar / fputs / fprintf, чтобы посмотреть, куда там вклиниться со своими магическими функциями.
Правда, это платформенно-зависимо.

Можно использовать строго iostream со специально обученным streambuf'ом. (Пример рукоделия — это hello debug window
Автор: Кодт
Дата: 29.11.03
).
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: Прямое перенаправления stdout в stdin
От: 0xff  
Дата: 13.08.07 14:41
Оценка: 3 (1)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 0xff, Вы писали:


0>>Вообще задача банальная — хочется иметь все вкусности printf(), при этом вывод производить на графическую консоль, либо в файл.


К>Трубки (pipe) пробовал?

К>Хотя с ними тоже заморочек хватит. Придётся ведь как-то синхронизировать вывод в stdout (ну или в другой глобально доступный дескриптор файла) и чтение оттуда — вывод на экран. И в каком потоке это делать, тоже вопрос.

К>Ещё можно распотрошить структуру FILE и реализации тех же fputchar / fputs / fprintf, чтобы посмотреть, куда там вклиниться со своими магическими функциями.

К>Правда, это платформенно-зависимо.

К>Можно использовать строго iostream со специально обученным streambuf'ом. (Пример рукоделия — это hello debug window
Автор: Кодт
Дата: 29.11.03
).


Спасиб за инфу.

Для начала решил попробовать pipe'ы. Делаю так:

void some_func(){
    int pipe_fd[2] = {-1};

    if(_pipe(pipe_fd,128,_O_BINARY) == -1){
        perror("_pipe()");
    }else{
        if(_dup2(pipe_fd[0],0) == -1)
            perror("dup2(pipe_fd[0],0)");
        if(_dup2(pipe_fd[1],1) == -1)
            perror("dup2(pipe_fd[1],1)");
    }

    char tmp[128] = {0};

    setvbuf(stdout,NULL,_IONBF,0);

    printf("test\n");
    gets(tmp);
}


Все работает отлично, НО только в консольном приложении.
Если делать такое в приложении в котором консоль не была создана, то получается следующее недоразумение — fprintf(1,"bla-bla-bla\n") пишет инфу в пайп, а fprintf(stdout,"bla-bla-bla #2\n") — нет!

Решил проверить дескрипторы через _fileno(stdout), и что же получилось? А то что он равен -2
Лезу в хелп по слову _fileno, и вижу:

In Visual C++ 2005, there is a behavior change. If stdout or stderr is not associated with an output stream (for example, in a Windows application without a console window), the file descriptor returned is -2. In previous versions, the file descriptor returned was -1. This change allows applications to distinguish this condition from an error.


В общем, век живи, век RTFM... Получается, что с одной стороны stdout всегда есть и ссылается на дескриптор 1, а с другой стороны, только в случае если была создана консоль...

Короче говоря, теперь возник вопрос, как заново ассоциировать stdout и stdin с дескрипторами 1 и 0? Или единственный выход создавать консоль и прятать ее потом каким-то образом?


Спасибо
Re[3]: Прямое перенаправления stdout в stdin
От: Кодт Россия  
Дата: 13.08.07 15:41
Оценка:
Здравствуйте, 0xff, Вы писали:

0>В общем, век живи, век RTFM... Получается, что с одной стороны stdout всегда есть и ссылается на дескриптор 1, а с другой стороны, только в случае если была создана консоль...


Получается, что с помощью _dup2(...,1),
— когда у тебя есть консоль (и stdout сидит на 1), ты его переназначаешь,
— а когда его нет — то ты создаёшь новый дескриптор.
Вот и вся история.

0>Короче говоря, теперь возник вопрос, как заново ассоциировать stdout и stdin с дескрипторами 1 и 0? Или единственный выход создавать консоль и прятать ее потом каким-то образом?


Было бы проще не заморачивать себе голову, а завести ещё один глобальный поток, и дублировать туда дескриптор либо трубки, либо стдаута.
Грубо говоря,
// ...h
extern FILE* myout;
....

// ...cpp

FILE* myout;

....
if( use_pipe || _fileno(stdout)<0 )
{
    int fds[2] = {0,0};
    _pipe(fds, 1024, _O_BINARY);
    myout = _fdopen(fds[1], "w");
}
else
    myout = _fdopen(_fileno(stdout));
....



///// использование
fprintf(myout, "hello, %s", "world");
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[4]: Прямое перенаправления stdout в stdin
От: 0xff  
Дата: 13.08.07 16:16
Оценка: 27 (1)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 0xff, Вы писали:


К>Было бы проще не заморачивать себе голову, а завести ещё один глобальный поток, и дублировать туда дескриптор либо трубки, либо стдаута.

К>Грубо говоря,
К>
К>// ...h
К>extern FILE* myout;
К>....

К>// ...cpp

К>FILE* myout;

К>....
К>if( use_pipe || _fileno(stdout)<0 )
К>{
К>    int fds[2] = {0,0};
К>    _pipe(fds, 1024, _O_BINARY);
К>    myout = _fdopen(fds[1], "w");
К>}
К>else
К>    myout = _fdopen(_fileno(stdout));
К>....



К>///// использование
К>fprintf(myout, "hello, %s", "world");
К>


Я для себя только что нашел более удобный способ. Можно переопределить stdin и stdout через freopen(), указав, какой-нибудь временный файл, а dup2() закроет его сам. Дескрипторы конечно будут не 0 и 1, но зато printf() будет работать без проблем. Единственное, что временный файл надо удалить будет потом. В общем, код теперь примерно такой

    f = freopen("tmp.tmp","wb",stdout);
    f = freopen("tmp.tmp","rb",stdin);

    setvbuf(stdout,NULL,_IONBF,0);

...
    int pipe_fd[2] = {-1};

    if(_pipe(pipe_fd,1024,_O_BINARY) == -1){
        perror("_pipe()");
    }else{
        if(dup2(pipe_fd[0],_fileno(stdin)) == -1)
            perror("dup2(pipe_fd[0],_fileno(stdin))");
        if(dup2(pipe_fd[1],_fileno(stdout)) == -1)
            perror("dup2(pipe_fd[1],_fileno(stdout))");
    }

...

    printf("Test printf()\n");

...
    char tmp[1024] = {0};
    gets(tmp);
    // теперь tmp == "Test printf()"


Спасибо, за помощь.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.