// vim:foldmethod=marker /* Copyright (c) 2013 Daniel Stahlke (dan@stahlke.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* A C++ interface to gnuplot. * Web page: http://www.stahlke.org/dan/gnuplot-iostream * Documentation: https://gitorious.org/gnuplot-iostream/pages/Home * * The whole library consists of this monolithic header file, for ease of installation (the * Makefile and *.cc files are only for examples and tests). * * TODO: * What version of boost is currently required? * Callbacks via gnuplot's 'bind' function. This would allow triggering user functions when * keys are pressed in the gnuplot window. However, it would require a PTY reader thread. * Maybe temporary files read in a thread can replace PTY stuff. */ #ifndef GNUPLOT_IOSTREAM_H #define GNUPLOT_IOSTREAM_H // {{{1 Includes and defines #define GNUPLOT_IOSTREAM_VERSION 2 #ifndef GNUPLOT_ENABLE_CXX11 # define GNUPLOT_ENABLE_CXX11 (__cplusplus >= 201103) #endif // C system includes #include #ifdef GNUPLOT_ENABLE_PTY # include # include # include #endif // GNUPLOT_ENABLE_PTY // C++ system includes #include #include #include #include #include #include #include #include #include #include #include #if GNUPLOT_ENABLE_CXX11 # include #endif #include #include #include #include #include #include // This is the version of boost which has v3 of the filesystem libraries by default. #if BOOST_VERSION >= 104600 # define GNUPLOT_USE_TMPFILE # include #endif // BOOST_VERSION // This is used because VS2008 doesn't have stdint.h. #include // Note: this is here for reverse compatibility. The new way to enable blitz support is to // just include the gnuplot-iostream.h header after you include the blitz header (likewise for // armadillo). #ifdef GNUPLOT_ENABLE_BLITZ # include #endif #ifdef BOOST_STATIC_ASSERT_MSG # define GNUPLOT_STATIC_ASSERT_MSG(cond, msg) BOOST_STATIC_ASSERT_MSG((cond), msg) #else # define GNUPLOT_STATIC_ASSERT_MSG(cond, msg) BOOST_STATIC_ASSERT((cond)) #endif // If this is defined, warn about use of deprecated functions. #ifdef GNUPLOT_DEPRECATE_WARN # ifdef __GNUC__ # define GNUPLOT_DEPRECATE(msg) __attribute__ ((deprecated(msg))) # elif defined(_MSC_VER) # define GNUPLOT_DEPRECATE(msg) __declspec(deprecated(msg)) # else # define GNUPLOT_DEPRECATE(msg) # endif #else # define GNUPLOT_DEPRECATE(msg) #endif // Patch for Windows by Damien Loison #ifdef _WIN32 # include # define GNUPLOT_PCLOSE _pclose # define GNUPLOT_POPEN _popen # define GNUPLOT_FILENO _fileno #else # define GNUPLOT_PCLOSE pclose # define GNUPLOT_POPEN popen # define GNUPLOT_FILENO fileno #endif #ifdef _WIN32 # define GNUPLOT_ISNAN _isnan #else // cppreference.com says std::isnan is only for C++11. However, this seems to work on Linux // and I am assuming that if isnan exists in math.h then std::isnan exists in cmath. # define GNUPLOT_ISNAN std::isnan #endif // MSVC gives a warning saying that fopen and getenv are not secure. But they are secure. // Unfortunately their replacement functions are not simple drop-in replacements. The best // solution is to just temporarily disable this warning whenever fopen or getenv is used. // http://stackoverflow.com/a/4805353/1048959 #if defined(_MSC_VER) && _MSC_VER >= 1400 # define GNUPLOT_MSVC_WARNING_4996_PUSH \ __pragma(warning(push)) \ __pragma(warning(disable:4996)) # define GNUPLOT_MSVC_WARNING_4996_POP \ __pragma(warning(pop)) #else # define GNUPLOT_MSVC_WARNING_4996_PUSH # define GNUPLOT_MSVC_WARNING_4996_POP #endif #ifndef GNUPLOT_DEFAULT_COMMAND #ifdef _WIN32 // "pgnuplot" is considered deprecated according to the Internet. It may be faster. It // doesn't seem to handle binary data though. //# define GNUPLOT_DEFAULT_COMMAND "pgnuplot -persist" // On Windows, gnuplot echos commands to stderr. So we forward its stderr to the bit bucket. // Unfortunately, this means you will miss out on legitimate error messages. # define GNUPLOT_DEFAULT_COMMAND "gnuplot -persist 2> NUL" #else # define GNUPLOT_DEFAULT_COMMAND "gnuplot -persist" #endif #endif // }}}1 namespace gnuplotio { // {{{1 Basic traits helpers // // The mechanisms constructed in this section enable us to detect what sort of datatype has // been passed to a function. // This can be specialized as needed, in order to not use the STL interfaces for specific // classes. template struct dont_treat_as_stl_container { typedef boost::mpl::bool_ type; }; BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type) BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator) template struct is_like_stl_container { typedef boost::mpl::and_< typename has_value_type::type, typename has_const_iterator::type, boost::mpl::not_ > > type; static const bool value = type::value; }; template struct is_boost_tuple_nulltype { static const bool value = false; typedef boost::mpl::bool_ type; }; template <> struct is_boost_tuple_nulltype { static const bool value = true; typedef boost::mpl::bool_ type; }; BOOST_MPL_HAS_XXX_TRAIT_DEF(head_type) BOOST_MPL_HAS_XXX_TRAIT_DEF(tail_type) template struct is_boost_tuple { typedef boost::mpl::and_< typename has_head_type::type, typename has_tail_type::type > type; static const bool value = type::value; }; // More fine-grained, but doesn't compile! //template //struct is_boost_tuple { // typedef boost::mpl::and_< // typename boost::is_class::type, // typename boost::mpl::and_< // typename has_head_type::type, // typename boost::mpl::and_< // typename has_tail_type::type, // typename boost::mpl::or_< // typename is_boost_tuple_nulltype::type, // typename is_boost_tuple::type // >::type // >::type // >::type // > type; //}; // //template <> //struct is_boost_tuple { // typedef boost::mpl::bool_ type; //}; // }}}1 // {{{1 Tmpfile helper class #ifdef GNUPLOT_USE_TMPFILE // RAII temporary file. File is removed when this object goes out of scope. class GnuplotTmpfile { public: GnuplotTmpfile() : file(boost::filesystem::unique_path( boost::filesystem::temp_directory_path() / "tmp-gnuplot-%%%%-%%%%-%%%%-%%%%")) { } private: // noncopyable GnuplotTmpfile(const GnuplotTmpfile &); const GnuplotTmpfile& operator=(const GnuplotTmpfile &); public: ~GnuplotTmpfile() { // it is never good to throw exceptions from a destructor try { remove(file); } catch(const std::exception &) { std::cerr << "Failed to remove temporary file " << file << std::endl; } } public: boost::filesystem::path file; }; #endif // GNUPLOT_USE_TMPFILE // }}}1 // {{{1 Feedback helper classes // // Used for reading stuff sent from gnuplot via gnuplot's "print" function. // // For example, this is used for capturing mouse clicks in the gnuplot window. There are two // implementations, only the first of which is complete. The first implementation allocates a // PTY (pseudo terminal) which is written to by gnuplot and read by us. This only works in // Linux. The second implementation creates a temporary file which is written to by gnuplot // and read by us. However, this doesn't currently work since fscanf doesn't block. It would // be possible to get this working using a more complicated mechanism (select or threads) but I // haven't had the need for it. class GnuplotFeedback { public: GnuplotFeedback() { } virtual ~GnuplotFeedback() { } virtual std::string filename() const = 0; virtual FILE *handle() const = 0; private: // noncopyable GnuplotFeedback(const GnuplotFeedback &); const GnuplotFeedback& operator=(const GnuplotFeedback &); }; #ifdef GNUPLOT_ENABLE_PTY #define GNUPLOT_ENABLE_FEEDBACK class GnuplotFeedbackPty : public GnuplotFeedback { public: explicit GnuplotFeedbackPty(bool debug_messages) : pty_fn(), pty_fh(NULL), master_fd(-1), slave_fd(-1) { // adapted from http://www.gnuplot.info/files/gpReadMouseTest.c if(0 > openpty(&master_fd, &slave_fd, NULL, NULL, NULL)) { perror("openpty"); throw std::runtime_error("openpty failed"); } char pty_fn_buf[1024]; if(ttyname_r(slave_fd, pty_fn_buf, 1024)) { perror("ttyname_r"); throw std::runtime_error("ttyname failed"); } pty_fn = std::string(pty_fn_buf); if(debug_messages) { std::cerr << "feedback_fn=" << pty_fn << std::endl; } // disable echo struct termios tios; if(tcgetattr(slave_fd, &tios) < 0) { perror("tcgetattr"); throw std::runtime_error("tcgetattr failed"); } tios.c_lflag &= ~(ECHO | ECHONL); if(tcsetattr(slave_fd, TCSAFLUSH, &tios) < 0) { perror("tcsetattr"); throw std::runtime_error("tcsetattr failed"); } pty_fh = fdopen(master_fd, "r"); if(!pty_fh) { throw std::runtime_error("fdopen failed"); } } private: // noncopyable GnuplotFeedbackPty(const GnuplotFeedbackPty &); const GnuplotFeedbackPty& operator=(const GnuplotFeedbackPty &); public: ~GnuplotFeedbackPty() { if(pty_fh) fclose(pty_fh); if(master_fd > 0) ::close(master_fd); if(slave_fd > 0) ::close(slave_fd); } std::string filename() const { return pty_fn; } FILE *handle() const { return pty_fh; } private: std::string pty_fn; FILE *pty_fh; int master_fd, slave_fd; }; //#elif defined GNUPLOT_USE_TMPFILE //// Currently this doesn't work since fscanf doesn't block (need something like "tail -f") //#define GNUPLOT_ENABLE_FEEDBACK //class GnuplotFeedbackTmpfile : public GnuplotFeedback { //public: // explicit GnuplotFeedbackTmpfile(bool debug_messages) : // tmp_file(), // fh(NULL) // { // if(debug_messages) { // std::cerr << "feedback_fn=" << filename() << std::endl; // } // GNUPLOT_MSVC_WARNING_4996_PUSH // fh = std::fopen(filename().c_str(), "a"); // GNUPLOT_MSVC_WARNING_4996_POP // } // // ~GnuplotFeedbackTmpfile() { // fclose(fh); // } // //private: // // noncopyable // GnuplotFeedbackTmpfile(const GnuplotFeedbackTmpfile &); // const GnuplotFeedbackTmpfile& operator=(const GnuplotFeedbackTmpfile &); // //public: // std::string filename() const { // return tmp_file.file.string(); // } // // FILE *handle() const { // return fh; // } // //private: // GnuplotTmpfile tmp_file; // FILE *fh; //}; #endif // GNUPLOT_ENABLE_PTY, GNUPLOT_USE_TMPFILE // }}}1 // {{{1 Traits and printers for entry datatypes // // This section contains the mechanisms for sending scalar and tuple data to gnuplot. Pairs // and tuples are sent by appealing to the senders defined for their component scalar types. // Senders for arrays are defined in a later section. // // There are three classes which need to be specialized for each supported datatype: // 1. TextSender to send data as text. The default is to just send using the ostream's `<<` // operator. // 2. BinarySender to send data as binary, in a format which gnuplot can understand. There is // no default implementation (unimplemented types raise a compile time error), however // inheriting from FlatBinarySender will send the data literally as it is stored in memory. // This suffices for most of the standard built-in types (e.g. uint32_t or double). // 3. BinfmtSender sends a description of the data format to gnuplot (e.g. `%uint32`). Type // `show datafile binary datasizes` in gnuplot to see a list of supported formats. // {{{2 Basic entry datatypes // Default TextSender, sends data using `<<` operator. template struct TextSender { static void send(std::ostream &stream, const T &v) { stream << v; } }; // Default BinarySender, raises a compile time error. template struct BinarySender { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "BinarySender class not specialized for this type"); // This is here to avoid further compilation errors, beyond what the assert prints. static void send(std::ostream &stream, const T &v); }; // This is a BinarySender implementation that just sends directly from memory. Data types // which can be sent this way can have their BinarySender specialization inherit from this. template struct FlatBinarySender { static void send(std::ostream &stream, const T &v) { stream.write(reinterpret_cast(&v), sizeof(T)); } }; // Default BinfmtSender, raises a compile time error. template struct BinfmtSender { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "BinfmtSender class not specialized for this type"); // This is here to avoid further compilation errors, beyond what the assert prints. static void send(std::ostream &); }; // BinfmtSender implementations for basic data types supported by gnuplot. // Types from boost/cstdint.hpp are used because VS2008 doesn't have stdint.h. template<> struct BinfmtSender< float> { static void send(std::ostream &stream) { stream << "%float"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%double"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%int8"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint8"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%int16"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint16"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%int32"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint32"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%int64"; } }; template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint64"; } }; // BinarySender implementations for basic data types supported by gnuplot. These types can // just be sent as stored in memory, so all these senders inherit from FlatBinarySender. template<> struct BinarySender< float> : public FlatBinarySender< float> { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; template<> struct BinarySender : public FlatBinarySender { }; // Make char types print as integers, not as characters. template struct CastIntTextSender { static void send(std::ostream &stream, const T &v) { stream << int(v); } }; template<> struct TextSender< char> : public CastIntTextSender< char> { }; template<> struct TextSender< signed char> : public CastIntTextSender< signed char> { }; template<> struct TextSender< unsigned char> : public CastIntTextSender< unsigned char> { }; // Make sure that the same not-a-number string is printed on all platforms. template struct FloatTextSender { static void send(std::ostream &stream, const T &v) { if(GNUPLOT_ISNAN(v)) { stream << "nan"; } else { stream << v; } } }; template<> struct TextSender< float> : FloatTextSender< float> { }; template<> struct TextSender< double> : FloatTextSender< double> { }; template<> struct TextSender : FloatTextSender { }; // }}}2 // {{{2 std::pair support template struct TextSender > { static void send(std::ostream &stream, const std::pair &v) { TextSender::send(stream, v.first); stream << " "; TextSender::send(stream, v.second); } }; template struct BinfmtSender > { static void send(std::ostream &stream) { BinfmtSender::send(stream); BinfmtSender::send(stream); } }; template struct BinarySender > { static void send(std::ostream &stream, const std::pair &v) { BinarySender::send(stream, v.first); BinarySender::send(stream, v.second); } }; // }}}2 // {{{2 std::complex support template struct TextSender > { static void send(std::ostream &stream, const std::complex &v) { TextSender::send(stream, v.real()); stream << " "; TextSender::send(stream, v.imag()); } }; template struct BinfmtSender > { static void send(std::ostream &stream) { BinfmtSender::send(stream); BinfmtSender::send(stream); } }; template struct BinarySender > { static void send(std::ostream &stream, const std::complex &v) { BinarySender::send(stream, v.real()); BinarySender::send(stream, v.imag()); } }; // }}}2 // {{{2 boost::tuple support template struct TextSender, boost::mpl::not_ > > >::type > { static void send(std::ostream &stream, const T &v) { TextSender::send(stream, v.get_head()); stream << " "; TextSender::send(stream, v.get_tail()); } }; template struct TextSender, is_boost_tuple_nulltype > >::type > { static void send(std::ostream &stream, const T &v) { TextSender::send(stream, v.get_head()); } }; template struct BinfmtSender, boost::mpl::not_ > > >::type > { static void send(std::ostream &stream) { BinfmtSender::send(stream); stream << " "; BinfmtSender::send(stream); } }; template struct BinfmtSender, is_boost_tuple_nulltype > >::type > { static void send(std::ostream &stream) { BinfmtSender::send(stream); } }; template struct BinarySender, boost::mpl::not_ > > >::type > { static void send(std::ostream &stream, const T &v) { BinarySender::send(stream, v.get_head()); BinarySender::send(stream, v.get_tail()); } }; template struct BinarySender, is_boost_tuple_nulltype > >::type > { static void send(std::ostream &stream, const T &v) { BinarySender::send(stream, v.get_head()); } }; // }}}2 // {{{2 std::tuple support #if GNUPLOT_ENABLE_CXX11 // http://stackoverflow.com/questions/6245735/pretty-print-stdtuple template struct int_{}; // compile-time counter template void std_tuple_formatcode_helper(std::ostream &stream, const Tuple *, int_) { std_tuple_formatcode_helper(stream, (const Tuple *)(0), int_()); stream << " "; BinfmtSender::type>::send(stream); } template void std_tuple_formatcode_helper(std::ostream &stream, const Tuple *, int_<0>) { BinfmtSender::type>::send(stream); } template struct BinfmtSender > { typedef typename std::tuple Tuple; static void send(std::ostream &stream) { std_tuple_formatcode_helper(stream, (const Tuple *)(0), int_()); } }; template void std_tuple_textsend_helper(std::ostream &stream, const Tuple &v, int_) { std_tuple_textsend_helper(stream, v, int_()); stream << " "; TextSender::type>::send(stream, std::get(v)); } template void std_tuple_textsend_helper(std::ostream &stream, const Tuple &v, int_<0>) { TextSender::type>::send(stream, std::get<0>(v)); } template struct TextSender > { typedef typename std::tuple Tuple; static void send(std::ostream &stream, const Tuple &v) { std_tuple_textsend_helper(stream, v, int_()); } }; template void std_tuple_binsend_helper(std::ostream &stream, const Tuple &v, int_) { std_tuple_binsend_helper(stream, v, int_()); BinarySender::type>::send(stream, std::get(v)); } template void std_tuple_binsend_helper(std::ostream &stream, const Tuple &v, int_<0>) { BinarySender::type>::send(stream, std::get<0>(v)); } template struct BinarySender > { typedef typename std::tuple Tuple; static void send(std::ostream &stream, const Tuple &v) { std_tuple_binsend_helper(stream, v, int_()); } }; #endif // GNUPLOT_ENABLE_CXX11 // }}}2 // }}}1 // {{{1 ArrayTraits and Range classes // // This section handles sending of array data to gnuplot. It is rather complicated because of // the diversity of storage schemes supported. For example, it treats a // `std::pair, std::vector>` in the same way as a // `std::vector>`, iterating through the two arrays in lockstep, and sending // pairs to gnuplot as columns. In fact, any nested combination of pairs, tuples, STL // containers, Blitz arrays, and Armadillo arrays is supported (with the caveat that, for // instance, Blitz arrays should never be put into an STL container or you will suffer // unpredictable results due the way Blitz handles assignment). Nested containers are // considered to be multidimensional arrays. Although gnuplot only supports 1D and 2D arrays, // our module is in principle not limited. // // The ArrayTraits class is specialized for every supported array or container type (the // default, unspecialized, version of ArrayTraits exists only to tell you that something is // *not* a container, via the is_container flag). ArrayTraits tells you the depth of a nested // (or multidimensional) container, as well as the value type, and provides a specialized // sort of iterator (a.k.a. "range"). Ranges are sort of like STL iterators, except that they // have built-in knowledge of the end condition so you don't have to carry around both a // begin() and an end() iterator like in STL. // // As an example of how this works, consider a std::pair of std::vectors. Ultimately this gets // sent to gnuplot as two columns, so the two vectors need to be iterated in lockstep. // The `value_type` of `std::pair, std::vector>` is then `std::pair` // and this is what deferencing the range (iterator) gives. Internally, this range is built // out of a pair of ranges (PairOfRange class), the `inc()` (advance to next element) // method calls `inc()` on each of the children, and `deref()` calls `deref()` on each child // and combines the results to return a `std::pair`. Tuples are handled as nested pairs. // // In addition to PairOfRange, there is also a VecOfRange class that can be used to treat the // outermost part of a nested container as if it were a tuple. Since tuples are printed as // columns, this is like treating a multidimensional array as if it were column-major. A // VecOfRange is obtained by calling `get_columns_range`. This is used by, for instance, // `send1d_colmajor`. The implementation is similar to that of PairOfRange. // // The range, accessed via `ArrayTraits::get_range`, will be of a different class depending // on T, and this is defined by the ArrayTraits specialization for T. It will always have // methods `inc()` to advance to the next element and `is_end()` for checking whether one has // advanced past the final element. For nested containers, `deref_subiter()` returns a range // iterator for the next nesting level. When at the innermost level of nesting, `deref()` // returns the value of the entry the iterator points to (a scalar, pair, or tuple). // Only one of `deref()` or `deref_subiter()` will be available, depending on whether there are // deeper levels of nesting. The typedefs `value_type` and `subiter_type` tell the return // types of these two methods. // // Support for standard C++ and boost containers and tuples of containers is provided in this // section. Support for third party packages like Blitz and Armadillo is in a later section. // {{{2 ArrayTraits generic class and defaults // Error messages involving this stem from treating something that was not a container as if it // was. This is only here to allow compiliation without errors in normal cases. struct Error_WasNotContainer { // This is just here to make VC++ happy. // https://connect.microsoft.com/VisualStudio/feedback/details/777612/class-template-specialization-that-compiles-in-g-but-not-visual-c typedef void subiter_type; }; // Error messages involving this stem from calling deref instead of deref_subiter for a nested // container. struct Error_InappropriateDeref { }; // The unspecialized version of this class gives traits for things that are *not* arrays. template class ArrayTraits { public: // The value type of elements after all levels of nested containers have been dereferenced. typedef Error_WasNotContainer value_type; // The type of the range (a.k.a. iterator) that `get_range()` returns. typedef Error_WasNotContainer range_type; // Tells whether T is in fact a container type. static const bool is_container = false; // This flag supports the legacy behavior of automatically guessing whether the data should // be treated as column major. This guessing happens when `send()` is called rather than // `send1d()` or `send2d()`. This is deprecated, but is still supported for reverse // compatibility. static const bool allow_auto_unwrap = false; // The number of levels of nesting, or the dimension of multidimensional arrays. static const size_t depth = 0; // Returns the range (iterator) for an array. static range_type get_range(const T &) { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T)==0), "argument was not a container"); throw std::logic_error("static assert should have been triggered by this point"); } }; // Most specializations of ArrayTraits should inherit from this (with V set to the value type). // It sets some default values. template class ArrayTraitsDefaults { public: typedef V value_type; static const bool is_container = true; static const bool allow_auto_unwrap = true; static const size_t depth = ArrayTraits::depth + 1; }; // This handles reference types, such as are given with boost::tie. // It also allows for instance "ArrayTraits" to match "ArrayTraits". // I think this is okay to do... The alternative is to use remove_reference all over the place. template class ArrayTraits : public ArrayTraits { }; // FIXME - is this okay? // It supports gp.send1d(std::forward_as_tuple(x, std::move(y))); #if GNUPLOT_ENABLE_CXX11 template class ArrayTraits : public ArrayTraits { }; #endif // }}}2 // {{{2 STL container support template class IteratorRange { public: IteratorRange() { } IteratorRange(const TI &_it, const TI &_end) : it(_it), end(_end) { } static const bool is_container = ArrayTraits::is_container; typedef typename boost::mpl::if_c::type value_type; typedef typename ArrayTraits::range_type subiter_type; bool is_end() const { return it == end; } void inc() { ++it; } value_type deref() const { GNUPLOT_STATIC_ASSERT_MSG(sizeof(TV) && !is_container, "deref called on nested container"); return *it; } subiter_type deref_subiter() const { GNUPLOT_STATIC_ASSERT_MSG(sizeof(TV) && is_container, "deref_iter called on non-nested container"); return ArrayTraits::get_range(*it); } private: TI it, end; }; template class ArrayTraits >::type > : public ArrayTraitsDefaults { public: typedef IteratorRange range_type; static range_type get_range(const T &arg) { return range_type(arg.begin(), arg.end()); } }; // }}}2 // {{{2 C style array support template class ArrayTraits : public ArrayTraitsDefaults { public: typedef IteratorRange range_type; static range_type get_range(const T (&arg)[N]) { return range_type(arg, arg+N); } }; // }}}2 // {{{2 std::pair support template class PairOfRange { template friend void deref_and_print(std::ostream &, const PairOfRange &, PrintMode); public: PairOfRange() { } PairOfRange(const RT &_l, const RU &_r) : l(_l), r(_r) { } static const bool is_container = RT::is_container && RU::is_container; typedef std::pair value_type; typedef PairOfRange subiter_type; bool is_end() const { bool el = l.is_end(); bool er = r.is_end(); if(el != er) { throw std::length_error("columns were different lengths"); } return el; } void inc() { l.inc(); r.inc(); } value_type deref() const { return std::make_pair(l.deref(), r.deref()); } subiter_type deref_subiter() const { return subiter_type(l.deref_subiter(), r.deref_subiter()); } private: RT l; RU r; }; template class ArrayTraits > { public: typedef PairOfRange::range_type, typename ArrayTraits::range_type> range_type; typedef std::pair::value_type, typename ArrayTraits::value_type> value_type; static const bool is_container = ArrayTraits::is_container && ArrayTraits::is_container; // Don't allow colwrap since it's already wrapped. static const bool allow_auto_unwrap = false; // It is allowed for l_depth != r_depth, for example one column could be 'double' and the // other column could be 'vector'. static const size_t l_depth = ArrayTraits::depth; static const size_t r_depth = ArrayTraits::depth; static const size_t depth = (l_depth < r_depth) ? l_depth : r_depth; static range_type get_range(const std::pair &arg) { return range_type( ArrayTraits::get_range(arg.first), ArrayTraits::get_range(arg.second) ); } }; // }}}2 // {{{2 boost::tuple support template class ArrayTraits, boost::mpl::not_ > > >::type > : public ArrayTraits< typename std::pair< typename T::head_type, typename T::tail_type > > { public: typedef typename T::head_type HT; typedef typename T::tail_type TT; typedef ArrayTraits > parent; static typename parent::range_type get_range(const T &arg) { return typename parent::range_type( ArrayTraits::get_range(arg.get_head()), ArrayTraits::get_range(arg.get_tail()) ); } }; template class ArrayTraits, is_boost_tuple_nulltype > >::type > : public ArrayTraits< typename T::head_type > { typedef typename T::head_type HT; typedef ArrayTraits parent; public: static typename parent::range_type get_range(const T &arg) { return parent::get_range(arg.get_head()); } }; // }}}2 // {{{2 std::tuple support #if GNUPLOT_ENABLE_CXX11 template struct StdTupUnwinder { typedef std::pair< typename StdTupUnwinder::type, typename std::tuple_element::type > type; static typename ArrayTraits::range_type get_range(const Tuple &arg) { return typename ArrayTraits::range_type( StdTupUnwinder::get_range(arg), ArrayTraits::type>::get_range(std::get(arg)) ); } }; template struct StdTupUnwinder { typedef typename std::tuple_element<0, Tuple>::type type; static typename ArrayTraits::range_type get_range(const Tuple &arg) { return ArrayTraits::get_range(std::get<0>(arg)); } }; template class ArrayTraits > : public ArrayTraits, sizeof...(Args)-1>::type> { typedef std::tuple Tuple; typedef ArrayTraits::type> parent; public: static typename parent::range_type get_range(const Tuple &arg) { return StdTupUnwinder, sizeof...(Args)-1>::get_range(arg); } }; #endif // GNUPLOT_ENABLE_CXX11 // }}}2 // {{{2 Support column unwrap of container (VecOfRange) // // VecOfRange (created via `get_columns_range()`) treats the outermost level of a nested // container as if it were a tuple. Since tuples are sent to gnuplot as columns, this has the // effect of addressing a multidimensional array in column major order. template class VecOfRange { template friend void deref_and_print(std::ostream &, const VecOfRange &, PrintMode); public: VecOfRange() { } explicit VecOfRange(const std::vector &_rvec) : rvec(_rvec) { } static const bool is_container = RT::is_container; // Don't allow colwrap since it's already wrapped. static const bool allow_auto_unwrap = false; typedef std::vector value_type; typedef VecOfRange subiter_type; bool is_end() const { if(rvec.empty()) return true; bool ret = rvec[0].is_end(); for(size_t i=1; i subvec(rvec.size()); for(size_t i=0; i rvec; }; template VecOfRange::range_type::subiter_type> get_columns_range(const T &arg) { typedef typename ArrayTraits::range_type::subiter_type U; std::vector rvec; typename ArrayTraits::range_type outer = ArrayTraits::get_range(arg); while(!outer.is_end()) { rvec.push_back(outer.deref_subiter()); outer.inc(); } VecOfRange ret(rvec); return ret; } // }}}2 // }}}1 // {{{1 Array printing functions // // This section coordinates the sending of data to gnuplot. The ArrayTraits mechanism tells us // about nested containers and provides iterators over them. Here we make use of this, // deciding what dimensions should be treated as rows, columns, or blocks, telling gnuplot the // size of the array if needed, and so on. // If this is set, then text-mode data will be sent in a format that is not compatible with // gnuplot, but which helps the programmer tell what the library is thinking. Basically it // puts brackets around groups of items and puts a message delineating blocks of data. static bool debug_array_print = 0; // {{{2 Tags (like enums for metaprogramming) // These tags define what our goal is, what sort of thing should ultimately be sent to the // ostream. These tags are passed to the PrintMode template argument of the functions in this // section. // // ModeText - Sends the data in an array in text format // ModeBinary - Sends the data in an array in binary format // ModeBinfmt - Sends the gnuplot format code for binary data (e.g. "%double%double") // ModeSize - Sends the size of an array. Needed when sending binary data. struct ModeText { static const bool is_text = 1; static const bool is_binfmt = 0; static const bool is_size = 0; }; struct ModeBinary { static const bool is_text = 0; static const bool is_binfmt = 0; static const bool is_size = 0; }; struct ModeBinfmt { static const bool is_text = 0; static const bool is_binfmt = 1; static const bool is_size = 0; }; struct ModeSize { static const bool is_text = 0; static const bool is_binfmt = 0; static const bool is_size = 1; }; // Whether to treat the outermost level of a nested container as columns (column major mode). struct ColUnwrapNo { }; struct ColUnwrapYes { }; // The user must give a hint to describe how nested containers are to be interpreted. This is // done by calling e.g. `send1d_colmajor()` or `send2d()`. This hint is then described by the // following tags. This is passed to the OrganizationMode template argument. struct Mode1D { static std::string class_name() { return "Mode1D" ; } }; struct Mode2D { static std::string class_name() { return "Mode2D" ; } }; struct Mode1DUnwrap { static std::string class_name() { return "Mode1DUnwrap"; } }; struct Mode2DUnwrap { static std::string class_name() { return "Mode2DUnwrap"; } }; // Support for the legacy behavior that guesses which of the above four modes should be used. struct ModeAuto { static std::string class_name() { return "ModeAuto" ; } }; // }}}2 // {{{2 ModeAutoDecoder // // ModeAuto guesses which of Mode1D, Mode2D, Mode1DUnwrap, or Mode2DUnwrap should be used. // This is provided for reverse compatibility; it is better to specify explicitly which mode to // use. Since this is only for reverse compatibility, and shouldn't be used, I'm not going to // spell out what the rules are. See below for details. template struct ModeAutoDecoder { }; template struct ModeAutoDecoder::depth == 1) >::type> { typedef Mode1D mode; }; template struct ModeAutoDecoder::depth == 2) && !ArrayTraits::allow_auto_unwrap >::type> { typedef Mode2D mode; }; template struct ModeAutoDecoder::depth == 2) && ArrayTraits::allow_auto_unwrap >::type> { typedef Mode1DUnwrap mode; }; template struct ModeAutoDecoder::depth > 2) && ArrayTraits::allow_auto_unwrap >::type> { typedef Mode2DUnwrap mode; }; template struct ModeAutoDecoder::depth > 2) && !ArrayTraits::allow_auto_unwrap >::type> { typedef Mode2D mode; }; // }}}2 // The data is processed using several levels of functions that call each other in sequence, // each defined in a subsection of code below. Because C++ wants you to declare a function // before using it, we begin with the innermost function. So in order to see the sequence in // which these are called, you should read the following subsections in reverse order. Nested // arrays are formated into blocks (for 2D data) and lines (for 1D or 2D data), then further // nesting levels are formatted into columns. Also tag dispatching is used in order to define // various sorts of behavior. Each of these tasks is handled by one of the following // subsections. // {{{2 send_scalar() // // Send a scalar in one of three possible ways: via TextSender, BinarySender, or BinfmtSender, // depending on which PrintMode tag is passed to the function. template void send_scalar(std::ostream &stream, const T &arg, ModeText) { TextSender::send(stream, arg); } template void send_scalar(std::ostream &stream, const T &arg, ModeBinary) { BinarySender::send(stream, arg); } template void send_scalar(std::ostream &stream, const T &, ModeBinfmt) { BinfmtSender::send(stream); } // }}}2 // {{{2 deref_and_print() // // Dereferences and prints the given range (iterator). At this point we are done with treating // containers as blocks (for 2D data) and lines (for 1D or 2D data). Any further levels of // nested containers will at this point be treated as columns. // If arg is not a container, then print it via send_scalar(). template typename boost::disable_if_c::type deref_and_print(std::ostream &stream, const T &arg, PrintMode) { const typename T::value_type &v = arg.deref(); send_scalar(stream, v, PrintMode()); } // If arg is a container (but not a PairOfRange or VecOfRange, which are handled below) then // treat the contents as columns, iterating over the contents recursively. If outputting in // text mode, put a space between columns. template typename boost::enable_if_c::type deref_and_print(std::ostream &stream, const T &arg, PrintMode) { if(debug_array_print && PrintMode::is_text) stream << "{"; typename T::subiter_type subrange = arg.deref_subiter(); bool first = true; while(!subrange.is_end()) { if(!first && PrintMode::is_text) stream << " "; first = false; deref_and_print(stream, subrange, PrintMode()); subrange.inc(); } if(debug_array_print && PrintMode::is_text) stream << "}"; } // PairOfRange is treated as columns. In text mode, put a space between columns. template void deref_and_print(std::ostream &stream, const PairOfRange &arg, PrintMode) { deref_and_print(stream, arg.l, PrintMode()); if(PrintMode::is_text) stream << " "; deref_and_print(stream, arg.r, PrintMode()); } // VecOfRange is treated as columns. In text mode, put a space between columns. template void deref_and_print(std::ostream &stream, const VecOfRange &arg, PrintMode) { for(size_t i=0; i typename boost::enable_if_c<(Depth==1) && !PrintMode::is_size>::type print_block(std::ostream &stream, T &arg, PrintMode) { for(; !arg.is_end(); arg.inc()) { //print_entry(arg.deref()); deref_and_print(stream, arg, PrintMode()); // If asked to print the binary format string, only the first element needs to be // looked at. if(PrintMode::is_binfmt) break; if(PrintMode::is_text) stream << std::endl; } } // Depth>1 and we are not asked to print the size of the array. Loop over the range and // recurse into print_block() with Depth -> Depth-1. template typename boost::enable_if_c<(Depth>1) && !PrintMode::is_size>::type print_block(std::ostream &stream, T &arg, PrintMode) { bool first = true; for(; !arg.is_end(); arg.inc()) { if(first) { first = false; } else { if(PrintMode::is_text) stream << std::endl; } if(debug_array_print && PrintMode::is_text) stream << "" << std::endl; typename T::subiter_type sub = arg.deref_subiter(); print_block(stream, sub, PrintMode()); // If asked to print the binary format string, only the first element needs to be // looked at. if(PrintMode::is_binfmt) break; } } // Determine how many elements are in the given range. Used in the functions below. template size_t get_range_size(const T &arg) { // FIXME - not the fastest way. Implement a size() method for range. size_t ret = 0; for(T i=arg; !i.is_end(); i.inc()) ++ret; return ret; } // Depth==1 and we are asked to print the size of the array. template typename boost::enable_if_c<(Depth==1) && PrintMode::is_size>::type print_block(std::ostream &stream, T &arg, PrintMode) { stream << get_range_size(arg); } // Depth>1 and we are asked to print the size of the array. template typename boost::enable_if_c<(Depth>1) && PrintMode::is_size>::type print_block(std::ostream &stream, T &arg, PrintMode) { // It seems that size for two dimensional arrays needs the fastest varying index first, // contrary to intuition. The gnuplot documentation is not too clear on this point. typename T::subiter_type sub = arg.deref_subiter(); print_block(stream, sub, PrintMode()); stream << "," << get_range_size(arg); } // }}}2 // {{{2 handle_colunwrap_tag() // // If passed the ColUnwrapYes then treat the outermost nested container as columns by calling // get_columns_range(). Otherwise just call get_range(). The range iterator is then passed to // print_block() for further processing. template void handle_colunwrap_tag(std::ostream &stream, const T &arg, ColUnwrapNo, PrintMode) { GNUPLOT_STATIC_ASSERT_MSG(ArrayTraits::depth >= Depth, "container not deep enough"); typename ArrayTraits::range_type range = ArrayTraits::get_range(arg); print_block(stream, range, PrintMode()); } template void handle_colunwrap_tag(std::ostream &stream, const T &arg, ColUnwrapYes, PrintMode) { GNUPLOT_STATIC_ASSERT_MSG(ArrayTraits::depth >= Depth+1, "container not deep enough"); VecOfRange::range_type::subiter_type> cols = get_columns_range(arg); print_block(stream, cols, PrintMode()); } // }}}2 // {{{2 handle_organization_tag() // // Parse the OrganizationMode tag then forward to handle_colunwrap_tag() for further // processing. If passed the Mode1D or Mode2D tags, then set Depth=1 or Depth=2. If passed // Mode{1,2}DUnwrap then use the ColUnwrapYes tag. If passed ModeAuto (which is for legacy // support) then use ModeAutoDecoder to guess which of Mode1D, Mode2D, etc. should be used. template void handle_organization_tag(std::ostream &stream, const T &arg, Mode1D, PrintMode) { handle_colunwrap_tag<1>(stream, arg, ColUnwrapNo(), PrintMode()); } template void handle_organization_tag(std::ostream &stream, const T &arg, Mode2D, PrintMode) { handle_colunwrap_tag<2>(stream, arg, ColUnwrapNo(), PrintMode()); } template void handle_organization_tag(std::ostream &stream, const T &arg, Mode1DUnwrap, PrintMode) { handle_colunwrap_tag<1>(stream, arg, ColUnwrapYes(), PrintMode()); } template void handle_organization_tag(std::ostream &stream, const T &arg, Mode2DUnwrap, PrintMode) { handle_colunwrap_tag<2>(stream, arg, ColUnwrapYes(), PrintMode()); } template void handle_organization_tag(std::ostream &stream, const T &arg, ModeAuto, PrintMode) { handle_organization_tag(stream, arg, typename ModeAutoDecoder::mode(), PrintMode()); } // }}}2 // The entry point for the processing defined in this section. It just forwards immediately to // handle_organization_tag(). This function is only here to give a sane name to the entry // point. // // The allowed values for the OrganizationMode and PrintMode tags are defined in the beginning // of this section. template void top_level_array_sender(std::ostream &stream, const T &arg, OrganizationMode, PrintMode) { handle_organization_tag(stream, arg, OrganizationMode(), PrintMode()); } // }}}1 // {{{1 FileHandleWrapper // This holds the file handle that gnuplot commands will be sent to. The purpose of this // wrapper is twofold: // 1. It allows storing the FILE* before it gets passed to the boost::iostreams::stream // constructor (which is a base class of the main Gnuplot class). This is accomplished // via multiple inheritance as described at http://stackoverflow.com/a/3821756/1048959 // 2. It remembers whether the handle needs to be closed via fclose or pclose. struct FileHandleWrapper { FileHandleWrapper(std::FILE *_fh, bool _should_use_pclose) : wrapped_fh(_fh), should_use_pclose(_should_use_pclose) { } void fh_close() { if(should_use_pclose) { if(GNUPLOT_PCLOSE(wrapped_fh)) { std::cerr << "pclose returned error" << std::endl; } } else { if(fclose(wrapped_fh)) { std::cerr << "fclose returned error" << std::endl; } } } int fh_fileno() { return GNUPLOT_FILENO(wrapped_fh); } std::FILE *wrapped_fh; bool should_use_pclose; }; // }}}1 // {{{1 Main class class Gnuplot : // Some setup needs to be done before obtaining the file descriptor that gets passed to // boost::iostreams::stream. This is accomplished by using a multiple inheritance trick, // as described at http://stackoverflow.com/a/3821756/1048959 private FileHandleWrapper, public boost::iostreams::stream { private: static std::string get_default_cmd() { GNUPLOT_MSVC_WARNING_4996_PUSH char *from_env = std::getenv("GNUPLOT_IOSTREAM_CMD"); GNUPLOT_MSVC_WARNING_4996_POP if(from_env && from_env[0]) { return from_env; } else { return GNUPLOT_DEFAULT_COMMAND; } } static FileHandleWrapper open_cmdline(const std::string &in) { std::string cmd = in.empty() ? get_default_cmd() : in; assert(!cmd.empty()); if(cmd[0] == '>') { std::string fn = cmd.substr(1); GNUPLOT_MSVC_WARNING_4996_PUSH FILE *fh = std::fopen(fn.c_str(), "w"); GNUPLOT_MSVC_WARNING_4996_POP if(!fh) throw(std::ios_base::failure("cannot open file "+fn)); return FileHandleWrapper(fh, false); } else { FILE *fh = GNUPLOT_POPEN(cmd.c_str(), "w"); if(!fh) throw(std::ios_base::failure("cannot open pipe "+cmd)); return FileHandleWrapper(fh, true); } } public: explicit Gnuplot(const std::string &_cmd="") : FileHandleWrapper(open_cmdline(_cmd)), boost::iostreams::stream( fh_fileno(), #if BOOST_VERSION >= 104400 boost::iostreams::never_close_handle #else false #endif ), feedback(NULL), tmp_files(), debug_messages(false) { *this << std::scientific << std::setprecision(18); // refer } explicit Gnuplot(FILE *_fh) : FileHandleWrapper(_fh, 0), boost::iostreams::stream( fh_fileno(), #if BOOST_VERSION >= 104400 boost::iostreams::never_close_handle #else false #endif ), feedback(NULL), tmp_files(), debug_messages(false) { *this << std::scientific << std::setprecision(18); // refer } private: // noncopyable Gnuplot(const Gnuplot &); const Gnuplot& operator=(const Gnuplot &); public: ~Gnuplot() { if(debug_messages) { std::cerr << "ending gnuplot session" << std::endl; } // FIXME - boost's close method calls close() on the file descriptor, but we need to // use sometimes use pclose instead. For now, just skip calling boost's close and use // flush just in case. do_flush(); // Wish boost had a pclose method... //close(); fh_close(); delete feedback; } void clearTmpfiles() { // destructors will cause deletion tmp_files.clear(); } private: void do_flush() { *this << std::flush; fflush(wrapped_fh); } std::string make_tmpfile() { #ifdef GNUPLOT_USE_TMPFILE boost::shared_ptr tmp_file(new GnuplotTmpfile()); // The file will be removed once the pointer is removed from the // tmp_files container. tmp_files.push_back(tmp_file); return tmp_file->file.string(); #else throw(std::logic_error("no filename given and temporary files not enabled")); #endif // GNUPLOT_USE_TMPFILE } public: // {{{2 Generic sender routines. // // These are declared public, but are undocumented. It is recommended to use the functions in // the next section, which serve as adapters that pass specific values for the OrganizationMode // tag. template Gnuplot &send(const T &arg, OrganizationMode) { top_level_array_sender(*this, arg, OrganizationMode(), ModeText()); *this << "e" << std::endl; // gnuplot's "end of array" token do_flush(); // probably not really needed, but doesn't hurt return *this; } template Gnuplot &sendBinary(const T &arg, OrganizationMode) { top_level_array_sender(*this, arg, OrganizationMode(), ModeBinary()); do_flush(); // probably not really needed, but doesn't hurt return *this; } template std::string binfmt(const T &arg, const std::string &arr_or_rec, OrganizationMode) { std::ostringstream tmp; tmp << " format='"; top_level_array_sender(tmp, arg, OrganizationMode(), ModeBinfmt()); assert((arr_or_rec == "array") || (arr_or_rec == "record")); tmp << "' " << arr_or_rec << "=("; top_level_array_sender(tmp, arg, OrganizationMode(), ModeSize()); tmp << ")"; tmp << " "; return tmp.str(); } // NOTE: empty filename makes temporary file template std::string file(const T &arg, std::string filename, OrganizationMode) { if(filename.empty()) filename = make_tmpfile(); std::fstream tmp_stream(filename.c_str(), std::fstream::out); top_level_array_sender(tmp_stream, arg, OrganizationMode(), ModeText()); tmp_stream.close(); std::ostringstream cmdline; // FIXME - hopefully filename doesn't contain quotes or such... cmdline << " '" << filename << "' "; return cmdline.str(); } // NOTE: empty filename makes temporary file template std::string binaryFile(const T &arg, std::string filename, const std::string &arr_or_rec, OrganizationMode) { if(filename.empty()) filename = make_tmpfile(); std::fstream tmp_stream(filename.c_str(), std::fstream::out | std::fstream::binary); top_level_array_sender(tmp_stream, arg, OrganizationMode(), ModeBinary()); tmp_stream.close(); std::ostringstream cmdline; // FIXME - hopefully filename doesn't contain quotes or such... cmdline << " '" << filename << "' binary" << binfmt(arg, arr_or_rec, OrganizationMode()); return cmdline.str(); } // }}}2 // {{{2 Deprecated data sending interface that guesses an appropriate OrganizationMode. This is here // for reverse compatibility. Don't use it. A warning will be printed if // GNUPLOT_DEPRECATE_WARN is defined. template Gnuplot GNUPLOT_DEPRECATE("use send1d or send2d") &send(const T &arg) { return send(arg, ModeAuto()); } template std::string GNUPLOT_DEPRECATE("use binfmt1d or binfmt2d") binfmt(const T &arg, const std::string &arr_or_rec="array") { return binfmt(arg, arr_or_rec, ModeAuto()); } template Gnuplot GNUPLOT_DEPRECATE("use sendBinary1d or sendBinary2d") &sendBinary(const T &arg) { return sendBinary(arg, ModeAuto()); } template std::string GNUPLOT_DEPRECATE("use file1d or file2d") file(const T &arg, const std::string &filename="") { return file(arg, filename, ModeAuto()); } template std::string GNUPLOT_DEPRECATE("use binArr1d or binArr2d") binaryFile(const T &arg, const std::string &filename="", const std::string &arr_or_rec="array") { return binaryFile(arg, filename, arr_or_rec, ModeAuto()); } // }}}2 // {{{2 Public (documented) data sending interface. // // It seems odd to define 16 different functions, but I think this ends up being the most // convenient in terms of usage by the end user. template Gnuplot &send1d (const T &arg) { return send(arg, Mode1D ()); } template Gnuplot &send2d (const T &arg) { return send(arg, Mode2D ()); } template Gnuplot &send1d_colmajor(const T &arg) { return send(arg, Mode1DUnwrap()); } template Gnuplot &send2d_colmajor(const T &arg) { return send(arg, Mode2DUnwrap()); } template Gnuplot &sendBinary1d (const T &arg) { return sendBinary(arg, Mode1D ()); } template Gnuplot &sendBinary2d (const T &arg) { return sendBinary(arg, Mode2D ()); } template Gnuplot &sendBinary1d_colmajor(const T &arg) { return sendBinary(arg, Mode1DUnwrap()); } template Gnuplot &sendBinary2d_colmajor(const T &arg) { return sendBinary(arg, Mode2DUnwrap()); } template std::string file1d (const T &arg, const std::string &filename="") { return file(arg, filename, Mode1D ()); } template std::string file2d (const T &arg, const std::string &filename="") { return file(arg, filename, Mode2D ()); } template std::string file1d_colmajor(const T &arg, const std::string &filename="") { return file(arg, filename, Mode1DUnwrap()); } template std::string file2d_colmajor(const T &arg, const std::string &filename="") { return file(arg, filename, Mode2DUnwrap()); } template std::string binFmt1d (const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode1D ()); } template std::string binFmt2d (const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode2D ()); } template std::string binFmt1d_colmajor(const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode1DUnwrap()); } template std::string binFmt2d_colmajor(const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode2DUnwrap()); } template std::string binFile1d (const T &arg, const std::string &arr_or_rec, const std::string &filename="") { return binaryFile(arg, filename, arr_or_rec, Mode1D ()); } template std::string binFile2d (const T &arg, const std::string &arr_or_rec, const std::string &filename="") { return binaryFile(arg, filename, arr_or_rec, Mode2D ()); } template std::string binFile1d_colmajor(const T &arg, const std::string &arr_or_rec, const std::string &filename="") { return binaryFile(arg, filename, arr_or_rec, Mode1DUnwrap()); } template std::string binFile2d_colmajor(const T &arg, const std::string &arr_or_rec, const std::string &filename="") { return binaryFile(arg, filename, arr_or_rec, Mode2DUnwrap()); } // }}}2 #ifdef GNUPLOT_ENABLE_FEEDBACK public: // Input variables are set to the mouse position and button. If the gnuplot // window is closed, button -1 is returned. The msg parameter is the prompt // that is printed to the console. void getMouse( double &mx, double &my, int &mb, std::string msg="Click Mouse!" ) { allocFeedback(); *this << "set mouse" << std::endl; *this << "pause mouse \"" << msg << "\\n\"" << std::endl; *this << "if (exists(\"MOUSE_X\")) print MOUSE_X, MOUSE_Y, MOUSE_BUTTON; else print 0, 0, -1;" << std::endl; if(debug_messages) { std::cerr << "begin scanf" << std::endl; } if(3 != fscanf(feedback->handle(), "%50lf %50lf %50d", &mx, &my, &mb)) { throw std::runtime_error("could not parse reply"); } if(debug_messages) { std::cerr << "end scanf" << std::endl; } } private: void allocFeedback() { if(!feedback) { #ifdef GNUPLOT_ENABLE_PTY feedback = new GnuplotFeedbackPty(debug_messages); //#elif defined GNUPLOT_USE_TMPFILE //// Currently this doesn't work since fscanf doesn't block (need something like "tail -f") // feedback = new GnuplotFeedbackTmpfile(debug_messages); #else // This shouldn't happen because we are in an `#ifdef GNUPLOT_ENABLE_FEEDBACK` // block which should only be activated if GNUPLOT_ENABLE_PTY is defined. GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "No feedback mechanism defined."); #endif *this << "set print \"" << feedback->filename() << "\"" << std::endl; } } #endif // GNUPLOT_ENABLE_FEEDBACK private: GnuplotFeedback *feedback; #ifdef GNUPLOT_USE_TMPFILE std::vector > tmp_files; #else // just a placeholder std::vector tmp_files; #endif // GNUPLOT_USE_TMPFILE public: bool debug_messages; }; // }}}1 } // namespace gnuplotio // The first version of this library didn't use namespaces, and now this must be here forever // for reverse compatibility. using gnuplotio::Gnuplot; #endif // GNUPLOT_IOSTREAM_H // {{{1 Support for 3rd party array libraries // {{{2 Blitz support // This is outside of the main header guard so that it will be compiled when people do // something like this: // #include "gnuplot-iostream.h" // #include // #include "gnuplot-iostream.h" // Note that it has its own header guard to avoid double inclusion. #ifdef BZ_BLITZ_H #ifndef GNUPLOT_BLITZ_SUPPORT_LOADED #define GNUPLOT_BLITZ_SUPPORT_LOADED namespace gnuplotio { template struct BinfmtSender > { static void send(std::ostream &stream) { for(int i=0; i::send(stream); } } }; template struct TextSender > { static void send(std::ostream &stream, const blitz::TinyVector &v) { for(int i=0; i::send(stream, v[i]); } } }; template struct BinarySender > { static void send(std::ostream &stream, const blitz::TinyVector &v) { for(int i=0; i::send(stream, v[i]); } } }; class Error_WasBlitzPartialSlice { }; template class BlitzIterator { public: BlitzIterator() : p(NULL) { } BlitzIterator( const blitz::Array *_p, const blitz::TinyVector _idx ) : p(_p), idx(_idx) { } typedef Error_WasBlitzPartialSlice value_type; typedef BlitzIterator subiter_type; static const bool is_container = true; // FIXME - it would be nice to also handle one-based arrays bool is_end() const { return idx[ArrayDim-SliceDim] == p->shape()[ArrayDim-SliceDim]; } void inc() { ++idx[ArrayDim-SliceDim]; } value_type deref() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "cannot deref a blitz slice"); throw std::logic_error("static assert should have been triggered by this point"); } subiter_type deref_subiter() const { return BlitzIterator(p, idx); } private: const blitz::Array *p; blitz::TinyVector idx; }; template class BlitzIterator { public: BlitzIterator() : p(NULL) { } BlitzIterator( const blitz::Array *_p, const blitz::TinyVector _idx ) : p(_p), idx(_idx) { } typedef T value_type; typedef Error_WasNotContainer subiter_type; static const bool is_container = false; // FIXME - it would be nice to also handle one-based arrays bool is_end() const { return idx[ArrayDim-1] == p->shape()[ArrayDim-1]; } void inc() { ++idx[ArrayDim-1]; } value_type deref() const { return (*p)(idx); } subiter_type deref_subiter() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "argument was not a container"); throw std::logic_error("static assert should have been triggered by this point"); } private: const blitz::Array *p; blitz::TinyVector idx; }; template class ArrayTraits > : public ArrayTraitsDefaults { public: static const bool allow_auto_unwrap = false; static const size_t depth = ArrayTraits::depth + ArrayDim; typedef BlitzIterator range_type; static range_type get_range(const blitz::Array &arg) { blitz::TinyVector start_idx; start_idx = 0; return range_type(&arg, start_idx); } }; } // namespace gnuplotio #endif // GNUPLOT_BLITZ_SUPPORT_LOADED #endif // BZ_BLITZ_H // }}}2 // {{{2 Armadillo support // This is outside of the main header guard so that it will be compiled when people do // something like this: // #include "gnuplot-iostream.h" // #include // #include "gnuplot-iostream.h" // Note that it has its own header guard to avoid double inclusion. #ifdef ARMA_INCLUDES #ifndef GNUPLOT_ARMADILLO_SUPPORT_LOADED #define GNUPLOT_ARMADILLO_SUPPORT_LOADED namespace gnuplotio { template struct dont_treat_as_stl_container > { typedef boost::mpl::bool_ type; }; template struct dont_treat_as_stl_container > { typedef boost::mpl::bool_ type; }; template struct dont_treat_as_stl_container > { typedef boost::mpl::bool_ type; }; template struct dont_treat_as_stl_container > { typedef boost::mpl::bool_ type; }; template struct dont_treat_as_stl_container > { typedef boost::mpl::bool_ type; }; // {{{3 Cube template class ArrayTraits > : public ArrayTraitsDefaults { class SliceRange { public: SliceRange() : p(NULL), col(0), slice(0) { } explicit SliceRange(const arma::Cube *_p, size_t _row, size_t _col) : p(_p), row(_row), col(_col), slice(0) { } typedef T value_type; typedef Error_WasNotContainer subiter_type; static const bool is_container = false; bool is_end() const { return slice == p->n_slices; } void inc() { ++slice; } value_type deref() const { return (*p)(row, col, slice); } subiter_type deref_subiter() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "argument was not a container"); throw std::logic_error("static assert should have been triggered by this point"); } private: const arma::Cube *p; size_t row, col, slice; }; class ColRange { public: ColRange() : p(NULL), row(0), col(0) { } explicit ColRange(const arma::Cube *_p, size_t _row) : p(_p), row(_row), col(0) { } typedef T value_type; typedef SliceRange subiter_type; static const bool is_container = true; bool is_end() const { return col == p->n_cols; } void inc() { ++col; } value_type deref() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "can't call deref on an armadillo cube col"); throw std::logic_error("static assert should have been triggered by this point"); } subiter_type deref_subiter() const { return subiter_type(p, row, col); } private: const arma::Cube *p; size_t row, col; }; class RowRange { public: RowRange() : p(NULL), row(0) { } explicit RowRange(const arma::Cube *_p) : p(_p), row(0) { } typedef T value_type; typedef ColRange subiter_type; static const bool is_container = true; bool is_end() const { return row == p->n_rows; } void inc() { ++row; } value_type deref() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "can't call deref on an armadillo cube row"); throw std::logic_error("static assert should have been triggered by this point"); } subiter_type deref_subiter() const { return subiter_type(p, row); } private: const arma::Cube *p; size_t row; }; public: static const bool allow_auto_unwrap = false; static const size_t depth = ArrayTraits::depth + 3; typedef RowRange range_type; static range_type get_range(const arma::Cube &arg) { //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; return range_type(&arg); } }; // }}}3 // {{{3 Mat and Field template class ArrayTraits_ArmaMatOrField : public ArrayTraitsDefaults { class ColRange { public: ColRange() : p(NULL), row(0), col(0) { } explicit ColRange(const RF *_p, size_t _row) : p(_p), row(_row), col(0) { } typedef T value_type; typedef Error_WasNotContainer subiter_type; static const bool is_container = false; bool is_end() const { return col == p->n_cols; } void inc() { ++col; } value_type deref() const { return (*p)(row, col); } subiter_type deref_subiter() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "argument was not a container"); throw std::logic_error("static assert should have been triggered by this point"); } private: const RF *p; size_t row, col; }; class RowRange { public: RowRange() : p(NULL), row(0) { } explicit RowRange(const RF *_p) : p(_p), row(0) { } typedef T value_type; typedef ColRange subiter_type; static const bool is_container = true; bool is_end() const { return row == p->n_rows; } void inc() { ++row; } value_type deref() const { GNUPLOT_STATIC_ASSERT_MSG((sizeof(T) == 0), "can't call deref on an armadillo matrix row"); throw std::logic_error("static assert should have been triggered by this point"); } subiter_type deref_subiter() const { return subiter_type(p, row); } private: const RF *p; size_t row; }; public: static const bool allow_auto_unwrap = false; static const size_t depth = ArrayTraits::depth + 2; typedef RowRange range_type; static range_type get_range(const RF &arg) { //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; return range_type(&arg); } }; template class ArrayTraits > : public ArrayTraits_ArmaMatOrField, T> { }; template class ArrayTraits > : public ArrayTraits_ArmaMatOrField, T> { }; // }}}3 // {{{3 Row template class ArrayTraits > : public ArrayTraitsDefaults { public: static const bool allow_auto_unwrap = false; typedef IteratorRange::const_iterator, T> range_type; static range_type get_range(const arma::Row &arg) { //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; return range_type(arg.begin(), arg.end()); } }; // }}}3 // {{{3 Col template class ArrayTraits > : public ArrayTraitsDefaults { public: static const bool allow_auto_unwrap = false; typedef IteratorRange::const_iterator, T> range_type; static range_type get_range(const arma::Col &arg) { //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; return range_type(arg.begin(), arg.end()); } }; // }}}3 } // namespace gnuplotio #endif // GNUPLOT_ARMADILLO_SUPPORT_LOADED #endif // ARMA_INCLUDES // }}}2 // }}}1