Skip to content

New api #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- stage: UnitTests
os: linux
sudo: false
script: make && make test
script: make check
addons:
apt:
packages:
Expand Down
19 changes: 10 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@

CXX=g++
CXXFLAGS=-g -std=c++11
# temporary for pnappa
CXX=clang++
CXXFLAGS=-g -std=c++11 -Wall -pedantic
LIBS=-lpthread

.PHONY: all clean
all: demo test
.PHONY: all clean check test_programs
all: demo check

clean:
rm -fv demo test coverage

demo: demo.cpp subprocess.hpp
$(CXX) $(CXXFLAGS) demo.cpp -o demo $(LIBS)

check: test test_programs
valgrind ./test

test: test.cpp subprocess.hpp
$(CXX) $(CXXFLAGS) test.cpp -o test $(LIBS)
# run the testsuite (-s makes it nice and verbose)
# XXX: should we make this verbose?
./test -s
valgrind ./test

coverage: test.cpp subprocess.hpp
$(CXX) $(CXXFLAGS) -fprofile-arcs -ftest-coverage test.cpp -o coverage $(LIBS)
.codecov/run_coverage.sh

test_programs:
$(MAKE) -C test_programs/
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,17 @@ Unfortunately, I am not as familiar with Windows to write code for it, if you wa
### TODO: ....detail what each of the functions _should_ be used for.

```C++
old API (current for now):
int execute(const std::string& commandPath, const std::vector<std::string>& commandArgs, std::list<std::string>& stringInput, std::function<void(std::string)> lambda)
std::vector<std::string> checkOutput(const std::string& commandPath, const std::vector<std::string>& commandArgs, std::list<std::string>& stringInput, int& status)
std::future<int> async(const std::string commandPath, const std::vector<std::string> commandArgs, std::list<std::string> stringInput, std::function<void(std::string)> lambda)
// Iterable can be stuff that can be iterated over (std::vector, etc.), all arguments other than commandPath are optional!
int execute(const std::string& commandPath, const Iterable& commandArgs, const Iterable& stdinInput, std::function<void(std::string)> lambda, const Iterable& environmentVariables);
// accepts iterators, the argument iterators are mandatory, the rest are optional
int execute(const std::string& commandPath, Iterator argsBegin, Iterator argsEnd, Iterator stdinBegin, Iterator stdinEnd, std::function<void(std::string)> lambda, Iterator envStart, Iterator envEnd);

// ctor for ProcessStream class
class ProcessStream(const std::string& commandPath, const std::vector<std::string>& commandArgs, std::list<std::string>& stringInput)
// Iterable can be stuff that can be iterated over (std::vector, etc.), all arguments other than commandPath are optional!
std::vector<std::string> check_output(const std::string& commandPath, const Iterable& commandArgs, const Iterable& stdinInput, const Iterable& environmentVariables);
// accepts iterators, the argument iterators are mandatory, the rest are optional
std::vector<std::string> check_output(const std::string& commandPath, Iterator argsBegin, Iterator argsEnd, Iterator stdinBegin, Iterator stdinEnd, Iterator envStart, Iterator envEnd);

// we currently don't have asynchronous, daemon spawning, or iterable interaction yet. coming soon(TM)
```

# License
Expand All @@ -88,8 +91,7 @@ This is dual-licensed under a MIT and GPLv3 license - so FOSS lovers can use it,
I don't know too much about licenses, so if I missed anything, please let me know.

# Future Features
Some stuff that I haven't written yet, but I wanna:
- [X] Output streaming. Provide an iterator to allow iteration over the output lines, such that we don't have to load all in memory at once.
- [ ] Thread-safe async lambda interactions. Provide a method to launch a process in async, but still allow writing to the list of stdin without a race condition.
- [ ] A ping-ponging interface. This should allow incrementally providing stdin, then invoking the functor if output is emitted. Note that will likely not be possible if there's not performed asynchronously, or without using select. Using select is a bit annoying, because how do we differentiate between a command taking a while and it providing no input?
- [ ] Provide a way to set environment variables (i can pretty easily do it via using `execvpe`, but what should the API look like?)
Some stuff that I haven't written yet, but I wanna (see [this issue for a more in depth explanation of each](https://github.com/pnappa/subprocesscpp/issues/3)):
- Asynchronous execution
- Daemon spawning helpers (execute process and disown it, only need to consider where stdout should go).
- Interactive processes (manually feed stdin and retrieve stdout)
34 changes: 19 additions & 15 deletions demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ void echoString(std::string in) {
}

