paradiseo/eo/src/eoEvalCmd.h

143 lines
4.1 KiB
C++

/*
(c) Thales group, 2020
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation;
version 2 of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact: http://eodev.sourceforge.net
Authors:
Johann Dréo <johann.dreo@thalesgroup.com>
*/
#ifndef eoEvalCmd_H
#define eoEvalCmd_H
#include <cstdlib>
#include <array>
#include "eoEvalFunc.h"
#include "eoExceptions.h"
/** Call a system command to evaluate an individual.
*
* @note Tested only under Unix systems, may not be portable as is.
*
* Use the default string serialization of the EOT
* and pass it as command line arguments of the command.
* The command is expected to output on stdout a string
* that can be deserialized as a fitness.
*
* For example, an eoReal would lead to a call like:
* ```>&2 cmd INVALID 3 1.0 2.1 3.2```
*
* Throw an eoSystemError exception if the command exited with
* a return code different from zero.
*
*@ingroup Evaluation
*/
template<class EOT, int BUFFER_SIZE = 128>
class eoEvalCmd : public eoEvalFunc< EOT >
{
public:
using Fitness = typename EOT::Fitness;
/** Constructor
*
* @note The prefix and suffix are automatically
* separated from the command by a space.
*
* The formated command looks like: `prefix cmd infix sol suffix`
*
* The default prefix allows to redirect any output to stdout under Unix.
*
* @param cmd The command to run.
* @param prefix Inserted before the command
* @param suffix Inserted between cmd and the serialized solution.
* @param suffix Append after the solution.
*/
eoEvalCmd( const std::string cmd,
const std::string prefix = ">&1",
const std::string infix = "",
const std::string suffix = ""
) :
_cmd(cmd),
_prefix(prefix),
_infix(infix),
_suffix(suffix),
_last_call("")
{}
virtual void operator()( EOT& sol )
{
// Any modification to sol would makes it invalid,
// it is thus useless to evaluate it, if it is not invalid.
if(not sol.invalid()) {
return;
}
sol.fitness( call( sol ) );
}
//! Return the last command string that was called.
std::string last_call() const
{
return _last_call;
}
private:
const std::string _cmd;
const std::string _prefix;
const std::string _infix;
const std::string _suffix;
std::string _last_call;
Fitness call( EOT& sol )
{
std::ostringstream cmd;
cmd << _prefix << " " << _cmd << " "
<< _infix << " " << sol << " " << _suffix;
// Keep track of the built command for debugging purpose.
_last_call = cmd.str();
// Run the command and read its output.
std::array<char, BUFFER_SIZE> buffer;
std::string output;
FILE* pipe = popen(cmd.str().c_str(), "r");
if(not pipe) {
throw eoSystemError(cmd.str());
}
while(fgets(buffer.data(), BUFFER_SIZE, pipe) != NULL) {
output += buffer.data();
}
auto return_code = pclose(pipe);
// We want the evaluation to fail if the command failed
// (you can still catch the exception if you do not want your program to exit).
if(return_code != 0) {
throw eoSystemError(cmd.str(), return_code, output);
}
// Convert the output string in a valid fitness object.
Fitness f;
std::istringstream result(output);
result >> f;
return f;
}
};
#endif // eoEvalCmd_H