diff --git a/include/minizinc/process.hh b/include/minizinc/process.hh index c1fd6580a..8cea04edc 100644 --- a/include/minizinc/process.hh +++ b/include/minizinc/process.hh @@ -72,6 +72,79 @@ namespace MiniZinc { #endif + class InputProvider { + + private: +#ifdef _WIN32 + HANDLE _h_pipe = nullptr; +#else + int _pipe = 0; +#endif + + public: + InputProvider() : stream(this) {} + + std::ostream* getStream() { + return &stream; + } + + virtual void provide() {} + +#ifdef _WIN32 + void setHandle(HANDLE h_pipe) { + _h_pipe = h_pipe; + } +#else + void setPipe(int pipe) { + _pipe = pipe; + } +#endif + + class PipeStream : public std::ostream { + + public: + explicit PipeStream(InputProvider* input) : std::ostream(new PipeBuf(input)) {} + + class PipeBuf : public std::streambuf { + + public: + explicit PipeBuf(InputProvider* input) : _input(input) {} + + protected: + std::streamsize xsputn(const char_type* s, std::streamsize n) override { + return write(s, n); + } + + int_type overflow(int_type ch) override { + return write(&ch, 1); + } + + private: + InputProvider* _input; + + std::streamsize write(const void* s, std::streamsize n) { +#ifdef _WIN32 + if (_input->_h_pipe == nullptr) { + return 0; + } + DWORD count; + BOOL success = WriteFile(_input->_h_pipe, reinterpret_cast(s), n, &count, nullptr); + return count; +#else + if (_input->_pipe == 0) { + return 0; + } + return ::write(_input->_pipe, s, n); +#endif + } + }; + }; + + protected: + InputProvider::PipeStream stream; + + }; + template class Process { protected: @@ -79,6 +152,7 @@ namespace MiniZinc { S2O* pS2Out; int timelimit; bool sigint; + InputProvider* _input; #ifndef _WIN32 static void handleInterrupt(int signal) { if (signal==SIGINT) @@ -90,8 +164,8 @@ namespace MiniZinc { static bool hadTerm; #endif public: - Process(std::vector& fzncmd, S2O* pso, int tl, bool si) - : _fzncmd(fzncmd), pS2Out(pso), timelimit(tl), sigint(si) { + Process(std::vector& fzncmd, S2O* pso, int tl, bool si, InputProvider* input) + : _fzncmd(fzncmd), pS2Out(pso), timelimit(tl), sigint(si), _input(input) { assert( 0!=pS2Out ); } int run(void) { @@ -173,6 +247,13 @@ namespace MiniZinc { // Stop ReadFile from blocking CloseHandle(g_hChildStd_OUT_Wr); CloseHandle(g_hChildStd_ERR_Wr); + + if (_input != NULL) { + _input->setHandle(g_hChildStd_IN_Wr); + _input->provide(); + CloseHandle(g_hChildStd_IN_Wr); + } + // Just close the child's in pipe here CloseHandle(g_hChildStd_IN_Rd); bool doneStdout = false; @@ -208,6 +289,12 @@ namespace MiniZinc { close(pipes[0][0]); close(pipes[1][1]); close(pipes[2][1]); + + if (_input != NULL) { + _input->setPipe(pipes[0][1]); + _input->provide(); + } + close(pipes[0][1]); fd_set fdset; diff --git a/include/minizinc/solvers/fzn_solverinstance.hh b/include/minizinc/solvers/fzn_solverinstance.hh index fd844ae35..d053eb1e6 100644 --- a/include/minizinc/solvers/fzn_solverinstance.hh +++ b/include/minizinc/solvers/fzn_solverinstance.hh @@ -29,6 +29,7 @@ namespace MiniZinc { int fzn_time_limit_ms = 0; int solver_time_limit_ms = 0; bool fzn_sigint = false; + bool fzn_use_stdin = false; bool fzn_needs_paths = false; bool fzn_output_passthrough = false; @@ -63,6 +64,8 @@ namespace MiniZinc { void resetSolver(void); + void printModel(Printer p); + protected: Expression* getSolutionValue(Id* id); }; diff --git a/solvers/fzn/fzn_solverinstance.cpp b/solvers/fzn/fzn_solverinstance.cpp index a2e0424ed..44357e836 100644 --- a/solvers/fzn/fzn_solverinstance.cpp +++ b/solvers/fzn/fzn_solverinstance.cpp @@ -80,6 +80,7 @@ namespace MiniZinc { << " -k, --keep-files\n For compatibility only: to produce .ozn and .fzn, use mzn2fzn\n" " or --fzn ..., --ozn ...\n" << " -r , --seed , --random-seed \n For compatibility only: use solver flags instead.\n" + << " --use-stdin\n Pass flatzinc to solver via stdin instead of temp file.\n" ; } @@ -143,6 +144,8 @@ namespace MiniZinc { } else if ( cop.getOption( "-f --free-search") ) { if (_opt.supports_f) _opt.fzn_flags.push_back("-f"); + } else if ( cop.getOption( "--use-stdin") ) { + _opt.fzn_use_stdin = true; } else { for (auto& fznf : _opt.fzn_solver_flags) { if (fznf.t==MZNFZNSolverFlag::FT_ARG && cop.getOption(fznf.n.c_str(), &buffer)) { @@ -186,6 +189,20 @@ namespace MiniZinc { } } + class FZN_Provider : public InputProvider { + + protected: + FZNSolverInstance* _inst; + Printer p; + + public: + explicit FZN_Provider(FZNSolverInstance* inst) : _inst(inst), p(*getStream(), 0, true) {}; + + void provide() override { + _inst->printModel(p); + } + + }; FZNSolverInstance::FZNSolverInstance(Env& env, std::ostream& log, SolverInstanceBase::Options* options) : SolverInstanceBase(env, log, options), _fzn(env.flat()), _ozn(env.output()) {} @@ -244,30 +261,6 @@ namespace MiniZinc { } int timelimit = opt.fzn_time_limit_ms; bool sigint = opt.fzn_sigint; - - FileUtils::TmpFile fznFile(".fzn"); - std::ofstream os(fznFile.name()); - Printer p(os, 0, true); - for (FunctionIterator it = _fzn->begin_functions(); it != _fzn->end_functions(); ++it) { - if(!it->removed()) { - Item& item = *it; - p.print(&item); - } - } - for (VarDeclIterator it = _fzn->begin_vardecls(); it != _fzn->end_vardecls(); ++it) { - if(!it->removed()) { - Item& item = *it; - p.print(&item); - } - } - for (ConstraintIterator it = _fzn->begin_constraints(); it != _fzn->end_constraints(); ++it) { - if(!it->removed()) { - Item& item = *it; - p.print(&item); - } - } - p.print(_fzn->solveItem()); - cmd_line.push_back(fznFile.name()); FileUtils::TmpFile* pathsFile = NULL; if(opt.fzn_needs_paths) { @@ -280,15 +273,29 @@ namespace MiniZinc { cmd_line.push_back(pathsFile->name()); } + FZN_Provider* input = nullptr; + FileUtils::TmpFile* fznFile = nullptr; + if (opt.fzn_use_stdin) { + input = new FZN_Provider(this); + } else { + fznFile = new FileUtils::TmpFile(".fzn"); + std::ofstream os(fznFile->name()); + Printer p(os, 0, true); + printModel(p); + cmd_line.push_back(fznFile->name()); + } + if(!opt.fzn_output_passthrough) { - Process proc(cmd_line, getSolns2Out(), timelimit, sigint); + Process proc(cmd_line, getSolns2Out(), timelimit, sigint, input); int exitStatus = proc.run(); + delete fznFile; delete pathsFile; return exitStatus == 0 ? getSolns2Out()->status : SolverInstance::ERROR; } else { Solns2Log s2l(getSolns2Out()->getOutput(), _log); - Process proc(cmd_line, &s2l, timelimit, sigint); + Process proc(cmd_line, &s2l, timelimit, sigint, input); int exitStatus = proc.run(); + delete fznFile; delete pathsFile; return exitStatus==0 ? SolverInstance::NONE : SolverInstance::ERROR; } @@ -303,4 +310,29 @@ namespace MiniZinc { assert(false); return NULL; } + + void FZNSolverInstance::printModel(Printer p) { + + for (FunctionIterator it = _fzn->begin_functions(); it != _fzn->end_functions(); ++it) { + if(!it->removed()) { + Item& item = *it; + p.print(&item); + } + } + for (VarDeclIterator it = _fzn->begin_vardecls(); it != _fzn->end_vardecls(); ++it) { + if(!it->removed()) { + Item& item = *it; + p.print(&item); + } + } + for (ConstraintIterator it = _fzn->begin_constraints(); it != _fzn->end_constraints(); ++it) { + if(!it->removed()) { + Item& item = *it; + p.print(&item); + } + } + p.print(_fzn->solveItem()); + + } + } diff --git a/solvers/mzn/mzn_solverinstance.cpp b/solvers/mzn/mzn_solverinstance.cpp index 8e280c9a4..cf60c8008 100644 --- a/solvers/mzn/mzn_solverinstance.cpp +++ b/solvers/mzn/mzn_solverinstance.cpp @@ -185,7 +185,7 @@ namespace MiniZinc { int timelimit = opt.mzn_time_limit_ms; bool sigint = opt.mzn_sigint; Solns2Log s2l(getSolns2Out()->getOutput(), _log); - Process proc(cmd_line, &s2l, timelimit, sigint); + Process proc(cmd_line, &s2l, timelimit, sigint, nullptr); int exitCode = proc.run(); return exitCode == 0 ? SolverInstance::UNKNOWN : SolverInstance::ERROR; diff --git a/solvers/nl/nl_solverinstance.cpp b/solvers/nl/nl_solverinstance.cpp index 3fab07da8..a879d4a18 100644 --- a/solvers/nl/nl_solverinstance.cpp +++ b/solvers/nl/nl_solverinstance.cpp @@ -209,7 +209,7 @@ namespace MiniZinc { cmd_line.push_back(opt.nl_solver); cmd_line.push_back(file_nl); cmd_line.push_back("-AMPL"); - Process proc(cmd_line, &s2o, 0, true); + Process proc(cmd_line, &s2o, 0, true, nullptr); exitStatus = proc.run(); if (exitStatus == 0) {