├── .gitignore ├── include ├── Statistics.h ├── Plotter.h ├── Arrival.h ├── Model.h ├── Simulator.h ├── Parameters.h ├── Simulation.h └── gnuplot-iostream.h ├── Makefile ├── src ├── Arrival.cpp ├── main.cpp ├── Parameters.cpp ├── CMakeLists.txt ├── Simulation.cpp ├── Plotter.cpp ├── Model.cpp ├── Statistic.cpp └── Simulator.cpp ├── CMakeLists.txt ├── LICENSE ├── README.md └── .clang-format /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .cache 3 | build 4 | pyVenv -------------------------------------------------------------------------------- /include/Statistics.h: -------------------------------------------------------------------------------- 1 | // Statistics.h 2 | #ifndef STATISTICS_H 3 | #define STATISTICS_H 4 | 5 | #include 6 | 7 | void calculateAndPrintStatistics(const Simulator &s); 8 | 9 | #endif // STATISTICS_H -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all configure build run plot clean 2 | 3 | all: configure build run 4 | 5 | configure: 6 | cmake -S . -B build 7 | 8 | build: 9 | cmake --build build 10 | 11 | run: 12 | build/bin/MarketMaker 13 | 14 | clean: 15 | rm -rf build -------------------------------------------------------------------------------- /include/Plotter.h: -------------------------------------------------------------------------------- 1 | #ifndef PLOTTER_H 2 | #define PLOTTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace model 9 | { 10 | void gnuHelper(Gnuplot &gp); 11 | void plot(const Simulator &s, const model::parameters &p); 12 | } // namespace model 13 | 14 | #endif // PLOTTER_H -------------------------------------------------------------------------------- /src/Arrival.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | PoissonArrival::PoissonArrival(model::parameters &p) : A(p.A), k(p.k), gen(rd()), d(0, 1) 4 | { 5 | } 6 | 7 | bool PoissonArrival::arrives(double delta, double dt) 8 | { 9 | double lambda = A * exp(-k * delta); 10 | double p = lambda * dt; // standard poisson probability of arrival. 11 | double u = d(gen); // uniform random number between 0 and 1. 12 | 13 | return u < p; 14 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | model::parameters p = model::initializeParameters(); 9 | 10 | // simulates order arrivals. 11 | PoissonArrival arrivals(p); 12 | 13 | Simulator s(p, arrivals, NUMBER_OF_SIMULATIONS); 14 | s.run(); 15 | 16 | calculateAndPrintStatistics(s); 17 | model::plot(s, p); 18 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(MarketMaker) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 8 | 9 | find_package( 10 | Boost 1.76.0 11 | REQUIRED COMPONENTS 12 | iostreams 13 | # filesystem 14 | ) 15 | 16 | add_subdirectory(src) 17 | target_include_directories( 18 | MarketMaker 19 | PUBLIC include) 20 | target_link_libraries( 21 | MarketMaker 22 | PUBLIC Boost::iostreams 23 | # Boost::filesystem 24 | ) -------------------------------------------------------------------------------- /src/Parameters.cpp: -------------------------------------------------------------------------------- 1 | // Parameters.cpp 2 | 3 | #include 4 | 5 | namespace model 6 | { 7 | 8 | parameters initializeParameters() 9 | { 10 | parameters p = {.k = 1.5, 11 | .sigma = 2.0, 12 | .gamma = 0.1, 13 | .A = 140.0, 14 | .T = TERMINAL_TIME, 15 | .Q0 = 0.0, 16 | .S0 = 100.0, 17 | .dt = TERMINAL_TIME / NUMBER_OF_TIME_STEPS, 18 | .N = NUMBER_OF_TIME_STEPS}; 19 | return p; 20 | } 21 | } // namespace model -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES 2 | ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Parameters.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Model.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Arrival.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Simulator.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/Simulation.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/Statistic.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/Plotter.cpp 10 | ) 11 | 12 | add_executable(${PROJECT_NAME} ${SOURCES}) 13 | 14 | set_target_properties(${PROJECT_NAME} PROPERTIES 15 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin 16 | ) -------------------------------------------------------------------------------- /include/Arrival.h: -------------------------------------------------------------------------------- 1 | #ifndef ARRIVAL_H 2 | #define ARRIVAL_H 3 | 4 | #include 5 | #include 6 | 7 | class PoissonArrival 8 | { 9 | public: 10 | PoissonArrival(model::parameters &p); 11 | 12 | bool arrives(double delta, // gap between stock price and bid/ask price. 13 | double dt // time step. 14 | ); 15 | 16 | private: 17 | double k; // intensity of arrival. 18 | double A; // likelihood of arrival. 19 | std::random_device rd; 20 | std::mt19937 gen; 21 | std::uniform_real_distribution d; 22 | }; 23 | 24 | #endif -------------------------------------------------------------------------------- /include/Model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | 4 | // standard library headers 5 | #include 6 | #include 7 | 8 | // model namespace 9 | namespace model::formulas 10 | { 11 | // random number generation 12 | extern std::random_device rd; 13 | extern std::mt19937 gen; 14 | 15 | // Model functionss 16 | double getNewStockMidPrice(const parameters &p, double price, double t); 17 | double getNewReservationPrice(const parameters &p, double price, double quantity, double t); 18 | double getNewSpread(const parameters &p, double t); 19 | double getNewBidPrice(double reservationPrice, double spread); 20 | double getNewAskPrice(double reservationPrice, double spread); 21 | } // namespace model::formulas 22 | 23 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Lost Traveler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Simulation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Simulation::Simulation(const model::parameters &p, PoissonArrival &arrivals) 4 | : p(&p), bids(&arrivals), asks(&arrivals), price(p.S0), quantity(0), wealth(0), reservationPrice(p.S0), spread(0) 5 | { 6 | } 7 | 8 | void Simulation::simulate(double t) 9 | { 10 | price = model::formulas::getNewStockMidPrice(*p, price, t); 11 | 12 | reservationPrice = model::formulas::getNewReservationPrice(*p, price, quantity, t); 13 | spread = model::formulas::getNewSpread(*p, t); 14 | 15 | bid = model::formulas::getNewBidPrice(reservationPrice, spread); 16 | ask = model::formulas::getNewAskPrice(reservationPrice, spread); 17 | 18 | double deltaBid = price - bid; 19 | double deltaAsk = ask - price; 20 | 21 | // for bids to be hit, the price must be <= deltaBid. 22 | if (bids->arrives(deltaBid, p->dt)) 23 | { 24 | // inventory becomes skewed (q > 0) -> market maker enters a short inventory position. 25 | quantity += 1; 26 | wealth -= bid; 27 | } 28 | 29 | // for asks to be hit, the price must be >= deltaAsk. 30 | if (asks->arrives(deltaAsk, p->dt)) 31 | { 32 | // inventory becomes skewed (q < 0) -> market maker enters a long inventory position. 33 | quantity -= 1; 34 | wealth += ask; 35 | } 36 | } -------------------------------------------------------------------------------- /include/Simulator.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMULATOR_H 2 | #define SIMULATOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Simulator 11 | { 12 | public: 13 | Simulator(const model::parameters &p, PoissonArrival &arrivals, double numberOfSimulations); 14 | 15 | void run(); 16 | 17 | // getter methods. 18 | std::vector getBidPrices() const; 19 | std::vector getAskPrices() const; 20 | std::vector getSpreads() const; 21 | std::vector getStockMidPrices() const; 22 | std::vector getReservationPrices() const; 23 | std::vector getQuantities() const; 24 | std::vector getWealths() const; 25 | std::vector getProfits() const; 26 | 27 | private: 28 | void simulate(); 29 | void commit(Simulation &s); 30 | 31 | const model::parameters *p; 32 | PoissonArrival *arrivals; 33 | double numberOfSimulations; 34 | double numberOfSteps; 35 | std::vector bidPrices; 36 | std::vector askPrices; 37 | std::vector spreads; 38 | std::vector stockMidPrice; 39 | std::vector reservationPrices; 40 | std::vector quantities; 41 | std::vector wealths; 42 | std::vector profits; 43 | }; 44 | 45 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Implementation of Avellaneda and Stoikov's High-Frequency Trading Model in a Limit Order Book Context 2 | ## Getting Started 3 | 4 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. 5 | 6 | ### Prerequisites 7 | 8 | Before you begin, ensure you have met the following requirements: 9 | 10 | * You have installed the latest version of CMake. 11 | * You have installed the boost library (minimum required version is 1.76.0) 12 | * You have the gnuplot program installed (with qt terminal support) 13 | * You have a `` machine. State which OS is supported/which is not. 14 | 15 | ### Installing and Running 16 | 17 | To install and run the project, follow these steps: 18 | 19 | 1. Clone the repository: 20 | ``` 21 | git clone 22 | ``` 23 | 24 | 2. Navigate to the project directory: 25 | ``` 26 | cd 27 | ``` 28 | 29 | 3. Install the required Python dependencies: 30 | ``` 31 | pip install -r requirements.txt 32 | ``` 33 | 34 | 4. Build and run the project using the provided Makefile: 35 | ``` 36 | make 37 | ``` 38 | 39 | This will configure and build the project, run the `MarketMaker` executable, and then run `plot.py` to generate the plot. 40 | 41 | 42 | ## License 43 | 44 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 45 | -------------------------------------------------------------------------------- /src/Plotter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace model 10 | { 11 | using PRICES = std::vector; 12 | using TIMESTAMPS = std::vector; 13 | using V_PRICES = std::vector; 14 | 15 | void gnuHelper(Gnuplot &gp) 16 | { 17 | gp << "set terminal qt enhanced font 'SpaceMono Nerd Font Propo,12'\n"; 18 | gp << "set grid\n"; 19 | gp << "set xlabel 'Time'\n"; 20 | gp << "set ylabel 'Price'\n"; 21 | gp << "plot '-' with lines title 'Bid Prices', '-' with lines title 'Ask Prices', '-' with lines title 'Stock Mid " 22 | "Prices'\n"; 23 | } 24 | 25 | void plot(const Simulator &s, const model::parameters &p) 26 | { 27 | // prices to plot. 28 | V_PRICES prices = {s.getBidPrices(), s.getAskPrices(), s.getStockMidPrices()}; 29 | 30 | // time stamps for the x-axis. 31 | TIMESTAMPS times(p.N); 32 | std::iota(times.begin(), times.end(), 0); 33 | std::for_each(times.begin(), times.end(), [&p](double &n) { n /= (p.N - 1); }); 34 | 35 | Gnuplot gp; 36 | gnuHelper(gp); 37 | for (const PRICES &price : prices) 38 | { 39 | // cut off the last N prices. 40 | PRICES lastPrices(price.end() - p.N, price.end()); 41 | gp.send1d(std::make_tuple(times, lastPrices)); 42 | } 43 | 44 | gp << "pause mouse close\n"; 45 | } 46 | } // namespace model -------------------------------------------------------------------------------- /src/Model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace model::formulas 4 | { 5 | 6 | double getNewStockMidPrice(const model::parameters &p, double price, double t) 7 | { 8 | // discretization of the geometric brownian motion. 9 | // S_{t + delta_t} = S_t + delta_S_t = S_t + sigma * dW_t 10 | // dW_t can be approximated by a normal random variable, 11 | // with mean 0 and variance delta_t, as it is normally distributed. 12 | // hence, S_{t + delta_t} = S_t + sigma * N(0, delta_t). 13 | static thread_local std::mt19937 gen(std::random_device{}()); 14 | static thread_local std::normal_distribution d(0, 1); 15 | return price + p.sigma * (d(gen) * sqrt(p.dt)); 16 | } 17 | 18 | double getNewReservationPrice(const model::parameters &p, double price, double quantity, double t) 19 | { 20 | // equation #29 on paper. 21 | return price - quantity * p.gamma * std::pow(p.sigma, 2) * (p.T - t); 22 | } 23 | 24 | double getNewSpread(const model::parameters &p, double t) 25 | { 26 | // equation #30 on paper. 27 | return p.gamma * std::pow(p.sigma, 2) * (p.T - t) + (2 / p.gamma) * log(1 + p.gamma / p.k); 28 | } 29 | 30 | // since symmetric spread -> equation #29 and #30 yields the following bid and ask prices. 31 | double getNewBidPrice(double reservationPrice, double spread) 32 | { 33 | return reservationPrice - spread / 2; 34 | } 35 | 36 | double getNewAskPrice(double reservationPrice, double spread) 37 | { 38 | return reservationPrice + spread / 2; 39 | } 40 | 41 | } // namespace model::formula -------------------------------------------------------------------------------- /include/Parameters.h: -------------------------------------------------------------------------------- 1 | // Parameters.h 2 | #ifndef PARAMETERS_H 3 | #define PARAMETERS_H 4 | 5 | #define TERMINAL_TIME 1.0 6 | #define NUMBER_OF_SIMULATIONS 1000 7 | #define NUMBER_OF_TIME_STEPS 200 8 | 9 | namespace model 10 | { 11 | 12 | struct parameters 13 | { 14 | double k; // intensity 15 | double sigma; // volatility 16 | 17 | double gamma; /* risk aversion 18 | risk aversion here refers to how much the market maker 19 | is willing to expose his/her inventory to risk. */ 20 | 21 | double A; // likelihood of arrival (for bid/ask orders) 22 | double T; // terminal time (end of trading day) 23 | 24 | double Q0; /* initial inventory state 25 | inventory state is ideal (meets the target) when it is zero. 26 | inventory state is long (more stocks than target) when it is positive. 27 | inventory state is short (less stocks than target) when it is negative. 28 | typically, the market maker will issue bid orders above the mid price 29 | (to attract sellers) to replenish his/her inventory when it is short, 30 | and ask orders below the mid price (to attract buyers) to reduce 31 | his/her inventory when it is long. */ 32 | 33 | double S0; // initial stock mid price 34 | double dt; // time step 35 | double N; // number of time steps 36 | }; 37 | 38 | parameters initializeParameters(); 39 | 40 | } // namespace model 41 | 42 | #endif // PARAMETERS_H -------------------------------------------------------------------------------- /src/Statistic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void calculateAndPrintStatistics(const Simulator &s) 7 | { 8 | // fetch the data from the simulator. 9 | std::vector spreads = s.getSpreads(); 10 | std::vector profits = s.getProfits(); 11 | std::vector quantities = s.getQuantities(); 12 | 13 | double averageSpread = std::accumulate(spreads.begin(), spreads.end(), 0.0) / spreads.size(); 14 | std::cout << "Average spread: " << averageSpread << std::endl; 15 | 16 | double averageProfit = std::accumulate(profits.begin(), profits.end(), 0.0) / profits.size(); 17 | std::cout << "Average profit: " << averageProfit << std::endl; 18 | 19 | double meanProfit = averageProfit; // this is the same as average profit. 20 | 21 | double stdDevProfit = 22 | std::sqrt(std::inner_product(profits.begin(), profits.end(), profits.begin(), 0.0) / profits.size() - 23 | meanProfit * meanProfit); 24 | std::cout << "Standard deviation of profit: " << stdDevProfit << std::endl; 25 | 26 | double averageQuantity = std::accumulate(quantities.begin(), quantities.end(), 0.0) / quantities.size(); 27 | std::cout << "Average quantity: " << averageQuantity << std::endl; 28 | 29 | double stdDevQuantity = std::sqrt( 30 | std::inner_product(quantities.begin(), quantities.end(), quantities.begin(), 0.0) / quantities.size() - 31 | averageQuantity * averageQuantity); 32 | std::cout << "Standard deviation of quantity: " << stdDevQuantity << std::endl; 33 | } -------------------------------------------------------------------------------- /include/Simulation.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMULATION_H 2 | #define SIMULATION_H 3 | 4 | #include 5 | #include 6 | 7 | class Simulation 8 | { 9 | public: 10 | Simulation(const model::parameters &p, PoissonArrival &arrivals); 11 | 12 | // simulate method performs one step of the simulation. 13 | void simulate(double t); 14 | 15 | // get methods. 16 | double getBidPrice() const 17 | { 18 | return bid; 19 | } 20 | double getAskPrice() const 21 | { 22 | return ask; 23 | } 24 | double getSpread() const 25 | { 26 | return spread; 27 | } 28 | double getStockMidPrice() const 29 | { 30 | return price; 31 | } 32 | double getReservationPrice() const 33 | { 34 | return reservationPrice; 35 | } 36 | double getQuantity() const 37 | { 38 | return quantity; 39 | } 40 | double getWealth() const 41 | { 42 | return wealth; 43 | } 44 | 45 | private: 46 | double price; // current stock price. 47 | double quantity; // current quantity of stocks. 48 | double bid; // current bid price. 49 | double ask; // current ask price. 50 | double wealth; // current wealth. 51 | double reservationPrice; // current reservation price. 52 | // reservation price is the price at which the market maker 53 | // is indifferent between buying and selling as the change 54 | // in inventory is still within the risk tolerance. 55 | double spread; // current spread. 56 | const model::parameters *p; // parameters of the simulation. 57 | PoissonArrival *bids; // poisson arrival for bids. 58 | PoissonArrival *asks; // poisson arrival for asks. 59 | }; 60 | 61 | #endif -------------------------------------------------------------------------------- /src/Simulator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Simulator::Simulator(const model::parameters &p, PoissonArrival &arrivals, double numberOfSimulations) 4 | : p(&p), arrivals(&arrivals), numberOfSimulations(numberOfSimulations), numberOfSteps(p.N) 5 | { 6 | } 7 | 8 | void Simulator::run() 9 | { 10 | for (int i = 0; i < numberOfSimulations; i++) 11 | { 12 | simulate(); 13 | } 14 | } 15 | 16 | // perform a single simulation. 17 | void Simulator::simulate() 18 | { 19 | Simulation s(*p, *arrivals); 20 | 21 | // simulate the simulation for the number of steps provided. 22 | for (int j = 1; j <= numberOfSteps; j++) 23 | { 24 | s.simulate(j / numberOfSteps); 25 | commit(s); 26 | } 27 | 28 | // calculate profit and loss. 29 | profits.push_back(wealths.back() + stockMidPrice.back() * quantities.back()); 30 | } 31 | 32 | // commit the results of a simulation 33 | void Simulator::commit(Simulation &s) 34 | { 35 | bidPrices.push_back(s.getBidPrice()); 36 | askPrices.push_back(s.getAskPrice()); 37 | spreads.push_back(s.getSpread()); 38 | stockMidPrice.push_back(s.getStockMidPrice()); 39 | reservationPrices.push_back(s.getReservationPrice()); 40 | quantities.push_back(s.getQuantity()); 41 | wealths.push_back(s.getWealth()); 42 | } 43 | 44 | // getter methods. 45 | std::vector Simulator::getBidPrices() const 46 | { 47 | return bidPrices; 48 | } 49 | std::vector Simulator::getAskPrices() const 50 | { 51 | return askPrices; 52 | } 53 | std::vector Simulator::getSpreads() const 54 | { 55 | return spreads; 56 | } 57 | std::vector Simulator::getStockMidPrices() const 58 | { 59 | return stockMidPrice; 60 | } 61 | std::vector Simulator::getReservationPrices() const 62 | { 63 | return reservationPrices; 64 | } 65 | std::vector Simulator::getQuantities() const 66 | { 67 | return quantities; 68 | } 69 | std::vector Simulator::getWealths() const 70 | { 71 | return wealths; 72 | } 73 | std::vector Simulator::getProfits() const 74 | { 75 | return profits; 76 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Microsoft 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignArrayOfStructures: None 7 | AlignConsecutiveAssignments: 8 | Enabled: false 9 | AcrossEmptyLines: false 10 | AcrossComments: false 11 | AlignCompound: false 12 | PadOperators: true 13 | AlignConsecutiveBitFields: 14 | Enabled: false 15 | AcrossEmptyLines: false 16 | AcrossComments: false 17 | AlignCompound: false 18 | PadOperators: false 19 | AlignConsecutiveDeclarations: 20 | Enabled: false 21 | AcrossEmptyLines: false 22 | AcrossComments: false 23 | AlignCompound: false 24 | PadOperators: false 25 | AlignConsecutiveMacros: 26 | Enabled: false 27 | AcrossEmptyLines: false 28 | AcrossComments: false 29 | AlignCompound: false 30 | PadOperators: false 31 | AlignConsecutiveShortCaseStatements: 32 | Enabled: false 33 | AcrossEmptyLines: false 34 | AcrossComments: false 35 | AlignCaseColons: false 36 | AlignEscapedNewlines: Right 37 | AlignOperands: Align 38 | AlignTrailingComments: 39 | Kind: Always 40 | OverEmptyLines: 0 41 | AllowAllArgumentsOnNextLine: true 42 | AllowAllParametersOfDeclarationOnNextLine: true 43 | AllowShortBlocksOnASingleLine: Never 44 | AllowShortCaseLabelsOnASingleLine: false 45 | AllowShortEnumsOnASingleLine: false 46 | AllowShortFunctionsOnASingleLine: None 47 | AllowShortIfStatementsOnASingleLine: Never 48 | AllowShortLambdasOnASingleLine: All 49 | AllowShortLoopsOnASingleLine: false 50 | AlwaysBreakAfterDefinitionReturnType: None 51 | AlwaysBreakAfterReturnType: None 52 | AlwaysBreakBeforeMultilineStrings: false 53 | AlwaysBreakTemplateDeclarations: MultiLine 54 | AttributeMacros: 55 | - __capability 56 | BinPackArguments: true 57 | BinPackParameters: true 58 | BitFieldColonSpacing: Both 59 | BraceWrapping: 60 | AfterCaseLabel: false 61 | AfterClass: true 62 | AfterControlStatement: Always 63 | AfterEnum: true 64 | AfterExternBlock: true 65 | AfterFunction: true 66 | AfterNamespace: true 67 | AfterObjCDeclaration: true 68 | AfterStruct: true 69 | AfterUnion: false 70 | BeforeCatch: true 71 | BeforeElse: true 72 | BeforeLambdaBody: false 73 | BeforeWhile: false 74 | IndentBraces: false 75 | SplitEmptyFunction: true 76 | SplitEmptyRecord: true 77 | SplitEmptyNamespace: true 78 | BreakAfterAttributes: Never 79 | BreakAfterJavaFieldAnnotations: false 80 | BreakArrays: true 81 | BreakBeforeBinaryOperators: None 82 | BreakBeforeConceptDeclarations: Always 83 | BreakBeforeBraces: Custom 84 | BreakBeforeInlineASMColon: OnlyMultiline 85 | BreakBeforeTernaryOperators: true 86 | BreakConstructorInitializers: BeforeColon 87 | BreakInheritanceList: BeforeColon 88 | BreakStringLiterals: true 89 | ColumnLimit: 120 90 | CommentPragmas: '^ IWYU pragma:' 91 | CompactNamespaces: false 92 | ConstructorInitializerIndentWidth: 4 93 | ContinuationIndentWidth: 4 94 | Cpp11BracedListStyle: true 95 | DerivePointerAlignment: false 96 | DisableFormat: false 97 | EmptyLineAfterAccessModifier: Never 98 | EmptyLineBeforeAccessModifier: LogicalBlock 99 | ExperimentalAutoDetectBinPacking: false 100 | FixNamespaceComments: true 101 | ForEachMacros: 102 | - foreach 103 | - Q_FOREACH 104 | - BOOST_FOREACH 105 | IfMacros: 106 | - KJ_IF_MAYBE 107 | IncludeBlocks: Preserve 108 | IncludeCategories: 109 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 110 | Priority: 2 111 | SortPriority: 0 112 | CaseSensitive: false 113 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 114 | Priority: 3 115 | SortPriority: 0 116 | CaseSensitive: false 117 | - Regex: '.*' 118 | Priority: 1 119 | SortPriority: 0 120 | CaseSensitive: false 121 | IncludeIsMainRegex: '(Test)?$' 122 | IncludeIsMainSourceRegex: '' 123 | IndentAccessModifiers: false 124 | IndentCaseBlocks: false 125 | IndentCaseLabels: false 126 | IndentExternBlock: AfterExternBlock 127 | IndentGotoLabels: true 128 | IndentPPDirectives: None 129 | IndentRequiresClause: true 130 | IndentWidth: 4 131 | IndentWrappedFunctionNames: false 132 | InsertBraces: false 133 | InsertNewlineAtEOF: false 134 | InsertTrailingCommas: None 135 | IntegerLiteralSeparator: 136 | Binary: 0 137 | BinaryMinDigits: 0 138 | Decimal: 0 139 | DecimalMinDigits: 0 140 | Hex: 0 141 | HexMinDigits: 0 142 | JavaScriptQuotes: Leave 143 | JavaScriptWrapImports: true 144 | KeepEmptyLinesAtTheStartOfBlocks: true 145 | KeepEmptyLinesAtEOF: false 146 | LambdaBodyIndentation: Signature 147 | LineEnding: DeriveLF 148 | MacroBlockBegin: '' 149 | MacroBlockEnd: '' 150 | MaxEmptyLinesToKeep: 1 151 | NamespaceIndentation: None 152 | ObjCBinPackProtocolList: Auto 153 | ObjCBlockIndentWidth: 2 154 | ObjCBreakBeforeNestedBlockParam: true 155 | ObjCSpaceAfterProperty: false 156 | ObjCSpaceBeforeProtocolList: true 157 | PackConstructorInitializers: BinPack 158 | PenaltyBreakAssignment: 2 159 | PenaltyBreakBeforeFirstCallParameter: 19 160 | PenaltyBreakComment: 300 161 | PenaltyBreakFirstLessLess: 120 162 | PenaltyBreakOpenParenthesis: 0 163 | PenaltyBreakString: 1000 164 | PenaltyBreakTemplateDeclaration: 10 165 | PenaltyExcessCharacter: 1000000 166 | PenaltyIndentedWhitespace: 0 167 | PenaltyReturnTypeOnItsOwnLine: 1000 168 | PointerAlignment: Right 169 | PPIndentWidth: -1 170 | QualifierAlignment: Leave 171 | ReferenceAlignment: Pointer 172 | ReflowComments: true 173 | RemoveBracesLLVM: false 174 | RemoveParentheses: Leave 175 | RemoveSemicolon: false 176 | RequiresClausePosition: OwnLine 177 | RequiresExpressionIndentation: OuterScope 178 | SeparateDefinitionBlocks: Leave 179 | ShortNamespaceLines: 1 180 | SortIncludes: CaseSensitive 181 | SortJavaStaticImport: Before 182 | SortUsingDeclarations: LexicographicNumeric 183 | SpaceAfterCStyleCast: false 184 | SpaceAfterLogicalNot: false 185 | SpaceAfterTemplateKeyword: true 186 | SpaceAroundPointerQualifiers: Default 187 | SpaceBeforeAssignmentOperators: true 188 | SpaceBeforeCaseColon: false 189 | SpaceBeforeCpp11BracedList: false 190 | SpaceBeforeCtorInitializerColon: true 191 | SpaceBeforeInheritanceColon: true 192 | SpaceBeforeJsonColon: false 193 | SpaceBeforeParens: ControlStatements 194 | SpaceBeforeParensOptions: 195 | AfterControlStatements: true 196 | AfterForeachMacros: true 197 | AfterFunctionDefinitionName: false 198 | AfterFunctionDeclarationName: false 199 | AfterIfMacros: true 200 | AfterOverloadedOperator: false 201 | AfterRequiresInClause: false 202 | AfterRequiresInExpression: false 203 | BeforeNonEmptyParentheses: false 204 | SpaceBeforeRangeBasedForLoopColon: true 205 | SpaceBeforeSquareBrackets: false 206 | SpaceInEmptyBlock: false 207 | SpacesBeforeTrailingComments: 1 208 | SpacesInAngles: Never 209 | SpacesInContainerLiterals: true 210 | SpacesInLineCommentPrefix: 211 | Minimum: 1 212 | Maximum: -1 213 | SpacesInParens: Never 214 | SpacesInParensOptions: 215 | InCStyleCasts: false 216 | InConditionalStatements: false 217 | InEmptyParentheses: false 218 | Other: false 219 | SpacesInSquareBrackets: false 220 | Standard: Latest 221 | StatementAttributeLikeMacros: 222 | - Q_EMIT 223 | StatementMacros: 224 | - Q_UNUSED 225 | - QT_REQUIRE_VERSION 226 | TabWidth: 4 227 | UseTab: Never 228 | VerilogBreakBetweenInstancePorts: true 229 | WhitespaceSensitiveMacros: 230 | - BOOST_PP_STRINGIZE 231 | - CF_SWIFT_NAME 232 | - NS_SWIFT_NAME 233 | - PP_STRINGIZE 234 | - STRINGIZE 235 | ... 236 | 237 | -------------------------------------------------------------------------------- /include/gnuplot-iostream.h: -------------------------------------------------------------------------------- 1 | // vim:foldmethod=marker 2 | 3 | /* 4 | Copyright (c) 2020 Daniel Stahlke (dan@stahlke.org) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | /* A C++ interface to gnuplot. 26 | * Web page: http://www.stahlke.org/dan/gnuplot-iostream 27 | * Documentation: https://github.com/dstahlke/gnuplot-iostream/wiki 28 | * 29 | * The whole library consists of this monolithic header file, for ease of installation (the 30 | * Makefile and *.cc files are only for examples and tests). 31 | * 32 | * TODO: 33 | * Callbacks via gnuplot's 'bind' function. This would allow triggering user functions when 34 | * keys are pressed in the gnuplot window. However, it would require a PTY reader thread. 35 | * Maybe temporary files read in a thread can replace PTY stuff. 36 | */ 37 | 38 | #ifndef GNUPLOT_IOSTREAM_H 39 | #define GNUPLOT_IOSTREAM_H 40 | 41 | // {{{1 Includes and defines 42 | 43 | #define GNUPLOT_IOSTREAM_VERSION 3 44 | 45 | // C system includes 46 | #include 47 | #ifdef GNUPLOT_ENABLE_PTY 48 | # include 49 | # include 50 | #ifdef __APPLE__ 51 | # include 52 | #else 53 | # include 54 | #endif 55 | #endif // GNUPLOT_ENABLE_PTY 56 | 57 | // C++ system includes 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | // This is the version of boost which has v3 of the filesystem libraries by default. 79 | #if BOOST_VERSION >= 104600 80 | # define GNUPLOT_USE_TMPFILE 81 | # include 82 | #endif // BOOST_VERSION 83 | 84 | // Note: this is here for reverse compatibility. The new way to enable blitz support is to 85 | // just include the gnuplot-iostream.h header after you include the blitz header (likewise for 86 | // armadillo). 87 | #ifdef GNUPLOT_ENABLE_BLITZ 88 | # include 89 | #endif 90 | 91 | // If this is defined, warn about use of deprecated functions. 92 | #ifdef GNUPLOT_DEPRECATE_WARN 93 | # ifdef __GNUC__ 94 | # define GNUPLOT_DEPRECATE(msg) __attribute__ ((deprecated(msg))) 95 | # elif defined(_MSC_VER) 96 | # define GNUPLOT_DEPRECATE(msg) __declspec(deprecated(msg)) 97 | # else 98 | # define GNUPLOT_DEPRECATE(msg) 99 | # endif 100 | #else 101 | # define GNUPLOT_DEPRECATE(msg) 102 | #endif 103 | 104 | // Patch for Windows by Damien Loison 105 | #ifdef _WIN32 106 | # define GNUPLOT_PCLOSE _pclose 107 | # define GNUPLOT_POPEN _popen 108 | # define GNUPLOT_FILENO _fileno 109 | #else 110 | # define GNUPLOT_PCLOSE pclose 111 | # define GNUPLOT_POPEN popen 112 | # define GNUPLOT_FILENO fileno 113 | #endif 114 | 115 | // MSVC gives a warning saying that fopen and getenv are not secure. But they are secure. 116 | // Unfortunately their replacement functions are not simple drop-in replacements. The best 117 | // solution is to just temporarily disable this warning whenever fopen or getenv is used. 118 | // http://stackoverflow.com/a/4805353/1048959 119 | #if defined(_MSC_VER) && _MSC_VER >= 1400 120 | # define GNUPLOT_MSVC_WARNING_4996_PUSH \ 121 | __pragma(warning(push)) \ 122 | __pragma(warning(disable:4996)) 123 | # define GNUPLOT_MSVC_WARNING_4996_POP \ 124 | __pragma(warning(pop)) 125 | #else 126 | # define GNUPLOT_MSVC_WARNING_4996_PUSH 127 | # define GNUPLOT_MSVC_WARNING_4996_POP 128 | #endif 129 | 130 | #ifndef GNUPLOT_DEFAULT_COMMAND 131 | #ifdef _WIN32 132 | // "pgnuplot" is considered deprecated according to the Internet. It may be faster. It 133 | // doesn't seem to handle binary data though. 134 | //# define GNUPLOT_DEFAULT_COMMAND "pgnuplot -persist" 135 | // The default install path for gnuplot is written here. This way the user doesn't have to add 136 | // anything to their %PATH% environment variable. 137 | # define GNUPLOT_DEFAULT_COMMAND "\"C:\\Program Files\\gnuplot\\bin\\gnuplot.exe\" -persist" 138 | #else 139 | # define GNUPLOT_DEFAULT_COMMAND "gnuplot -persist" 140 | #endif 141 | #endif 142 | 143 | // }}}1 144 | 145 | namespace gnuplotio { 146 | 147 | // {{{1 Basic traits helpers 148 | // 149 | // The mechanisms constructed in this section enable us to detect what sort of datatype has 150 | // been passed to a function. 151 | 152 | // This can be specialized as needed, in order to not use the STL interfaces for specific 153 | // classes. 154 | template 155 | static constexpr bool dont_treat_as_stl_container = false; 156 | 157 | 158 | template 159 | static constexpr bool is_like_stl_container = false; 160 | 161 | template 162 | static constexpr bool is_like_stl_container().begin()), 164 | decltype(std::declval().end()), 165 | typename T::value_type 166 | >> = !dont_treat_as_stl_container; 167 | 168 | static_assert( is_like_stl_container>); 169 | static_assert(!is_like_stl_container); 170 | 171 | 172 | template 173 | static constexpr bool is_like_stl_container2 = false; 174 | 175 | template 176 | static constexpr bool is_like_stl_container2())), 178 | decltype(end (std::declval())) 179 | >> = !is_like_stl_container && !dont_treat_as_stl_container; 180 | 181 | 182 | template 183 | static constexpr bool is_boost_tuple_nulltype = 184 | std::is_same_v; 185 | 186 | static_assert(is_boost_tuple_nulltype); 187 | 188 | template 189 | static constexpr bool is_boost_tuple = false; 190 | 191 | template 192 | static constexpr bool is_boost_tuple> = is_boost_tuple || is_boost_tuple_nulltype; 196 | 197 | static_assert( is_boost_tuple>); 198 | static_assert( is_boost_tuple>); 199 | static_assert(!is_boost_tuple>); 200 | static_assert(!is_boost_tuple>); 201 | 202 | // }}}1 203 | 204 | // {{{1 Tmpfile helper class 205 | #ifdef GNUPLOT_USE_TMPFILE 206 | // RAII temporary file. File is removed when this object goes out of scope. 207 | class GnuplotTmpfile { 208 | public: 209 | explicit GnuplotTmpfile(bool _debug_messages) : 210 | file(boost::filesystem::unique_path( 211 | boost::filesystem::temp_directory_path() / 212 | "tmp-gnuplot-%%%%-%%%%-%%%%-%%%%")), 213 | debug_messages(_debug_messages) 214 | { 215 | if(debug_messages) { 216 | std::cerr << "create tmpfile " << file << std::endl; 217 | } 218 | } 219 | 220 | private: 221 | // noncopyable 222 | GnuplotTmpfile(const GnuplotTmpfile &); 223 | const GnuplotTmpfile& operator=(const GnuplotTmpfile &); 224 | 225 | public: 226 | ~GnuplotTmpfile() { 227 | if(debug_messages) { 228 | std::cerr << "delete tmpfile " << file << std::endl; 229 | } 230 | // it is never good to throw exceptions from a destructor 231 | try { 232 | remove(file); 233 | } catch(const std::exception &) { 234 | std::cerr << "Failed to remove temporary file " << file << std::endl; 235 | } 236 | } 237 | 238 | public: 239 | boost::filesystem::path file; 240 | bool debug_messages; 241 | }; 242 | 243 | class GnuplotTmpfileCollection { 244 | public: 245 | std::string make_tmpfile() { 246 | const bool debug_messages = false; 247 | std::shared_ptr tmp_file(new GnuplotTmpfile(debug_messages)); 248 | // The file will be removed once the pointer is removed from the 249 | // tmp_files container. 250 | tmp_files.push_back(tmp_file); 251 | return tmp_file->file.string(); 252 | } 253 | 254 | void clear() { 255 | tmp_files.clear(); 256 | } 257 | 258 | private: 259 | std::vector> tmp_files; 260 | }; 261 | #else // GNUPLOT_USE_TMPFILE 262 | class GnuplotTmpfileCollection { 263 | public: 264 | std::string make_tmpfile() { 265 | throw std::logic_error("no filename given and temporary files not enabled"); 266 | } 267 | 268 | void clear() { } 269 | }; 270 | #endif // GNUPLOT_USE_TMPFILE 271 | // }}}1 272 | 273 | // {{{1 Feedback helper classes 274 | // 275 | // Used for reading stuff sent from gnuplot via gnuplot's "print" function. 276 | // 277 | // For example, this is used for capturing mouse clicks in the gnuplot window. There are two 278 | // implementations, only the first of which is complete. The first implementation allocates a 279 | // PTY (pseudo terminal) which is written to by gnuplot and read by us. This only works in 280 | // Linux. The second implementation creates a temporary file which is written to by gnuplot 281 | // and read by us. However, this doesn't currently work since fscanf doesn't block. It would 282 | // be possible to get this working using a more complicated mechanism (select or threads) but I 283 | // haven't had the need for it. 284 | 285 | class GnuplotFeedback { 286 | public: 287 | GnuplotFeedback() { } 288 | virtual ~GnuplotFeedback() { } 289 | virtual std::string filename() const = 0; 290 | virtual FILE *handle() const = 0; 291 | 292 | private: 293 | // noncopyable 294 | GnuplotFeedback(const GnuplotFeedback &); 295 | const GnuplotFeedback& operator=(const GnuplotFeedback &); 296 | }; 297 | 298 | #ifdef GNUPLOT_ENABLE_PTY 299 | #define GNUPLOT_ENABLE_FEEDBACK 300 | class GnuplotFeedbackPty : public GnuplotFeedback { 301 | public: 302 | explicit GnuplotFeedbackPty(bool debug_messages) : 303 | pty_fn(), 304 | pty_fh(nullptr), 305 | master_fd(-1), 306 | slave_fd(-1) 307 | { 308 | // adapted from http://www.gnuplot.info/files/gpReadMouseTest.c 309 | if(0 > openpty(&master_fd, &slave_fd, nullptr, nullptr, nullptr)) { 310 | perror("openpty"); 311 | throw std::runtime_error("openpty failed"); 312 | } 313 | char pty_fn_buf[1024]; 314 | if(ttyname_r(slave_fd, pty_fn_buf, 1024)) { 315 | perror("ttyname_r"); 316 | throw std::runtime_error("ttyname failed"); 317 | } 318 | pty_fn = std::string(pty_fn_buf); 319 | if(debug_messages) { 320 | std::cerr << "feedback_fn=" << pty_fn << std::endl; 321 | } 322 | 323 | // disable echo 324 | struct termios tios; 325 | if(tcgetattr(slave_fd, &tios) < 0) { 326 | perror("tcgetattr"); 327 | throw std::runtime_error("tcgetattr failed"); 328 | } 329 | tios.c_lflag &= ~(ECHO | ECHONL); 330 | if(tcsetattr(slave_fd, TCSAFLUSH, &tios) < 0) { 331 | perror("tcsetattr"); 332 | throw std::runtime_error("tcsetattr failed"); 333 | } 334 | 335 | pty_fh = fdopen(master_fd, "r"); 336 | if(!pty_fh) { 337 | throw std::runtime_error("fdopen failed"); 338 | } 339 | } 340 | 341 | private: 342 | // noncopyable 343 | GnuplotFeedbackPty(const GnuplotFeedbackPty &); 344 | const GnuplotFeedbackPty& operator=(const GnuplotFeedbackPty &); 345 | 346 | public: 347 | ~GnuplotFeedbackPty() { 348 | if(pty_fh) fclose(pty_fh); 349 | if(master_fd > 0) ::close(master_fd); 350 | if(slave_fd > 0) ::close(slave_fd); 351 | } 352 | 353 | std::string filename() const { 354 | return pty_fn; 355 | } 356 | 357 | FILE *handle() const { 358 | return pty_fh; 359 | } 360 | 361 | private: 362 | std::string pty_fn; 363 | FILE *pty_fh; 364 | int master_fd, slave_fd; 365 | }; 366 | //#elif defined GNUPLOT_USE_TMPFILE 367 | //// Currently this doesn't work since fscanf doesn't block (need something like "tail -f") 368 | //#define GNUPLOT_ENABLE_FEEDBACK 369 | //class GnuplotFeedbackTmpfile : public GnuplotFeedback { 370 | //public: 371 | // explicit GnuplotFeedbackTmpfile(bool debug_messages) : 372 | // tmp_file(), 373 | // fh(nullptr) 374 | // { 375 | // if(debug_messages) { 376 | // std::cerr << "feedback_fn=" << filename() << std::endl; 377 | // } 378 | // GNUPLOT_MSVC_WARNING_4996_PUSH 379 | // fh = std::fopen(filename().c_str(), "a"); 380 | // GNUPLOT_MSVC_WARNING_4996_POP 381 | // } 382 | // 383 | // ~GnuplotFeedbackTmpfile() { 384 | // fclose(fh); 385 | // } 386 | // 387 | //private: 388 | // // noncopyable 389 | // GnuplotFeedbackTmpfile(const GnuplotFeedbackTmpfile &); 390 | // const GnuplotFeedbackTmpfile& operator=(const GnuplotFeedbackTmpfile &); 391 | // 392 | //public: 393 | // std::string filename() const { 394 | // return tmp_file.file.string(); 395 | // } 396 | // 397 | // FILE *handle() const { 398 | // return fh; 399 | // } 400 | // 401 | //private: 402 | // GnuplotTmpfile tmp_file; 403 | // FILE *fh; 404 | //}; 405 | #endif // GNUPLOT_ENABLE_PTY, GNUPLOT_USE_TMPFILE 406 | // }}}1 407 | 408 | // {{{1 Traits and printers for entry datatypes 409 | // 410 | // This section contains the mechanisms for sending scalar and tuple data to gnuplot. Pairs 411 | // and tuples are sent by appealing to the senders defined for their component scalar types. 412 | // Senders for arrays are defined in a later section. 413 | // 414 | // There are three classes which need to be specialized for each supported datatype: 415 | // 1. TextSender to send data as text. The default is to just send using the ostream's `<<` 416 | // operator. 417 | // 2. BinarySender to send data as binary, in a format which gnuplot can understand. There is 418 | // no default implementation (unimplemented types raise a compile time error), however 419 | // inheriting from FlatBinarySender will send the data literally as it is stored in memory. 420 | // This suffices for most of the standard built-in types (e.g. uint32_t or double). 421 | // 3. BinfmtSender sends a description of the data format to gnuplot (e.g. `%uint32`). Type 422 | // `show datafile binary datasizes` in gnuplot to see a list of supported formats. 423 | 424 | // {{{2 Basic entry datatypes 425 | 426 | // Default TextSender, sends data using `<<` operator. 427 | template 428 | struct TextSender { 429 | static void send(std::ostream &stream, const T &v) { 430 | stream << v; 431 | } 432 | }; 433 | 434 | class BinarySenderNotImplemented : public std::logic_error { 435 | public: 436 | explicit BinarySenderNotImplemented(const std::string &w) : std::logic_error(w) { } 437 | }; 438 | 439 | // Default BinarySender, raises a compile time error. 440 | template 441 | struct BinarySender { 442 | static void send(std::ostream &, const T &) { 443 | throw BinarySenderNotImplemented("BinarySender not implemented for this type"); 444 | } 445 | }; 446 | 447 | // This is a BinarySender implementation that just sends directly from memory. Data types 448 | // which can be sent this way can have their BinarySender specialization inherit from this. 449 | template 450 | struct FlatBinarySender { 451 | static void send(std::ostream &stream, const T &v) { 452 | stream.write(reinterpret_cast(&v), sizeof(T)); 453 | } 454 | }; 455 | 456 | // Default BinfmtSender, raises a compile time error. 457 | template 458 | struct BinfmtSender { 459 | static void send(std::ostream &) { 460 | throw BinarySenderNotImplemented("BinfmtSender not implemented for this type"); 461 | } 462 | }; 463 | 464 | // BinfmtSender implementations for basic data types supported by gnuplot. 465 | template<> struct BinfmtSender< float> { static void send(std::ostream &stream) { stream << "%float"; } }; 466 | template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%double"; } }; 467 | template<> struct BinfmtSender< int8_t> { static void send(std::ostream &stream) { stream << "%int8"; } }; 468 | template<> struct BinfmtSender< uint8_t> { static void send(std::ostream &stream) { stream << "%uint8"; } }; 469 | template<> struct BinfmtSender< int16_t> { static void send(std::ostream &stream) { stream << "%int16"; } }; 470 | template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint16"; } }; 471 | template<> struct BinfmtSender< int32_t> { static void send(std::ostream &stream) { stream << "%int32"; } }; 472 | template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint32"; } }; 473 | template<> struct BinfmtSender< int64_t> { static void send(std::ostream &stream) { stream << "%int64"; } }; 474 | template<> struct BinfmtSender { static void send(std::ostream &stream) { stream << "%uint64"; } }; 475 | 476 | // BinarySender implementations for basic data types supported by gnuplot. These types can 477 | // just be sent as stored in memory, so all these senders inherit from FlatBinarySender. 478 | template<> struct BinarySender< float> : public FlatBinarySender< float> { }; 479 | template<> struct BinarySender : public FlatBinarySender { }; 480 | template<> struct BinarySender< int8_t> : public FlatBinarySender< int8_t> { }; 481 | template<> struct BinarySender< uint8_t> : public FlatBinarySender< uint8_t> { }; 482 | template<> struct BinarySender< int16_t> : public FlatBinarySender< int16_t> { }; 483 | template<> struct BinarySender : public FlatBinarySender { }; 484 | template<> struct BinarySender< int32_t> : public FlatBinarySender< int32_t> { }; 485 | template<> struct BinarySender : public FlatBinarySender { }; 486 | template<> struct BinarySender< int64_t> : public FlatBinarySender< int64_t> { }; 487 | template<> struct BinarySender : public FlatBinarySender { }; 488 | 489 | // Make char types print as integers, not as characters. 490 | template 491 | struct CastIntTextSender { 492 | static void send(std::ostream &stream, const T &v) { 493 | stream << static_cast(v); 494 | } 495 | }; 496 | template<> struct TextSender< char> : public CastIntTextSender< char> { }; 497 | template<> struct TextSender< signed char> : public CastIntTextSender< signed char> { }; 498 | template<> struct TextSender< unsigned char> : public CastIntTextSender< unsigned char> { }; 499 | 500 | // Make sure that the same not-a-number string is printed on all platforms. 501 | template 502 | struct FloatTextSender { 503 | static void send(std::ostream &stream, const T &v) { 504 | if(std::isnan(v)) { stream << "nan"; } else { stream << v; } 505 | } 506 | }; 507 | template<> struct TextSender< float> : FloatTextSender< float> { }; 508 | template<> struct TextSender< double> : FloatTextSender< double> { }; 509 | template<> struct TextSender : FloatTextSender { }; 510 | 511 | // }}}2 512 | 513 | // {{{2 std::pair support 514 | 515 | template 516 | struct TextSender> { 517 | static void send(std::ostream &stream, const std::pair &v) { 518 | TextSender::send(stream, v.first); 519 | stream << " "; 520 | TextSender::send(stream, v.second); 521 | } 522 | }; 523 | 524 | template 525 | struct BinfmtSender> { 526 | static void send(std::ostream &stream) { 527 | BinfmtSender::send(stream); 528 | BinfmtSender::send(stream); 529 | } 530 | }; 531 | 532 | template 533 | struct BinarySender> { 534 | static void send(std::ostream &stream, const std::pair &v) { 535 | BinarySender::send(stream, v.first); 536 | BinarySender::send(stream, v.second); 537 | } 538 | }; 539 | 540 | // }}}2 541 | 542 | // {{{2 std::complex support 543 | 544 | template 545 | struct TextSender> { 546 | static void send(std::ostream &stream, const std::complex &v) { 547 | TextSender::send(stream, v.real()); 548 | stream << " "; 549 | TextSender::send(stream, v.imag()); 550 | } 551 | }; 552 | 553 | template 554 | struct BinfmtSender> { 555 | static void send(std::ostream &stream) { 556 | BinfmtSender::send(stream); 557 | BinfmtSender::send(stream); 558 | } 559 | }; 560 | 561 | template 562 | struct BinarySender> { 563 | static void send(std::ostream &stream, const std::complex &v) { 564 | BinarySender::send(stream, v.real()); 565 | BinarySender::send(stream, v.imag()); 566 | } 567 | }; 568 | 569 | // }}}2 570 | 571 | // {{{2 boost::tuple support 572 | 573 | template 574 | struct TextSender> 576 | > { 577 | static void send(std::ostream &stream, const T &v) { 578 | TextSender::send(stream, v.get_head()); 579 | if constexpr (!is_boost_tuple_nulltype) { 580 | stream << " "; 581 | TextSender::send(stream, v.get_tail()); 582 | } 583 | } 584 | }; 585 | 586 | template 587 | struct BinfmtSender> 589 | > { 590 | static void send(std::ostream &stream) { 591 | BinfmtSender::send(stream); 592 | if constexpr (!is_boost_tuple_nulltype) { 593 | stream << " "; 594 | BinfmtSender::send(stream); 595 | } 596 | } 597 | }; 598 | 599 | template 600 | struct BinarySender> 602 | > { 603 | static void send(std::ostream &stream, const T &v) { 604 | BinarySender::send(stream, v.get_head()); 605 | if constexpr (!is_boost_tuple_nulltype) { 606 | BinarySender::send(stream, v.get_tail()); 607 | } 608 | } 609 | }; 610 | 611 | // }}}2 612 | 613 | // {{{2 std::tuple support 614 | 615 | // http://stackoverflow.com/questions/6245735/pretty-print-stdtuple 616 | 617 | template struct int_{}; // compile-time counter 618 | 619 | template 620 | void std_tuple_formatcode_helper(std::ostream &stream, const Tuple *, int_) { 621 | std_tuple_formatcode_helper(stream, (const Tuple *)(0), int_()); 622 | stream << " "; 623 | BinfmtSender::type>::send(stream); 624 | } 625 | 626 | template 627 | void std_tuple_formatcode_helper(std::ostream &stream, const Tuple *, int_<0>) { 628 | BinfmtSender::type>::send(stream); 629 | } 630 | 631 | template 632 | struct BinfmtSender> { 633 | typedef typename std::tuple Tuple; 634 | 635 | static void send(std::ostream &stream) { 636 | std_tuple_formatcode_helper(stream, (const Tuple *)(0), int_()); 637 | } 638 | }; 639 | 640 | template 641 | void std_tuple_textsend_helper(std::ostream &stream, const Tuple &v, int_) { 642 | std_tuple_textsend_helper(stream, v, int_()); 643 | stream << " "; 644 | TextSender::type>::send(stream, std::get(v)); 645 | } 646 | 647 | template 648 | void std_tuple_textsend_helper(std::ostream &stream, const Tuple &v, int_<0>) { 649 | TextSender::type>::send(stream, std::get<0>(v)); 650 | } 651 | 652 | template 653 | struct TextSender> { 654 | typedef typename std::tuple Tuple; 655 | 656 | static void send(std::ostream &stream, const Tuple &v) { 657 | std_tuple_textsend_helper(stream, v, int_()); 658 | } 659 | }; 660 | 661 | template 662 | void std_tuple_binsend_helper(std::ostream &stream, const Tuple &v, int_) { 663 | std_tuple_binsend_helper(stream, v, int_()); 664 | BinarySender::type>::send(stream, std::get(v)); 665 | } 666 | 667 | template 668 | void std_tuple_binsend_helper(std::ostream &stream, const Tuple &v, int_<0>) { 669 | BinarySender::type>::send(stream, std::get<0>(v)); 670 | } 671 | 672 | template 673 | struct BinarySender> { 674 | typedef typename std::tuple Tuple; 675 | 676 | static void send(std::ostream &stream, const Tuple &v) { 677 | std_tuple_binsend_helper(stream, v, int_()); 678 | } 679 | }; 680 | 681 | // }}}2 682 | 683 | // }}}1 684 | 685 | // {{{1 ArrayTraits and Range classes 686 | // 687 | // This section handles sending of array data to gnuplot. It is rather complicated because of 688 | // the diversity of storage schemes supported. For example, it treats a 689 | // `std::pair, std::vector>` in the same way as a 690 | // `std::vector>`, iterating through the two arrays in lockstep, and sending 691 | // pairs to gnuplot as columns. In fact, any nested combination of pairs, tuples, STL 692 | // containers, Blitz arrays, and Armadillo arrays is supported (with the caveat that, for 693 | // instance, Blitz arrays should never be put into an STL container or you will suffer 694 | // unpredictable results due the way Blitz handles assignment). Nested containers are 695 | // considered to be multidimensional arrays. Although gnuplot only supports 1D and 2D arrays, 696 | // our module is in principle not limited. 697 | // 698 | // The ArrayTraits class is specialized for every supported array or container type (the 699 | // default, unspecialized, version of ArrayTraits exists only to tell you that something is 700 | // *not* a container, via the is_container flag). ArrayTraits tells you the depth of a nested 701 | // (or multidimensional) container, as well as the value type, and provides a specialized 702 | // sort of iterator (a.k.a. "range"). Ranges are sort of like STL iterators, except that they 703 | // have built-in knowledge of the end condition so you don't have to carry around both a 704 | // begin() and an end() iterator like in STL. 705 | // 706 | // As an example of how this works, consider a std::pair of std::vectors. Ultimately this gets 707 | // sent to gnuplot as two columns, so the two vectors need to be iterated in lockstep. 708 | // The `value_type` of `std::pair, std::vector>` is then `std::pair` 709 | // and this is what deferencing the range (iterator) gives. Internally, this range is built 710 | // out of a pair of ranges (PairOfRange class), the `inc()` (advance to next element) 711 | // method calls `inc()` on each of the children, and `deref()` calls `deref()` on each child 712 | // and combines the results to return a `std::pair`. Tuples are handled as nested pairs. 713 | // 714 | // In addition to PairOfRange, there is also a VecOfRange class that can be used to treat the 715 | // outermost part of a nested container as if it were a tuple. Since tuples are printed as 716 | // columns, this is like treating a multidimensional array as if it were column-major. A 717 | // VecOfRange is obtained by calling `get_columns_range`. This is used by, for instance, 718 | // `send1d_colmajor`. The implementation is similar to that of PairOfRange. 719 | // 720 | // The range, accessed via `ArrayTraits::get_range`, will be of a different class depending 721 | // on T, and this is defined by the ArrayTraits specialization for T. It will always have 722 | // methods `inc()` to advance to the next element and `is_end()` for checking whether one has 723 | // advanced past the final element. For nested containers, `deref_subiter()` returns a range 724 | // iterator for the next nesting level. When at the innermost level of nesting, `deref()` 725 | // returns the value of the entry the iterator points to (a scalar, pair, or tuple). 726 | // Only one of `deref()` or `deref_subiter()` will be available, depending on whether there are 727 | // deeper levels of nesting. The typedefs `value_type` and `subiter_type` tell the return 728 | // types of these two methods. 729 | // 730 | // Support for standard C++ and boost containers and tuples of containers is provided in this 731 | // section. Support for third party packages like Blitz and Armadillo is in a later section. 732 | 733 | // {{{2 ArrayTraits generic class and defaults 734 | 735 | // Error messages involving this stem from treating something that was not a container as if it 736 | // was. This is only here to allow compiliation without errors in normal cases. 737 | struct Error_WasNotContainer { 738 | // This is just here to make VC++ happy. 739 | // https://connect.microsoft.com/VisualStudio/feedback/details/777612/class-template-specialization-that-compiles-in-g-but-not-visual-c 740 | typedef void subiter_type; 741 | }; 742 | 743 | // The unspecialized version of this class gives traits for things that are *not* arrays. 744 | template 745 | class ArrayTraits { 746 | public: 747 | // The value type of elements after all levels of nested containers have been dereferenced. 748 | typedef Error_WasNotContainer value_type; 749 | // The type of the range (a.k.a. iterator) that `get_range()` returns. 750 | typedef Error_WasNotContainer range_type; 751 | // Tells whether T is in fact a container type. 752 | static constexpr bool is_container = false; 753 | // This flag supports the legacy behavior of automatically guessing whether the data should 754 | // be treated as column major. This guessing happens when `send()` is called rather than 755 | // `send1d()` or `send2d()`. This is deprecated, but is still supported for reverse 756 | // compatibility. 757 | static constexpr bool allow_auto_unwrap = false; 758 | // The number of levels of nesting, or the dimension of multidimensional arrays. 759 | static constexpr size_t depth = 0; 760 | 761 | // Returns the range (iterator) for an array. 762 | static range_type get_range(const T &) { 763 | static_assert((sizeof(T)==0), "argument was not a container"); 764 | throw std::logic_error("static assert should have been triggered by this point"); 765 | } 766 | }; 767 | 768 | // Most specializations of ArrayTraits should inherit from this (with V set to the value type). 769 | // It sets some default values. 770 | template 771 | class ArrayTraitsDefaults { 772 | public: 773 | typedef V value_type; 774 | 775 | static constexpr bool is_container = true; 776 | static constexpr bool allow_auto_unwrap = true; 777 | static constexpr size_t depth = ArrayTraits::depth + 1; 778 | }; 779 | 780 | // This handles reference types, such as are given with std::tie. 781 | // It also allows for instance "ArrayTraits" to match "ArrayTraits". 782 | // I think this is okay to do... The alternative is to use remove_reference all over the place. 783 | template 784 | class ArrayTraits : public ArrayTraits { }; 785 | 786 | // This supports gp.send1d(std::forward_as_tuple(x, std::move(y))); 787 | template 788 | class ArrayTraits : public ArrayTraits { }; 789 | 790 | // }}}2 791 | 792 | // {{{2 STL container support 793 | 794 | template 795 | class IteratorRange { 796 | public: 797 | IteratorRange() { } 798 | IteratorRange(const TI &_it, const TI &_end) : it(_it), end(_end) { } 799 | 800 | static constexpr bool is_container = ArrayTraits::is_container; 801 | 802 | // Error messages involving this stem from calling deref instead of deref_subiter for a nested 803 | // container. 804 | struct Error_InappropriateDeref { }; 805 | using value_type = typename std::conditional_t; 806 | 807 | using subiter_type = typename ArrayTraits::range_type; 808 | 809 | bool is_end() const { return it == end; } 810 | 811 | void inc() { ++it; } 812 | 813 | value_type deref() const { 814 | static_assert(sizeof(TV) && !is_container, 815 | "deref called on nested container"); 816 | if(is_end()) { 817 | throw std::runtime_error("attepted to dereference past end of iterator"); 818 | } 819 | return *it; 820 | } 821 | 822 | subiter_type deref_subiter() const { 823 | static_assert(sizeof(TV) && is_container, 824 | "deref_subiter called on non-nested container"); 825 | if(is_end()) { 826 | throw std::runtime_error("attepted to dereference past end of iterator"); 827 | } 828 | return ArrayTraits::get_range(*it); 829 | } 830 | 831 | private: 832 | TI it, end; 833 | }; 834 | 835 | template 836 | class ArrayTraits> 838 | > : public ArrayTraitsDefaults { 839 | public: 840 | typedef IteratorRange range_type; 841 | 842 | static range_type get_range(const T &arg) { 843 | return range_type(arg.begin(), arg.end()); 844 | } 845 | }; 846 | 847 | template 848 | class ArrayTraits> 850 | > : public ArrayTraitsDefaults()))>::value_type> { 851 | using IterType = decltype(begin(std::declval())); 852 | using ValType = typename std::iterator_traits::value_type; 853 | public: 854 | typedef IteratorRange range_type; 855 | 856 | static range_type get_range(const T &arg) { 857 | return range_type(begin(arg), end(arg)); 858 | } 859 | }; 860 | 861 | // }}}2 862 | 863 | // {{{2 C style array support 864 | 865 | template 866 | class ArrayTraits : public ArrayTraitsDefaults { 867 | public: 868 | typedef IteratorRange range_type; 869 | 870 | static range_type get_range(const T (&arg)[N]) { 871 | return range_type(arg, arg+N); 872 | } 873 | }; 874 | 875 | // }}}2 876 | 877 | // {{{2 std::pair support 878 | 879 | template 880 | class PairOfRange { 881 | template 882 | friend void deref_and_print(std::ostream &, const PairOfRange &, PrintMode); 883 | 884 | public: 885 | PairOfRange() { } 886 | PairOfRange(const RT &_l, const RU &_r) : l(_l), r(_r) { } 887 | 888 | static constexpr bool is_container = RT::is_container && RU::is_container; 889 | 890 | typedef std::pair value_type; 891 | typedef PairOfRange subiter_type; 892 | 893 | bool is_end() const { 894 | bool el = l.is_end(); 895 | bool er = r.is_end(); 896 | if(el != er) { 897 | throw std::length_error("columns were different lengths"); 898 | } 899 | return el; 900 | } 901 | 902 | void inc() { 903 | l.inc(); 904 | r.inc(); 905 | } 906 | 907 | value_type deref() const { 908 | return std::make_pair(l.deref(), r.deref()); 909 | } 910 | 911 | subiter_type deref_subiter() const { 912 | return subiter_type(l.deref_subiter(), r.deref_subiter()); 913 | } 914 | 915 | private: 916 | RT l; 917 | RU r; 918 | }; 919 | 920 | template 921 | class ArrayTraits> { 922 | public: 923 | typedef PairOfRange::range_type, typename ArrayTraits::range_type> range_type; 924 | typedef std::pair::value_type, typename ArrayTraits::value_type> value_type; 925 | static constexpr bool is_container = ArrayTraits::is_container && ArrayTraits::is_container; 926 | // Don't allow colwrap since it's already wrapped. 927 | static constexpr bool allow_auto_unwrap = false; 928 | // It is allowed for l_depth != r_depth, for example one column could be 'double' and the 929 | // other column could be 'vector'. 930 | static constexpr size_t l_depth = ArrayTraits::depth; 931 | static constexpr size_t r_depth = ArrayTraits::depth; 932 | static constexpr size_t depth = (l_depth < r_depth) ? l_depth : r_depth; 933 | 934 | static range_type get_range(const std::pair &arg) { 935 | return range_type( 936 | ArrayTraits::get_range(arg.first), 937 | ArrayTraits::get_range(arg.second) 938 | ); 939 | } 940 | }; 941 | 942 | // }}}2 943 | 944 | // {{{2 boost::tuple support 945 | 946 | template 947 | class ArrayTraits && !is_boost_tuple_nulltype 950 | > 951 | > : public ArrayTraits< 952 | typename std::pair< 953 | typename T::head_type, 954 | typename T::tail_type 955 | > 956 | > { 957 | public: 958 | typedef typename T::head_type HT; 959 | typedef typename T::tail_type TT; 960 | 961 | typedef ArrayTraits> parent; 962 | 963 | static typename parent::range_type get_range(const T &arg) { 964 | return typename parent::range_type( 965 | ArrayTraits::get_range(arg.get_head()), 966 | ArrayTraits::get_range(arg.get_tail()) 967 | ); 968 | } 969 | }; 970 | 971 | template 972 | class ArrayTraits && is_boost_tuple_nulltype 975 | > 976 | > : public ArrayTraits< 977 | typename T::head_type 978 | > { 979 | typedef typename T::head_type HT; 980 | 981 | typedef ArrayTraits parent; 982 | 983 | public: 984 | static typename parent::range_type get_range(const T &arg) { 985 | return parent::get_range(arg.get_head()); 986 | } 987 | }; 988 | 989 | // }}}2 990 | 991 | // {{{2 std::tuple support 992 | 993 | template 994 | struct StdTupUnwinder { 995 | typedef std::pair< 996 | typename StdTupUnwinder::type, 997 | typename std::tuple_element::type 998 | > type; 999 | 1000 | static typename ArrayTraits::range_type get_range(const Tuple &arg) { 1001 | return typename ArrayTraits::range_type( 1002 | StdTupUnwinder::get_range(arg), 1003 | ArrayTraits::type>::get_range(std::get(arg)) 1004 | ); 1005 | } 1006 | }; 1007 | 1008 | template 1009 | struct StdTupUnwinder { 1010 | typedef typename std::tuple_element<0, Tuple>::type type; 1011 | 1012 | static typename ArrayTraits::range_type get_range(const Tuple &arg) { 1013 | return ArrayTraits::get_range(std::get<0>(arg)); 1014 | } 1015 | }; 1016 | 1017 | template 1018 | class ArrayTraits> : 1019 | public ArrayTraits, sizeof...(Args)-1>::type> 1020 | { 1021 | typedef std::tuple Tuple; 1022 | typedef ArrayTraits::type> parent; 1023 | 1024 | public: 1025 | static typename parent::range_type get_range(const Tuple &arg) { 1026 | return StdTupUnwinder, sizeof...(Args)-1>::get_range(arg); 1027 | } 1028 | }; 1029 | 1030 | // }}}2 1031 | 1032 | // {{{2 Support column unwrap of container (VecOfRange) 1033 | // 1034 | // VecOfRange (created via `get_columns_range()`) treats the outermost level of a nested 1035 | // container as if it were a tuple. Since tuples are sent to gnuplot as columns, this has the 1036 | // effect of addressing a multidimensional array in column major order. 1037 | 1038 | template 1039 | class VecOfRange { 1040 | template 1041 | friend void deref_and_print(std::ostream &, const VecOfRange &, PrintMode); 1042 | 1043 | public: 1044 | VecOfRange() { } 1045 | explicit VecOfRange(const std::vector &_rvec) : rvec(_rvec) { } 1046 | 1047 | static constexpr bool is_container = RT::is_container; 1048 | // Don't allow colwrap since it's already wrapped. 1049 | static constexpr bool allow_auto_unwrap = false; 1050 | 1051 | typedef std::vector value_type; 1052 | typedef VecOfRange subiter_type; 1053 | 1054 | bool is_end() const { 1055 | if(rvec.empty()) return true; 1056 | bool ret = rvec[0].is_end(); 1057 | for(size_t i=1; i subvec(rvec.size()); 1081 | for(size_t i=0; i rvec; 1089 | }; 1090 | 1091 | template 1092 | VecOfRange::range_type::subiter_type> 1093 | get_columns_range(const T &arg) { 1094 | typedef typename ArrayTraits::range_type::subiter_type U; 1095 | std::vector rvec; 1096 | typename ArrayTraits::range_type outer = ArrayTraits::get_range(arg); 1097 | while(!outer.is_end()) { 1098 | rvec.push_back(outer.deref_subiter()); 1099 | outer.inc(); 1100 | } 1101 | VecOfRange ret(rvec); 1102 | return ret; 1103 | } 1104 | 1105 | // }}}2 1106 | 1107 | // }}}1 1108 | 1109 | // {{{1 Array printing functions 1110 | // 1111 | // This section coordinates the sending of data to gnuplot. The ArrayTraits mechanism tells us 1112 | // about nested containers and provides iterators over them. Here we make use of this, 1113 | // deciding what dimensions should be treated as rows, columns, or blocks, telling gnuplot the 1114 | // size of the array if needed, and so on. 1115 | 1116 | // If this is set, then text-mode data will be sent in a format that is not compatible with 1117 | // gnuplot, but which helps the programmer tell what the library is thinking. Basically it 1118 | // puts brackets around groups of items and puts a message delineating blocks of data. 1119 | static bool debug_array_print = 0; 1120 | 1121 | // This is thrown when an empty container is being plotted. This exception should always 1122 | // be caught and should not propagate to the user. 1123 | class plotting_empty_container : public std::length_error { 1124 | public: 1125 | plotting_empty_container() : std::length_error("plotting empty container") { } 1126 | }; 1127 | 1128 | // {{{2 Tags (like enums for metaprogramming) 1129 | 1130 | // These tags define what our goal is, what sort of thing should ultimately be sent to the 1131 | // ostream. These tags are passed to the PrintMode template argument of the functions in this 1132 | // section. 1133 | // 1134 | // ModeText - Sends the data in an array in text format 1135 | // ModeBinary - Sends the data in an array in binary format 1136 | // ModeBinfmt - Sends the gnuplot format code for binary data (e.g. "%double%double") 1137 | // ModeSize - Sends the size of an array. Needed when sending binary data. 1138 | struct ModeText { static constexpr bool is_text = 1; static constexpr bool is_binfmt = 0; static constexpr bool is_size = 0; }; 1139 | struct ModeBinary { static constexpr bool is_text = 0; static constexpr bool is_binfmt = 0; static constexpr bool is_size = 0; }; 1140 | struct ModeBinfmt { static constexpr bool is_text = 0; static constexpr bool is_binfmt = 1; static constexpr bool is_size = 0; }; 1141 | struct ModeSize { static constexpr bool is_text = 0; static constexpr bool is_binfmt = 0; static constexpr bool is_size = 1; }; 1142 | 1143 | // Whether to treat the outermost level of a nested container as columns (column major mode). 1144 | struct ColUnwrapNo { }; 1145 | struct ColUnwrapYes { }; 1146 | 1147 | // The user must give a hint to describe how nested containers are to be interpreted. This is 1148 | // done by calling e.g. `send1d_colmajor()` or `send2d()`. This hint is then described by the 1149 | // following tags. This is passed to the OrganizationMode template argument. 1150 | struct Mode1D { static std::string class_name() { return "Mode1D" ; } }; 1151 | struct Mode2D { static std::string class_name() { return "Mode2D" ; } }; 1152 | struct Mode1DUnwrap { static std::string class_name() { return "Mode1DUnwrap"; } }; 1153 | struct Mode2DUnwrap { static std::string class_name() { return "Mode2DUnwrap"; } }; 1154 | // Support for the legacy behavior that guesses which of the above four modes should be used. 1155 | struct ModeAuto { static std::string class_name() { return "ModeAuto" ; } }; 1156 | 1157 | // }}}2 1158 | 1159 | // {{{2 ModeAutoDecoder 1160 | // 1161 | // ModeAuto guesses which of Mode1D, Mode2D, Mode1DUnwrap, or Mode2DUnwrap should be used. 1162 | // This is provided for reverse compatibility; it is better to specify explicitly which mode to 1163 | // use. Since this is only for reverse compatibility, and shouldn't be used, I'm not going to 1164 | // spell out what the rules are. See below for details. 1165 | 1166 | template 1167 | struct ModeAutoDecoder { }; 1168 | 1169 | template 1170 | struct ModeAutoDecoder::depth == 1) 1173 | >> 1174 | { 1175 | typedef Mode1D mode; 1176 | }; 1177 | 1178 | template 1179 | struct ModeAutoDecoder::depth == 2) && 1182 | !ArrayTraits::allow_auto_unwrap 1183 | >> 1184 | { 1185 | typedef Mode2D mode; 1186 | }; 1187 | 1188 | template 1189 | struct ModeAutoDecoder::depth == 2) && 1192 | ArrayTraits::allow_auto_unwrap 1193 | >> 1194 | { 1195 | typedef Mode1DUnwrap mode; 1196 | }; 1197 | 1198 | template 1199 | struct ModeAutoDecoder::depth > 2) && 1202 | ArrayTraits::allow_auto_unwrap 1203 | >> 1204 | { 1205 | typedef Mode2DUnwrap mode; 1206 | }; 1207 | 1208 | template 1209 | struct ModeAutoDecoder::depth > 2) && 1212 | !ArrayTraits::allow_auto_unwrap 1213 | >> 1214 | { 1215 | typedef Mode2D mode; 1216 | }; 1217 | 1218 | // }}}2 1219 | 1220 | // The data is processed using several levels of functions that call each other in sequence, 1221 | // each defined in a subsection of code below. Because C++ wants you to declare a function 1222 | // before using it, we begin with the innermost function. So in order to see the sequence in 1223 | // which these are called, you should read the following subsections in reverse order. Nested 1224 | // arrays are formated into blocks (for 2D data) and lines (for 1D or 2D data), then further 1225 | // nesting levels are formatted into columns. Also tag dispatching is used in order to define 1226 | // various sorts of behavior. Each of these tasks is handled by one of the following 1227 | // subsections. 1228 | 1229 | // {{{2 send_scalar() 1230 | // 1231 | // Send a scalar in one of three possible ways: via TextSender, BinarySender, or BinfmtSender, 1232 | // depending on which PrintMode tag is passed to the function. 1233 | 1234 | template 1235 | void send_scalar(std::ostream &stream, const T &arg, ModeText) { 1236 | TextSender::send(stream, arg); 1237 | } 1238 | 1239 | template 1240 | void send_scalar(std::ostream &stream, const T &arg, ModeBinary) { 1241 | BinarySender::send(stream, arg); 1242 | } 1243 | 1244 | template 1245 | void send_scalar(std::ostream &stream, const T &, ModeBinfmt) { 1246 | BinfmtSender::send(stream); 1247 | } 1248 | 1249 | // }}}2 1250 | 1251 | // {{{2 deref_and_print() 1252 | // 1253 | // Dereferences and prints the given range (iterator). At this point we are done with treating 1254 | // containers as blocks (for 2D data) and lines (for 1D or 2D data). Any further levels of 1255 | // nested containers will at this point be treated as columns. 1256 | 1257 | // If arg is not a container, then print it via send_scalar(). 1258 | template 1259 | typename std::enable_if_t 1260 | deref_and_print(std::ostream &stream, const T &arg, PrintMode) { 1261 | const typename T::value_type &v = arg.deref(); 1262 | send_scalar(stream, v, PrintMode()); 1263 | } 1264 | 1265 | // If arg is a container (but not a PairOfRange or VecOfRange, which are handled below) then 1266 | // treat the contents as columns, iterating over the contents recursively. If outputting in 1267 | // text mode, put a space between columns. 1268 | template 1269 | typename std::enable_if_t 1270 | deref_and_print(std::ostream &stream, const T &arg, PrintMode) { 1271 | if(arg.is_end()) throw plotting_empty_container(); 1272 | typename T::subiter_type subrange = arg.deref_subiter(); 1273 | if(PrintMode::is_binfmt && subrange.is_end()) throw plotting_empty_container(); 1274 | if(debug_array_print && PrintMode::is_text) stream << "{"; 1275 | bool first = true; 1276 | while(!subrange.is_end()) { 1277 | if(!first && PrintMode::is_text) stream << " "; 1278 | first = false; 1279 | deref_and_print(stream, subrange, PrintMode()); 1280 | subrange.inc(); 1281 | } 1282 | if(debug_array_print && PrintMode::is_text) stream << "}"; 1283 | } 1284 | 1285 | // PairOfRange is treated as columns. In text mode, put a space between columns. 1286 | template 1287 | void deref_and_print(std::ostream &stream, const PairOfRange &arg, PrintMode) { 1288 | deref_and_print(stream, arg.l, PrintMode()); 1289 | if(PrintMode::is_text) stream << " "; 1290 | deref_and_print(stream, arg.r, PrintMode()); 1291 | } 1292 | 1293 | // VecOfRange is treated as columns. In text mode, put a space between columns. 1294 | template 1295 | void deref_and_print(std::ostream &stream, const VecOfRange &arg, PrintMode) { 1296 | if(PrintMode::is_binfmt && arg.rvec.empty()) throw plotting_empty_container(); 1297 | for(size_t i=0; i 1323 | typename std::enable_if_t<(Depth==1) && !PrintMode::is_size> 1324 | print_block(std::ostream &stream, T &arg, PrintMode) { 1325 | if(PrintMode::is_binfmt && arg.is_end()) throw plotting_empty_container(); 1326 | for(; !arg.is_end(); arg.inc()) { 1327 | //print_entry(arg.deref()); 1328 | deref_and_print(stream, arg, PrintMode()); 1329 | // If asked to print the binary format string, only the first element needs to be 1330 | // looked at. 1331 | if(PrintMode::is_binfmt) break; 1332 | if(PrintMode::is_text) stream << std::endl; 1333 | } 1334 | } 1335 | 1336 | // Depth>1 and we are not asked to print the size of the array. Loop over the range and 1337 | // recurse into print_block() with Depth -> Depth-1. 1338 | template 1339 | typename std::enable_if_t<(Depth>1) && !PrintMode::is_size> 1340 | print_block(std::ostream &stream, T &arg, PrintMode) { 1341 | if(PrintMode::is_binfmt && arg.is_end()) throw plotting_empty_container(); 1342 | bool first = true; 1343 | for(; !arg.is_end(); arg.inc()) { 1344 | if(first) { 1345 | first = false; 1346 | } else { 1347 | if(PrintMode::is_text) stream << std::endl; 1348 | } 1349 | if(debug_array_print && PrintMode::is_text) stream << "" << std::endl; 1350 | if(arg.is_end()) throw plotting_empty_container(); 1351 | typename T::subiter_type sub = arg.deref_subiter(); 1352 | print_block(stream, sub, PrintMode()); 1353 | // If asked to print the binary format string, only the first element needs to be 1354 | // looked at. 1355 | if(PrintMode::is_binfmt) break; 1356 | } 1357 | } 1358 | 1359 | // Determine how many elements are in the given range. Used in the functions below. 1360 | template 1361 | size_t get_range_size(const T &arg) { 1362 | // FIXME - not the fastest way. Implement a size() method for range. 1363 | size_t ret = 0; 1364 | for(T i=arg; !i.is_end(); i.inc()) ++ret; 1365 | return ret; 1366 | } 1367 | 1368 | // Depth==1 and we are asked to print the size of the array. 1369 | template 1370 | typename std::enable_if_t<(Depth==1) && PrintMode::is_size> 1371 | print_block(std::ostream &stream, T &arg, PrintMode) { 1372 | stream << get_range_size(arg); 1373 | } 1374 | 1375 | // Depth>1 and we are asked to print the size of the array. 1376 | template 1377 | typename std::enable_if_t<(Depth>1) && PrintMode::is_size> 1378 | print_block(std::ostream &stream, T &arg, PrintMode) { 1379 | if(arg.is_end()) throw plotting_empty_container(); 1380 | // It seems that size for two dimensional arrays needs the fastest varying index first, 1381 | // contrary to intuition. The gnuplot documentation is not too clear on this point. 1382 | typename T::subiter_type sub = arg.deref_subiter(); 1383 | print_block(stream, sub, PrintMode()); 1384 | stream << "," << get_range_size(arg); 1385 | } 1386 | 1387 | // }}}2 1388 | 1389 | // {{{2 handle_colunwrap_tag() 1390 | // 1391 | // If passed the ColUnwrapYes then treat the outermost nested container as columns by calling 1392 | // get_columns_range(). Otherwise just call get_range(). The range iterator is then passed to 1393 | // print_block() for further processing. 1394 | 1395 | template 1396 | void handle_colunwrap_tag(std::ostream &stream, const T &arg, ColUnwrapNo, PrintMode) { 1397 | static_assert(ArrayTraits::depth >= Depth, "container not deep enough"); 1398 | typename ArrayTraits::range_type range = ArrayTraits::get_range(arg); 1399 | print_block(stream, range, PrintMode()); 1400 | } 1401 | 1402 | template 1403 | void handle_colunwrap_tag(std::ostream &stream, const T &arg, ColUnwrapYes, PrintMode) { 1404 | static_assert(ArrayTraits::depth >= Depth+1, "container not deep enough"); 1405 | VecOfRange::range_type::subiter_type> cols = get_columns_range(arg); 1406 | print_block(stream, cols, PrintMode()); 1407 | } 1408 | 1409 | // }}}2 1410 | 1411 | // {{{2 handle_organization_tag() 1412 | // 1413 | // Parse the OrganizationMode tag then forward to handle_colunwrap_tag() for further 1414 | // processing. If passed the Mode1D or Mode2D tags, then set Depth=1 or Depth=2. If passed 1415 | // Mode{1,2}DUnwrap then use the ColUnwrapYes tag. If passed ModeAuto (which is for legacy 1416 | // support) then use ModeAutoDecoder to guess which of Mode1D, Mode2D, etc. should be used. 1417 | 1418 | template 1419 | void handle_organization_tag(std::ostream &stream, const T &arg, Mode1D, PrintMode) { 1420 | handle_colunwrap_tag<1>(stream, arg, ColUnwrapNo(), PrintMode()); 1421 | } 1422 | 1423 | template 1424 | void handle_organization_tag(std::ostream &stream, const T &arg, Mode2D, PrintMode) { 1425 | handle_colunwrap_tag<2>(stream, arg, ColUnwrapNo(), PrintMode()); 1426 | } 1427 | 1428 | template 1429 | void handle_organization_tag(std::ostream &stream, const T &arg, Mode1DUnwrap, PrintMode) { 1430 | handle_colunwrap_tag<1>(stream, arg, ColUnwrapYes(), PrintMode()); 1431 | } 1432 | 1433 | template 1434 | void handle_organization_tag(std::ostream &stream, const T &arg, Mode2DUnwrap, PrintMode) { 1435 | handle_colunwrap_tag<2>(stream, arg, ColUnwrapYes(), PrintMode()); 1436 | } 1437 | 1438 | template 1439 | void handle_organization_tag(std::ostream &stream, const T &arg, ModeAuto, PrintMode) { 1440 | handle_organization_tag(stream, arg, typename ModeAutoDecoder::mode(), PrintMode()); 1441 | } 1442 | 1443 | // }}}2 1444 | 1445 | // The entry point for the processing defined in this section. It just forwards immediately to 1446 | // handle_organization_tag(). This function is only here to give a sane name to the entry 1447 | // point. 1448 | // 1449 | // The allowed values for the OrganizationMode and PrintMode tags are defined in the beginning 1450 | // of this section. 1451 | template 1452 | void top_level_array_sender(std::ostream &stream, const T &arg, OrganizationMode, PrintMode) { 1453 | handle_organization_tag(stream, arg, OrganizationMode(), PrintMode()); 1454 | } 1455 | 1456 | // }}}1 1457 | 1458 | // {{{1 PlotGroup 1459 | 1460 | class PlotData { 1461 | public: 1462 | PlotData() { } 1463 | 1464 | template 1465 | PlotData( 1466 | const T &arg, 1467 | const std::string &_plotspec, 1468 | const std::string &_arr_or_rec, 1469 | OrganizationMode, PrintMode 1470 | ) : 1471 | plotspec(_plotspec), 1472 | is_text(PrintMode::is_text), 1473 | is_inline(true), 1474 | has_data(true), 1475 | arr_or_rec(_arr_or_rec) 1476 | { 1477 | { 1478 | std::ostringstream tmp; 1479 | top_level_array_sender(tmp, arg, OrganizationMode(), PrintMode()); 1480 | data = tmp.str(); 1481 | } 1482 | 1483 | if(!is_text) { 1484 | try { 1485 | { 1486 | std::ostringstream tmp; 1487 | top_level_array_sender(tmp, arg, OrganizationMode(), ModeBinfmt()); 1488 | bin_fmt = tmp.str(); 1489 | } 1490 | { 1491 | std::ostringstream tmp; 1492 | top_level_array_sender(tmp, arg, OrganizationMode(), ModeSize()); 1493 | bin_size = tmp.str(); 1494 | } 1495 | } catch(const plotting_empty_container &) { 1496 | bin_fmt = ""; 1497 | bin_size = "0"; 1498 | } 1499 | } 1500 | } 1501 | 1502 | explicit PlotData(const std::string &_plotspec) : 1503 | plotspec(_plotspec), 1504 | is_text(true), 1505 | is_inline(false), 1506 | has_data(false) 1507 | { } 1508 | 1509 | PlotData &file(const std::string &fn) { 1510 | filename = fn; 1511 | is_inline = false; 1512 | 1513 | std::ios_base::openmode mode = std::fstream::out; 1514 | if(!is_text) mode |= std::fstream::binary; 1515 | std::fstream fh(filename.c_str(), mode); 1516 | fh << data; 1517 | fh.close(); 1518 | 1519 | return *this; 1520 | } 1521 | 1522 | std::string plotCmd() const { 1523 | std::string cmd; 1524 | if(has_data) { 1525 | if(filename.empty()) { 1526 | cmd += "'-' "; 1527 | } else { 1528 | // FIXME - hopefully filename doesn't contain quotes or such... 1529 | cmd += "'" + filename + "' "; 1530 | } 1531 | if(!is_text) { 1532 | cmd += binConfig() + " "; 1533 | } 1534 | } 1535 | cmd += plotspec; 1536 | return cmd; 1537 | } 1538 | 1539 | bool isInline() const { 1540 | return is_inline; 1541 | } 1542 | 1543 | const std::string &getData() const { 1544 | return data; 1545 | } 1546 | 1547 | bool isText() const { return is_text; } 1548 | 1549 | bool isBinary() const { return !is_text; } 1550 | 1551 | private: 1552 | std::string binConfig() const { 1553 | return "binary format='" + bin_fmt + "' " + arr_or_rec + "=(" + bin_size + ")"; 1554 | } 1555 | 1556 | private: 1557 | std::string plotspec; 1558 | bool is_text; 1559 | bool is_inline; 1560 | bool has_data; 1561 | std::string data; 1562 | std::string filename; 1563 | std::string arr_or_rec; 1564 | std::string bin_fmt; 1565 | std::string bin_size; 1566 | }; 1567 | 1568 | class PlotGroup { 1569 | public: 1570 | friend class Gnuplot; 1571 | 1572 | explicit PlotGroup(const std::string &plot_type_) : plot_type(plot_type_) { } 1573 | 1574 | PlotGroup &add_preamble(const std::string &s) { 1575 | preamble_lines.push_back(s); 1576 | return *this; 1577 | } 1578 | 1579 | PlotGroup &add_plot(const std::string &plotspec) { plots.emplace_back(plotspec); return *this; } 1580 | 1581 | template PlotGroup &add_plot1d (const T &arg, const std::string &plotspec="", const std::string &text_array_record="text") { add(arg, plotspec, text_array_record, Mode1D ()); return *this; } 1582 | template PlotGroup &add_plot2d (const T &arg, const std::string &plotspec="", const std::string &text_array_record="text") { add(arg, plotspec, text_array_record, Mode2D ()); return *this; } 1583 | template PlotGroup &add_plot1d_colmajor(const T &arg, const std::string &plotspec="", const std::string &text_array_record="text") { add(arg, plotspec, text_array_record, Mode1DUnwrap()); return *this; } 1584 | template PlotGroup &add_plot2d_colmajor(const T &arg, const std::string &plotspec="", const std::string &text_array_record="text") { add(arg, plotspec, text_array_record, Mode2DUnwrap()); return *this; } 1585 | 1586 | PlotGroup &file(const std::string &fn) { 1587 | assert(!plots.empty()); 1588 | plots.back().file(fn); 1589 | return *this; 1590 | } 1591 | 1592 | size_t num_plots() const { return plots.size(); } 1593 | 1594 | private: 1595 | template 1596 | void add(const T &arg, const std::string &plotspec, const std::string &text_array_record, OrganizationMode) { 1597 | if(!( 1598 | text_array_record == "text" || 1599 | text_array_record == "array" || 1600 | text_array_record == "record" 1601 | )) throw std::logic_error("text_array_record must be one of text, array, or record (was "+ 1602 | text_array_record+")"); 1603 | 1604 | if(text_array_record == "text") { 1605 | plots.emplace_back(arg, plotspec, 1606 | "array", // arbitrary value 1607 | OrganizationMode(), ModeText()); 1608 | } else { 1609 | plots.emplace_back(arg, plotspec, text_array_record, 1610 | OrganizationMode(), ModeBinary()); 1611 | } 1612 | } 1613 | 1614 | std::string plot_type; 1615 | std::vector preamble_lines; 1616 | std::vector plots; 1617 | }; 1618 | 1619 | // }}}1 1620 | 1621 | // {{{1 FileHandleWrapper 1622 | 1623 | // This holds the file handle that gnuplot commands will be sent to. The purpose of this 1624 | // wrapper is twofold: 1625 | // 1. It allows storing the FILE* before it gets passed to the boost::iostreams::stream 1626 | // constructor (which is a base class of the main Gnuplot class). This is accomplished 1627 | // via multiple inheritance as described at http://stackoverflow.com/a/3821756/1048959 1628 | // 2. It remembers whether the handle needs to be closed via fclose or pclose. 1629 | struct FileHandleWrapper { 1630 | FileHandleWrapper(std::FILE *_fh, bool _should_use_pclose) : 1631 | wrapped_fh(_fh), should_use_pclose(_should_use_pclose) { } 1632 | 1633 | void fh_close() { 1634 | if(should_use_pclose) { 1635 | if(GNUPLOT_PCLOSE(wrapped_fh)) { 1636 | perror("pclose"); 1637 | //char msg[1000]; 1638 | //strerror_s(msg, sizeof(msg), errno); 1639 | //std::cerr << "pclose returned error: " << msg << std::endl; 1640 | } 1641 | } else { 1642 | if(fclose(wrapped_fh)) { 1643 | std::cerr << "fclose returned error" << std::endl; 1644 | } 1645 | } 1646 | } 1647 | 1648 | int fh_fileno() { 1649 | return GNUPLOT_FILENO(wrapped_fh); 1650 | } 1651 | 1652 | std::FILE *wrapped_fh; 1653 | bool should_use_pclose; 1654 | }; 1655 | 1656 | // }}}1 1657 | 1658 | // {{{1 Main class 1659 | 1660 | class Gnuplot : 1661 | // Some setup needs to be done before obtaining the file descriptor that gets passed to 1662 | // boost::iostreams::stream. This is accomplished by using a multiple inheritance trick, 1663 | // as described at http://stackoverflow.com/a/3821756/1048959 1664 | private FileHandleWrapper, 1665 | public boost::iostreams::stream 1666 | { 1667 | private: 1668 | static std::string get_default_cmd() { 1669 | GNUPLOT_MSVC_WARNING_4996_PUSH 1670 | char *from_env = std::getenv("GNUPLOT_IOSTREAM_CMD"); 1671 | GNUPLOT_MSVC_WARNING_4996_POP 1672 | if(from_env && from_env[0]) { 1673 | return from_env; 1674 | } else { 1675 | return GNUPLOT_DEFAULT_COMMAND; 1676 | } 1677 | } 1678 | 1679 | static FileHandleWrapper open_cmdline(const std::string &in) { 1680 | std::string cmd = in.empty() ? get_default_cmd() : in; 1681 | assert(!cmd.empty()); 1682 | if(cmd[0] == '>') { 1683 | std::string fn = cmd.substr(1); 1684 | GNUPLOT_MSVC_WARNING_4996_PUSH 1685 | FILE *fh = std::fopen(fn.c_str(), "w"); 1686 | GNUPLOT_MSVC_WARNING_4996_POP 1687 | if(!fh) throw std::ios_base::failure("cannot open file "+fn); 1688 | return FileHandleWrapper(fh, false); 1689 | } else { 1690 | FILE *fh = GNUPLOT_POPEN(cmd.c_str(), "w"); 1691 | if(!fh) throw std::ios_base::failure("cannot open pipe "+cmd); 1692 | return FileHandleWrapper(fh, true); 1693 | } 1694 | } 1695 | 1696 | public: 1697 | explicit Gnuplot(const std::string &_cmd="") : 1698 | FileHandleWrapper(open_cmdline(_cmd)), 1699 | boost::iostreams::stream( 1700 | fh_fileno(), 1701 | #if BOOST_VERSION >= 104400 1702 | boost::iostreams::never_close_handle 1703 | #else 1704 | false 1705 | #endif 1706 | ), 1707 | feedback(nullptr), 1708 | tmp_files(new GnuplotTmpfileCollection()), 1709 | debug_messages(false), 1710 | transport_tmpfile(false) 1711 | { 1712 | set_stream_options(*this); 1713 | } 1714 | 1715 | explicit Gnuplot(FILE *_fh) : 1716 | FileHandleWrapper(_fh, 0), 1717 | boost::iostreams::stream( 1718 | fh_fileno(), 1719 | #if BOOST_VERSION >= 104400 1720 | boost::iostreams::never_close_handle 1721 | #else 1722 | false 1723 | #endif 1724 | ), 1725 | feedback(nullptr), 1726 | tmp_files(new GnuplotTmpfileCollection()), 1727 | debug_messages(false), 1728 | transport_tmpfile(false) 1729 | { 1730 | set_stream_options(*this); 1731 | } 1732 | 1733 | private: 1734 | // noncopyable 1735 | Gnuplot(const Gnuplot &) = delete; 1736 | const Gnuplot& operator=(const Gnuplot &) = delete; 1737 | 1738 | public: 1739 | ~Gnuplot() { 1740 | if(debug_messages) { 1741 | std::cerr << "ending gnuplot session" << std::endl; 1742 | } 1743 | 1744 | // FIXME - boost's close method calls close() on the file descriptor, but we need to 1745 | // use sometimes use pclose instead. For now, just skip calling boost's close and use 1746 | // flush just in case. 1747 | do_flush(); 1748 | // Wish boost had a pclose method... 1749 | //close(); 1750 | 1751 | fh_close(); 1752 | 1753 | delete feedback; 1754 | } 1755 | 1756 | void useTmpFile(bool state) { 1757 | transport_tmpfile = state; 1758 | } 1759 | 1760 | void clearTmpfiles() { 1761 | // destructors will cause deletion 1762 | tmp_files->clear(); 1763 | } 1764 | 1765 | public: 1766 | void do_flush() { 1767 | *this << std::flush; 1768 | fflush(wrapped_fh); 1769 | } 1770 | 1771 | private: 1772 | std::string make_tmpfile() { 1773 | return tmp_files->make_tmpfile(); 1774 | } 1775 | 1776 | void set_stream_options(std::ostream &os) const 1777 | { 1778 | os << std::defaultfloat << std::setprecision(17); // refer 1779 | } 1780 | 1781 | public: 1782 | // {{{2 Generic sender routines. 1783 | // 1784 | // These are declared public, but are undocumented. It is recommended to use the functions in 1785 | // the next section, which serve as adapters that pass specific values for the OrganizationMode 1786 | // tag. 1787 | 1788 | template 1789 | Gnuplot &send(const T &arg, OrganizationMode) { 1790 | top_level_array_sender(*this, arg, OrganizationMode(), ModeText()); 1791 | *this << "e" << std::endl; // gnuplot's "end of array" token 1792 | do_flush(); // probably not really needed, but doesn't hurt 1793 | return *this; 1794 | } 1795 | 1796 | template 1797 | Gnuplot &sendBinary(const T &arg, OrganizationMode) { 1798 | top_level_array_sender(*this, arg, OrganizationMode(), ModeBinary()); 1799 | do_flush(); // probably not really needed, but doesn't hurt 1800 | return *this; 1801 | } 1802 | 1803 | template 1804 | std::string binfmt(const T &arg, const std::string &arr_or_rec, OrganizationMode) { 1805 | assert((arr_or_rec == "array") || (arr_or_rec == "record")); 1806 | std::string ret; 1807 | try { 1808 | std::ostringstream tmp; 1809 | tmp << " format='"; 1810 | top_level_array_sender(tmp, arg, OrganizationMode(), ModeBinfmt()); 1811 | tmp << "' " << arr_or_rec << "=("; 1812 | top_level_array_sender(tmp, arg, OrganizationMode(), ModeSize()); 1813 | tmp << ")"; 1814 | tmp << " "; 1815 | ret = tmp.str(); 1816 | } catch(const plotting_empty_container &) { 1817 | ret = std::string(" format='' ") + arr_or_rec + "=(0) "; 1818 | } 1819 | return ret; 1820 | } 1821 | 1822 | // NOTE: empty filename makes temporary file 1823 | template 1824 | std::string file(const T &arg, std::string filename, OrganizationMode) { 1825 | if(filename.empty()) filename = make_tmpfile(); 1826 | std::fstream tmp_stream(filename.c_str(), std::fstream::out); 1827 | tmp_stream.copyfmt(*this); 1828 | top_level_array_sender(tmp_stream, arg, OrganizationMode(), ModeText()); 1829 | tmp_stream.close(); 1830 | 1831 | std::ostringstream cmdline; 1832 | // FIXME - hopefully filename doesn't contain quotes or such... 1833 | cmdline << " '" << filename << "' "; 1834 | return cmdline.str(); 1835 | } 1836 | 1837 | // NOTE: empty filename makes temporary file 1838 | template 1839 | std::string binaryFile(const T &arg, std::string filename, const std::string &arr_or_rec, OrganizationMode) { 1840 | if(filename.empty()) filename = make_tmpfile(); 1841 | std::fstream tmp_stream(filename.c_str(), std::fstream::out | std::fstream::binary); 1842 | top_level_array_sender(tmp_stream, arg, OrganizationMode(), ModeBinary()); 1843 | tmp_stream.close(); 1844 | 1845 | std::ostringstream cmdline; 1846 | // FIXME - hopefully filename doesn't contain quotes or such... 1847 | cmdline << " '" << filename << "' binary" << binfmt(arg, arr_or_rec, OrganizationMode()); 1848 | return cmdline.str(); 1849 | } 1850 | 1851 | // }}}2 1852 | 1853 | // {{{2 Deprecated data sending interface that guesses an appropriate OrganizationMode. This is here 1854 | // for reverse compatibility. Don't use it. A warning will be printed if 1855 | // GNUPLOT_DEPRECATE_WARN is defined. 1856 | 1857 | template Gnuplot GNUPLOT_DEPRECATE("use send1d or send2d") 1858 | &send(const T &arg) { return send(arg, ModeAuto()); } 1859 | 1860 | template std::string GNUPLOT_DEPRECATE("use binfmt1d or binfmt2d") 1861 | binfmt(const T &arg, const std::string &arr_or_rec="array") 1862 | { return binfmt(arg, arr_or_rec, ModeAuto()); } 1863 | 1864 | template Gnuplot GNUPLOT_DEPRECATE("use sendBinary1d or sendBinary2d") 1865 | &sendBinary(const T &arg) { return sendBinary(arg, ModeAuto()); } 1866 | 1867 | template std::string GNUPLOT_DEPRECATE("use file1d or file2d") 1868 | file(const T &arg, const std::string &filename="") 1869 | { return file(arg, filename, ModeAuto()); } 1870 | 1871 | template std::string GNUPLOT_DEPRECATE("use binArr1d or binArr2d") 1872 | binaryFile(const T &arg, const std::string &filename="", const std::string &arr_or_rec="array") 1873 | { return binaryFile(arg, filename, arr_or_rec, ModeAuto()); } 1874 | 1875 | // }}}2 1876 | 1877 | // {{{2 Public (documented) data sending interface. 1878 | // 1879 | // It seems odd to define 16 different functions, but I think this ends up being the most 1880 | // convenient in terms of usage by the end user. 1881 | 1882 | template Gnuplot &send1d (const T &arg) { return send(arg, Mode1D ()); } 1883 | template Gnuplot &send2d (const T &arg) { return send(arg, Mode2D ()); } 1884 | template Gnuplot &send1d_colmajor(const T &arg) { return send(arg, Mode1DUnwrap()); } 1885 | template Gnuplot &send2d_colmajor(const T &arg) { return send(arg, Mode2DUnwrap()); } 1886 | 1887 | template Gnuplot &sendBinary1d (const T &arg) { return sendBinary(arg, Mode1D ()); } 1888 | template Gnuplot &sendBinary2d (const T &arg) { return sendBinary(arg, Mode2D ()); } 1889 | template Gnuplot &sendBinary1d_colmajor(const T &arg) { return sendBinary(arg, Mode1DUnwrap()); } 1890 | template Gnuplot &sendBinary2d_colmajor(const T &arg) { return sendBinary(arg, Mode2DUnwrap()); } 1891 | 1892 | template std::string file1d (const T &arg, const std::string &filename="") { return file(arg, filename, Mode1D ()); } 1893 | template std::string file2d (const T &arg, const std::string &filename="") { return file(arg, filename, Mode2D ()); } 1894 | template std::string file1d_colmajor(const T &arg, const std::string &filename="") { return file(arg, filename, Mode1DUnwrap()); } 1895 | template std::string file2d_colmajor(const T &arg, const std::string &filename="") { return file(arg, filename, Mode2DUnwrap()); } 1896 | 1897 | template std::string binFmt1d (const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode1D ()); } 1898 | template std::string binFmt2d (const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode2D ()); } 1899 | template std::string binFmt1d_colmajor(const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode1DUnwrap()); } 1900 | template std::string binFmt2d_colmajor(const T &arg, const std::string &arr_or_rec) { return binfmt(arg, arr_or_rec, Mode2DUnwrap()); } 1901 | 1902 | 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 ()); } 1903 | 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 ()); } 1904 | 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()); } 1905 | 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()); } 1906 | 1907 | // }}}2 1908 | 1909 | #ifdef GNUPLOT_ENABLE_FEEDBACK 1910 | public: 1911 | // Input variables are set to the mouse position and button. If the gnuplot 1912 | // window is closed, button -1 is returned. The msg parameter is the prompt 1913 | // that is printed to the console. 1914 | void getMouse( 1915 | double &mx, double &my, int &mb, 1916 | std::string msg="Click Mouse!" 1917 | ) { 1918 | allocFeedback(); 1919 | 1920 | *this << "set mouse" << std::endl; 1921 | *this << "pause mouse \"" << msg << "\\n\"" << std::endl; 1922 | *this << "if (exists(\"MOUSE_X\")) print MOUSE_X, MOUSE_Y, MOUSE_BUTTON; else print 0, 0, -1;" << std::endl; 1923 | if(debug_messages) { 1924 | std::cerr << "begin scanf" << std::endl; 1925 | } 1926 | if(3 != fscanf(feedback->handle(), "%50lf %50lf %50d", &mx, &my, &mb)) { 1927 | throw std::runtime_error("could not parse reply"); 1928 | } 1929 | if(debug_messages) { 1930 | std::cerr << "end scanf" << std::endl; 1931 | } 1932 | } 1933 | 1934 | private: 1935 | void allocFeedback() { 1936 | if(!feedback) { 1937 | #ifdef GNUPLOT_ENABLE_PTY 1938 | feedback = new GnuplotFeedbackPty(debug_messages); 1939 | //#elif defined GNUPLOT_USE_TMPFILE 1940 | //// Currently this doesn't work since fscanf doesn't block (need something like "tail -f") 1941 | // feedback = new GnuplotFeedbackTmpfile(debug_messages); 1942 | #else 1943 | // This shouldn't happen because we are in an `#ifdef GNUPLOT_ENABLE_FEEDBACK` 1944 | // block which should only be activated if GNUPLOT_ENABLE_PTY is defined. 1945 | static_assert((sizeof(T) == 0), "No feedback mechanism defined."); 1946 | #endif 1947 | *this << "set print \"" << feedback->filename() << "\"" << std::endl; 1948 | } 1949 | } 1950 | #endif // GNUPLOT_ENABLE_FEEDBACK 1951 | 1952 | // {{{2 PlotGroup 1953 | 1954 | public: 1955 | static PlotGroup plotGroup() { 1956 | return PlotGroup("plot"); 1957 | } 1958 | 1959 | static PlotGroup splotGroup() { 1960 | return PlotGroup("splot"); 1961 | } 1962 | 1963 | Gnuplot &send(const PlotGroup &plot_group) { 1964 | return send(PlotGroup(plot_group)); 1965 | } 1966 | 1967 | Gnuplot &send(const PlotGroup &&plot_group) { 1968 | for(const std::string &s : plot_group.preamble_lines) { 1969 | *this << s << "\n"; 1970 | } 1971 | 1972 | std::vector spl = std::move(plot_group.plots); 1973 | 1974 | if(transport_tmpfile) { 1975 | for(size_t i=0; i tmp_files; 2020 | public: 2021 | bool debug_messages; 2022 | bool transport_tmpfile; 2023 | }; 2024 | 2025 | inline Gnuplot &operator<<(Gnuplot &gp, PlotGroup &sp) { 2026 | return gp.send(sp); 2027 | } 2028 | 2029 | inline Gnuplot &operator<<(Gnuplot &gp, PlotGroup &&sp) { 2030 | return gp.send(sp); 2031 | } 2032 | 2033 | // }}}1 2034 | 2035 | } // namespace gnuplotio 2036 | 2037 | // The first version of this library didn't use namespaces, and now this must be here forever 2038 | // for reverse compatibility. 2039 | using gnuplotio::Gnuplot; 2040 | 2041 | #endif // GNUPLOT_IOSTREAM_H 2042 | 2043 | // {{{1 Support for 3rd party array libraries 2044 | 2045 | // {{{2 Blitz support 2046 | 2047 | // This is outside of the main header guard so that it will be compiled when people do 2048 | // something like this: 2049 | // #include "gnuplot-iostream.h" 2050 | // #include 2051 | // #include "gnuplot-iostream.h" 2052 | // Note that it has its own header guard to avoid double inclusion. 2053 | 2054 | #ifdef BZ_BLITZ_H 2055 | #ifndef GNUPLOT_BLITZ_SUPPORT_LOADED 2056 | #define GNUPLOT_BLITZ_SUPPORT_LOADED 2057 | namespace gnuplotio { 2058 | 2059 | template 2060 | struct BinfmtSender> { 2061 | static void send(std::ostream &stream) { 2062 | for(int i=0; i::send(stream); 2064 | } 2065 | } 2066 | }; 2067 | 2068 | template 2069 | struct TextSender> { 2070 | static void send(std::ostream &stream, const blitz::TinyVector &v) { 2071 | for(int i=0; i::send(stream, v[i]); 2074 | } 2075 | } 2076 | }; 2077 | 2078 | template 2079 | struct BinarySender> { 2080 | static void send(std::ostream &stream, const blitz::TinyVector &v) { 2081 | for(int i=0; i::send(stream, v[i]); 2083 | } 2084 | } 2085 | }; 2086 | 2087 | class Error_WasBlitzPartialSlice { }; 2088 | 2089 | template 2090 | class BlitzIterator { 2091 | public: 2092 | BlitzIterator() : p(nullptr) { } 2093 | BlitzIterator( 2094 | const blitz::Array *_p, 2095 | const blitz::TinyVector _idx 2096 | ) : p(_p), idx(_idx) { } 2097 | 2098 | typedef Error_WasBlitzPartialSlice value_type; 2099 | typedef BlitzIterator subiter_type; 2100 | static constexpr bool is_container = true; 2101 | 2102 | // FIXME - it would be nice to also handle one-based arrays 2103 | bool is_end() const { 2104 | return idx[ArrayDim-SliceDim] == p->shape()[ArrayDim-SliceDim]; 2105 | } 2106 | 2107 | void inc() { 2108 | ++idx[ArrayDim-SliceDim]; 2109 | } 2110 | 2111 | value_type deref() const { 2112 | static_assert((sizeof(T) == 0), "cannot deref a blitz slice"); 2113 | throw std::logic_error("static assert should have been triggered by this point"); 2114 | } 2115 | 2116 | subiter_type deref_subiter() const { 2117 | return BlitzIterator(p, idx); 2118 | } 2119 | 2120 | private: 2121 | const blitz::Array *p; 2122 | blitz::TinyVector idx; 2123 | }; 2124 | 2125 | template 2126 | class BlitzIterator { 2127 | public: 2128 | BlitzIterator() : p(nullptr) { } 2129 | BlitzIterator( 2130 | const blitz::Array *_p, 2131 | const blitz::TinyVector _idx 2132 | ) : p(_p), idx(_idx) { } 2133 | 2134 | typedef T value_type; 2135 | typedef Error_WasNotContainer subiter_type; 2136 | static constexpr bool is_container = false; 2137 | 2138 | // FIXME - it would be nice to also handle one-based arrays 2139 | bool is_end() const { 2140 | return idx[ArrayDim-1] == p->shape()[ArrayDim-1]; 2141 | } 2142 | 2143 | void inc() { 2144 | ++idx[ArrayDim-1]; 2145 | } 2146 | 2147 | value_type deref() const { 2148 | return (*p)(idx); 2149 | } 2150 | 2151 | subiter_type deref_subiter() const { 2152 | static_assert((sizeof(T) == 0), "argument was not a container"); 2153 | throw std::logic_error("static assert should have been triggered by this point"); 2154 | } 2155 | 2156 | private: 2157 | const blitz::Array *p; 2158 | blitz::TinyVector idx; 2159 | }; 2160 | 2161 | template 2162 | class ArrayTraits> : public ArrayTraitsDefaults { 2163 | public: 2164 | static constexpr bool allow_auto_unwrap = false; 2165 | static constexpr size_t depth = ArrayTraits::depth + ArrayDim; 2166 | 2167 | typedef BlitzIterator range_type; 2168 | 2169 | static range_type get_range(const blitz::Array &arg) { 2170 | blitz::TinyVector start_idx; 2171 | start_idx = 0; 2172 | return range_type(&arg, start_idx); 2173 | } 2174 | }; 2175 | 2176 | } // namespace gnuplotio 2177 | #endif // GNUPLOT_BLITZ_SUPPORT_LOADED 2178 | #endif // BZ_BLITZ_H 2179 | 2180 | // }}}2 2181 | 2182 | // {{{2 Armadillo support 2183 | 2184 | // This is outside of the main header guard so that it will be compiled when people do 2185 | // something like this: 2186 | // #include "gnuplot-iostream.h" 2187 | // #include 2188 | // #include "gnuplot-iostream.h" 2189 | // Note that it has its own header guard to avoid double inclusion. 2190 | 2191 | #ifdef ARMA_INCLUDES 2192 | #ifndef GNUPLOT_ARMADILLO_SUPPORT_LOADED 2193 | #define GNUPLOT_ARMADILLO_SUPPORT_LOADED 2194 | namespace gnuplotio { 2195 | 2196 | template static constexpr bool dont_treat_as_stl_container> = true; 2197 | template static constexpr bool dont_treat_as_stl_container> = true; 2198 | template static constexpr bool dont_treat_as_stl_container> = true; 2199 | template static constexpr bool dont_treat_as_stl_container> = true; 2200 | template static constexpr bool dont_treat_as_stl_container> = true; 2201 | 2202 | // {{{3 Cube 2203 | 2204 | template 2205 | class ArrayTraits> : public ArrayTraitsDefaults { 2206 | class SliceRange { 2207 | public: 2208 | SliceRange() : p(nullptr), col(0), slice(0) { } 2209 | explicit SliceRange(const arma::Cube *_p, size_t _row, size_t _col) : 2210 | p(_p), row(_row), col(_col), slice(0) { } 2211 | 2212 | typedef T value_type; 2213 | typedef Error_WasNotContainer subiter_type; 2214 | static constexpr bool is_container = false; 2215 | 2216 | bool is_end() const { return slice == p->n_slices; } 2217 | 2218 | void inc() { ++slice; } 2219 | 2220 | value_type deref() const { 2221 | return (*p)(row, col, slice); 2222 | } 2223 | 2224 | subiter_type deref_subiter() const { 2225 | static_assert((sizeof(T) == 0), "argument was not a container"); 2226 | throw std::logic_error("static assert should have been triggered by this point"); 2227 | } 2228 | 2229 | private: 2230 | const arma::Cube *p; 2231 | size_t row, col, slice; 2232 | }; 2233 | 2234 | class ColRange { 2235 | public: 2236 | ColRange() : p(nullptr), row(0), col(0) { } 2237 | explicit ColRange(const arma::Cube *_p, size_t _row) : 2238 | p(_p), row(_row), col(0) { } 2239 | 2240 | typedef T value_type; 2241 | typedef SliceRange subiter_type; 2242 | static constexpr bool is_container = true; 2243 | 2244 | bool is_end() const { return col == p->n_cols; } 2245 | 2246 | void inc() { ++col; } 2247 | 2248 | value_type deref() const { 2249 | static_assert((sizeof(T) == 0), "can't call deref on an armadillo cube col"); 2250 | throw std::logic_error("static assert should have been triggered by this point"); 2251 | } 2252 | 2253 | subiter_type deref_subiter() const { 2254 | return subiter_type(p, row, col); 2255 | } 2256 | 2257 | private: 2258 | const arma::Cube *p; 2259 | size_t row, col; 2260 | }; 2261 | 2262 | class RowRange { 2263 | public: 2264 | RowRange() : p(nullptr), row(0) { } 2265 | explicit RowRange(const arma::Cube *_p) : p(_p), row(0) { } 2266 | 2267 | typedef T value_type; 2268 | typedef ColRange subiter_type; 2269 | static constexpr bool is_container = true; 2270 | 2271 | bool is_end() const { return row == p->n_rows; } 2272 | 2273 | void inc() { ++row; } 2274 | 2275 | value_type deref() const { 2276 | static_assert((sizeof(T) == 0), "can't call deref on an armadillo cube row"); 2277 | throw std::logic_error("static assert should have been triggered by this point"); 2278 | } 2279 | 2280 | subiter_type deref_subiter() const { 2281 | return subiter_type(p, row); 2282 | } 2283 | 2284 | private: 2285 | const arma::Cube *p; 2286 | size_t row; 2287 | }; 2288 | 2289 | public: 2290 | static constexpr bool allow_auto_unwrap = false; 2291 | static constexpr size_t depth = ArrayTraits::depth + 3; 2292 | 2293 | typedef RowRange range_type; 2294 | 2295 | static range_type get_range(const arma::Cube &arg) { 2296 | //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; 2297 | return range_type(&arg); 2298 | } 2299 | }; 2300 | 2301 | // }}}3 2302 | 2303 | // {{{3 Mat and Field 2304 | 2305 | template 2306 | class ArrayTraits_ArmaMatOrField : public ArrayTraitsDefaults { 2307 | class ColRange { 2308 | public: 2309 | ColRange() : p(nullptr), row(0), col(0) { } 2310 | explicit ColRange(const RF *_p, size_t _row) : 2311 | p(_p), row(_row), col(0) { } 2312 | 2313 | typedef T value_type; 2314 | typedef Error_WasNotContainer subiter_type; 2315 | static constexpr bool is_container = false; 2316 | 2317 | bool is_end() const { return col == p->n_cols; } 2318 | 2319 | void inc() { ++col; } 2320 | 2321 | value_type deref() const { 2322 | return (*p)(row, col); 2323 | } 2324 | 2325 | subiter_type deref_subiter() const { 2326 | static_assert((sizeof(T) == 0), "argument was not a container"); 2327 | throw std::logic_error("static assert should have been triggered by this point"); 2328 | } 2329 | 2330 | private: 2331 | const RF *p; 2332 | size_t row, col; 2333 | }; 2334 | 2335 | class RowRange { 2336 | public: 2337 | RowRange() : p(nullptr), row(0) { } 2338 | explicit RowRange(const RF *_p) : p(_p), row(0) { } 2339 | 2340 | typedef T value_type; 2341 | typedef ColRange subiter_type; 2342 | static constexpr bool is_container = true; 2343 | 2344 | bool is_end() const { return row == p->n_rows; } 2345 | 2346 | void inc() { ++row; } 2347 | 2348 | value_type deref() const { 2349 | static_assert((sizeof(T) == 0), "can't call deref on an armadillo matrix row"); 2350 | throw std::logic_error("static assert should have been triggered by this point"); 2351 | } 2352 | 2353 | subiter_type deref_subiter() const { 2354 | return subiter_type(p, row); 2355 | } 2356 | 2357 | private: 2358 | const RF *p; 2359 | size_t row; 2360 | }; 2361 | 2362 | public: 2363 | static constexpr bool allow_auto_unwrap = false; 2364 | static constexpr size_t depth = ArrayTraits::depth + 2; 2365 | 2366 | typedef RowRange range_type; 2367 | 2368 | static range_type get_range(const RF &arg) { 2369 | //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; 2370 | return range_type(&arg); 2371 | } 2372 | }; 2373 | 2374 | template 2375 | class ArrayTraits> : public ArrayTraits_ArmaMatOrField, T> { }; 2376 | 2377 | template 2378 | class ArrayTraits> : public ArrayTraits_ArmaMatOrField, T> { }; 2379 | 2380 | // }}}3 2381 | 2382 | // {{{3 Row 2383 | 2384 | template 2385 | class ArrayTraits> : public ArrayTraitsDefaults { 2386 | public: 2387 | static constexpr bool allow_auto_unwrap = false; 2388 | 2389 | typedef IteratorRange::const_iterator, T> range_type; 2390 | 2391 | static range_type get_range(const arma::Row &arg) { 2392 | //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; 2393 | return range_type(arg.begin(), arg.end()); 2394 | } 2395 | }; 2396 | 2397 | // }}}3 2398 | 2399 | // {{{3 Col 2400 | 2401 | template 2402 | class ArrayTraits> : public ArrayTraitsDefaults { 2403 | public: 2404 | static constexpr bool allow_auto_unwrap = false; 2405 | 2406 | typedef IteratorRange::const_iterator, T> range_type; 2407 | 2408 | static range_type get_range(const arma::Col &arg) { 2409 | //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; 2410 | return range_type(arg.begin(), arg.end()); 2411 | } 2412 | }; 2413 | 2414 | // }}}3 2415 | 2416 | } // namespace gnuplotio 2417 | #endif // GNUPLOT_ARMADILLO_SUPPORT_LOADED 2418 | #endif // ARMA_INCLUDES 2419 | 2420 | // }}}2 2421 | 2422 | // {{{2 Eigen support 2423 | 2424 | // This is outside of the main header guard so that it will be compiled when people do 2425 | // something like this: 2426 | // #include "gnuplot-iostream.h" 2427 | // #include 2428 | // #include "gnuplot-iostream.h" 2429 | // Note that it has its own header guard to avoid double inclusion. 2430 | 2431 | #ifdef EIGEN_CORE_H 2432 | #ifndef GNUPLOT_EIGEN_SUPPORT_LOADED 2433 | #define GNUPLOT_EIGEN_SUPPORT_LOADED 2434 | namespace gnuplotio { 2435 | 2436 | template 2437 | static constexpr bool is_eigen_matrix = false; 2438 | 2439 | template 2440 | static constexpr bool is_eigen_matrix, T>>> = true; 2442 | 2443 | static_assert( is_eigen_matrix); 2444 | static_assert(!is_eigen_matrix); 2445 | 2446 | template 2447 | static constexpr bool dont_treat_as_stl_container>> = true; 2448 | 2449 | static_assert(dont_treat_as_stl_container); 2450 | 2451 | // {{{3 Matrix 2452 | 2453 | template 2454 | class ArrayTraits_Eigen1D : public ArrayTraitsDefaults { 2455 | class IdxRange { 2456 | public: 2457 | IdxRange() : p(nullptr), idx(0) { } 2458 | explicit IdxRange(const RF *_p) : 2459 | p(_p), idx(0) { } 2460 | 2461 | using value_type = typename RF::value_type; 2462 | typedef Error_WasNotContainer subiter_type; 2463 | static constexpr bool is_container = false; 2464 | 2465 | bool is_end() const { return idx == p->size(); } 2466 | 2467 | void inc() { ++idx; } 2468 | 2469 | value_type deref() const { 2470 | return (*p)(idx); 2471 | } 2472 | 2473 | subiter_type deref_subiter() const { 2474 | static_assert((sizeof(value_type) == 0), "argument was not a container"); 2475 | throw std::logic_error("static assert should have been triggered by this point"); 2476 | } 2477 | 2478 | private: 2479 | const RF *p; 2480 | Eigen::Index idx; 2481 | }; 2482 | 2483 | public: 2484 | static constexpr bool allow_auto_unwrap = false; 2485 | static constexpr size_t depth = ArrayTraits::depth + 1; 2486 | 2487 | typedef IdxRange range_type; 2488 | 2489 | static range_type get_range(const RF &arg) { 2490 | //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; 2491 | return range_type(&arg); 2492 | } 2493 | }; 2494 | 2495 | template 2496 | class ArrayTraits_Eigen2D : public ArrayTraitsDefaults { 2497 | class ColRange { 2498 | public: 2499 | ColRange() : p(nullptr), row(0), col(0) { } 2500 | explicit ColRange(const RF *_p, Eigen::Index _row) : 2501 | p(_p), row(_row), col(0) { } 2502 | 2503 | using value_type = typename RF::value_type; 2504 | typedef Error_WasNotContainer subiter_type; 2505 | static constexpr bool is_container = false; 2506 | 2507 | bool is_end() const { return col == p->cols(); } 2508 | 2509 | void inc() { ++col; } 2510 | 2511 | value_type deref() const { 2512 | return (*p)(row, col); 2513 | } 2514 | 2515 | subiter_type deref_subiter() const { 2516 | static_assert((sizeof(value_type) == 0), "argument was not a container"); 2517 | throw std::logic_error("static assert should have been triggered by this point"); 2518 | } 2519 | 2520 | private: 2521 | const RF *p; 2522 | Eigen::Index row, col; 2523 | }; 2524 | 2525 | class RowRange { 2526 | public: 2527 | RowRange() : p(nullptr), row(0) { } 2528 | explicit RowRange(const RF *_p) : p(_p), row(0) { } 2529 | 2530 | using value_type = typename RF::value_type; 2531 | typedef ColRange subiter_type; 2532 | static constexpr bool is_container = true; 2533 | 2534 | bool is_end() const { return row == p->rows(); } 2535 | 2536 | void inc() { ++row; } 2537 | 2538 | value_type deref() const { 2539 | static_assert((sizeof(value_type) == 0), "can't call deref on an eigen matrix row"); 2540 | throw std::logic_error("static assert should have been triggered by this point"); 2541 | } 2542 | 2543 | subiter_type deref_subiter() const { 2544 | return subiter_type(p, row); 2545 | } 2546 | 2547 | private: 2548 | const RF *p; 2549 | Eigen::Index row; 2550 | }; 2551 | 2552 | public: 2553 | static constexpr bool allow_auto_unwrap = false; 2554 | static constexpr size_t depth = ArrayTraits::depth + 2; 2555 | 2556 | typedef RowRange range_type; 2557 | 2558 | static range_type get_range(const RF &arg) { 2559 | //std::cout << arg.n_elem << "," << arg.n_rows << "," << arg.n_cols << std::endl; 2560 | return range_type(&arg); 2561 | } 2562 | }; 2563 | 2564 | template 2565 | class ArrayTraits>> : 2566 | public std::conditional_t< 2567 | T::RowsAtCompileTime == 1 || T::ColsAtCompileTime == 1, 2568 | ArrayTraits_Eigen1D, 2569 | ArrayTraits_Eigen2D 2570 | > { }; 2571 | 2572 | // }}}3 2573 | 2574 | } // namespace gnuplotio 2575 | #endif // GNUPLOT_EIGEN_SUPPORT_LOADED 2576 | #endif // EIGEN_CORE_H 2577 | 2578 | // }}}2 2579 | 2580 | // }}}1 2581 | --------------------------------------------------------------------------------