int main() {
// execute bc and pass it some equations
std::vector<std::string> args = {};
std::list<std::string> inputs = {"1+1\n", "2^333\n", "32-32\n"};
std::vector<std::string> env = {};
subprocess::execute("/usr/bin/bc", args.begin(), args.end(), inputs.begin(), inputs.end(), echoString, env.begin(), env.end());

// execute bc and pass it some equations
inputs = {"1+1\n", "2^333\n", "32-32\n"};
subprocess::execute("/usr/bin/bc", {}, inputs, echoString);

// grep over some inputs
Expand All @@ -19,34 +24,33 @@ int main() {

// execute a process and extract all lines outputted
inputs.clear(); // provide no stdin
int status;
std::vector<std::string> vec = subprocess::checkOutput("/usr/bin/time", {"sleep", "1"}, inputs, status);
std::vector<std::string> vec = subprocess::check_output("/usr/bin/time", {"sleep", "1"}, inputs);
//std::vector<std::string> vec = subprocess::check_output("/usr/bin/time", {"sleep", "1"}, inputs, status);
for (const std::string& s : vec) {
std::cout << "output: " << s << '\t';
std::cout << "line length:" << s.length() << std::endl;
}
std::cout << "process finished with an exit code of: " << status << std::endl;

// execute sleep asynchronously, and block when needing the output
std::future<int> futureStatus = subprocess::async("/bin/sleep", {"3"}, inputs, [](std::string) {});
// if this wasn't async, this line wouldn't print until after the process finished!
std::cout << "executing sleep..." << std::endl;
std::cout << "sleep executed with exit status: " << futureStatus.get() << std::endl;
//// execute sleep asynchronously, and block when needing the output
//std::future<int> futureStatus = subprocess::async("/bin/sleep", {"3"}, inputs, [](std::string) {});
//// if this wasn't async, this line wouldn't print until after the process finished!
//std::cout << "executing sleep..." << std::endl;
//std::cout << "sleep executed with exit status: " << futureStatus.get() << std::endl;

// simulate pipes between programs: lets launch cat to provide input into a grep process!
// Note! This will read all output into a single vector, then provide this as input into the second
// process
// If you want a function that isn't as memory intensive, consider streamOutput, which provides an
// iterator interface
inputs = {"12232\n", "hello, world\n", "Hello, world\n", "line: Hello, world!\n"};
vec = subprocess::checkOutput("/bin/cat", {}, inputs, status);
vec = subprocess::check_output("/bin/cat", {}, inputs);
inputs = std::list<std::string>(vec.begin(), vec.end());
subprocess::execute("/bin/grep", {"-i", "^Hello, world$"}, inputs, echoString);

// stream output from a process
inputs = {"12232\n", "hello, world\n", "Hello, world\n", "line: Hello, world!\n"};
subprocess::ProcessStream ps("/bin/grep", {"-i", "^Hello, world$"}, inputs);
for (std::string out : ps) {
std::cout << "received: " << out;
}
//inputs = {"12232\n", "hello, world\n", "Hello, world\n", "line: Hello, world!\n"};
//subprocess::ProcessStream ps("/bin/grep", {"-i", "^Hello, world$"}, inputs);
//for (std::string out : ps) {
//std::cout << "received: " << out;
//}
}
28 changes: 28 additions & 0 deletions next_prime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Demonstration of recursive subprocess pipes
*
* This program find the next prime for an input number.
*/


#include "subprocess.hpp"

int main() {
subprocess::Process incrementer("./test_programs/increment", {}, [&](std::string s) { std::cout << s; });
subprocess::Process prime_checker("./test_programs/tee_if_nonprime");

incrementer.pipe_to(prime_checker);
prime_checker.pipe_to(incrementer);
prime_checker.output_to_file("prime.out");

incrementer.start();
incrementer << "33\n";

incrementer.force_output();
prime_checker.force_output();
incrementer.force_output();
prime_checker.force_output();
incrementer.force_output();
prime_checker.force_output();
}

Loading