1

Is it possible to redirect stdout (NOT cout!) to a stream (ostream) (NOT to a file!)

Why? I am integrating a python interpreter in my application and want to capture print() calls from python code.

I am able to redirect cout this way by using rdbuf() but printf() or print() from python is not redirected since it goes to stdout and not cout

Sturm
  • 3,968
  • 10
  • 48
  • 78
  • 4
    I doubt we can answer this properly without more information on how you're going about things and without seeing some code. If it's your interpreter then you're the one implementing `print` so you can send the text wherever you like?? – Lightness Races in Orbit Sep 07 '18 at 11:04
  • You may be able to use [freopen](https://en.cppreference.com/w/c/io/freopen), not sure though. Although LRiO is right, you should be responsible for implementing printf for your python interpreter – hellow Sep 07 '18 at 11:14
  • Possible duplicate of [Rerouting stdin and stdout from C](https://stackoverflow.com/questions/584868/rerouting-stdin-and-stdout-from-c) – hellow Sep 07 '18 at 11:14
  • 1
    How do you launch this python script? I'm sure it is possible to provide custom standard output to the python script directly. – Marek R Sep 07 '18 at 11:20
  • IMO you should rephrase question punting stress and more information on `integrating a python interpreter`, since this is your actual problem and you want standard output of python script redirected to something. – Marek R Sep 07 '18 at 11:25
  • It depends on what you mean by "integrating a python interpreter". If your program is launching a separate process that starts a python interpreter as a separate program, then the techniques would differ quite substantially compared with using a library function that executes python script from within your program. Also, it depends on how the interpreter itself works (e.g. does it uses C's `printf()` to implement Python's `print` or `printf()`, or something else? None of those things are universal, so you need to specify (or research) them. You haven't, so nobody can usefully help you. – Peter Sep 07 '18 at 11:43
  • 1
    This is very much an A/B problem. Solutions to your actual problem are explained in https://stackoverflow.com/q/4307187/214671; the second one in particular seems really well made. – Matteo Italia Sep 07 '18 at 12:18

1 Answers1

0

On Linux, you can simply temporarily redirect STDOUT to a temporary file for the duration of the python script.

At the end of the python call you can read the contents of the temporary file and then dump the file.

I'm pretty sure Windows will have a similar mechanism.

Here's a first go with an attempt at some RAII to clean up all the handles.

#include <unistd.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <iostream>

void simulate_python_script() {
    std::printf("Written to STDOUT I think");
}

struct auto_fd {
    auto_fd(int fd)
            : fd_(fd) {}

    ~auto_fd() {
        if (fd_ != -1)
            ::close(fd_);
    }
    auto_fd(auto_fd const&) = delete;
    auto_fd& operator=(auto_fd const&) = delete;

    operator int() const {
        return fd_;
    }

    int fd_;
};

struct file_closer
{
    void operator()(FILE* p) const noexcept
    {
        ::fclose(p);
    }
};


using auto_fp = std::unique_ptr<FILE, file_closer>;
auto make_auto_fp(FILE* fp)
{
    return auto_fp(fp, file_closer());
}

struct push_fd {
    push_fd(int target, int new_fd)
            : saved_(::dup(target)), target_(target) {
        ::dup2(new_fd, target);
    }

    ~push_fd() {
        if (saved_ != -1) {
            ::dup2(saved_, target_);
            ::close(saved_);
        }
    }

    int saved_, target_;
};

int main() {
    using namespace std::literals;


    auto tempfp = make_auto_fp(::tmpfile());
    auto tempfd = auto_fd(::fileno(tempfp.get()));

    // redirect STDOUT to the temp file with RAII
    {
        push_fd fd_save(1, tempfd);
        simulate_python_script();
    }

    // now read the file which Python thought was STDOUT    
    char buf[256];
    while (auto count = ::read(tempfd, buf, 256)) {
        if (count < 0) break; // error condition
        std::cout.write(buf, count);
    }

    std::cout << std::endl;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142