Boost.Process article: Tutorial and request for comments

I've written an article about Boost.Process: http://www.highscore.de/cpp/process/ I've also fixed a few bugs in Boost.Process (which is now 0.31): http://www.highscore.de/boost/process.zip And the latest source code can also be found in the sandbox: https://svn.boost.org/svn/boost/sandbox/process/ For developers not familar with Boost.Process the article is a tutorial to get more easily started. For developers familiar with Boost.Process it is a request for comments. I've been looking at the big picture finally trying to find out where and how Boost.Process should be changed and in which direction it should move to become a complete library one day. As not everyone will read the entire article here are my thoughts: 1) Boost.Process is based on a few concepts like 'process' and 'context'. While those make sense there are other concepts where I wonder if they should be dropped: * 'executable' is the executable name which in practice must be a std::string. I think the original idea was to support numerous string types. This leads to the question how to support different Unicode encodings which I think is still unanswered in the Boost community. Other libraries, eg. Boost.Interprocess, simply stick to one string type (typically std::string). * 'handle' is the underlying native file handle of a stream (file descriptor on POSIX systems, handle on Windows). The concept is based on a class in the detail namespace of Boost.Process. There is just one class and it's not meant to be used by users of the library either. Thus I'm not sure what's the benefit of having an explicit 'handle' concept at all. 2) Environment variables must be sorted on Windows but not on POSIX systems. On Windows the type environment could be a typedef for std::map and on POSIX systems for std::unordered_map. Currently it's always a typedef for std::map. Is that ok? Should it be changed? Should a macro be provided in case developers want to use different typedefs? 3) Currently Boost.Process is really only useful for creating child processes. It's not possible for example to detect and iterate over other processes on a system. I guess Boost.Process should be enhanced here. Then one day the well-known utility 'ps' on POSIX systems could be implemented in Boost C++. Any comments? 4) When a process is created the standard streams (stdin, stdout and stderr) are closed by default. Is this a good choice? If it isn't what should happen otherwise? Inherting standard streams (which is the default behavior with fork on POSIX systems)? 5) Boost.Process requires to specify an absolute path to an executable which should be started. There is a helper function which returns the absolute path for an executable name (it uses the environment variable PATH). The question is if this should be done automatically if no absolute path is given? 6) When a stream of a child process is captured Boost.Process automatically creates a pipe. As pipes don't support asynchronous operations on Windows a named pipe is required (called FIFO on POSIX systems). Currently a macro must be defined to make Boost.Process use a named pipe on Windows. It might make more sense to enable developers to choose either a pipe or a FIFO explicitly - not only on Windows but on all platforms. Any comments? 7) It's possible to capture and access streams of a child process. Those streams are derived from std::istream and std::ostream and thus only support blocking I/O operations. Currently the underlying handle has to be fetched to initialize a generic I/O object from Boost.Asio to use asynchronous I/O operations. How should the library be changed to make it easier to use asynchronous I/O? 8) There is a method wait() which makes it possible to wait for another process to exit. Currently wait() is provided by the class child though. Shouldn't it really be defined by the class process (so you can wait not only for child processes to exit)? 9) As wait() blocks an asynchronous alternative should be provided. 10) The Windows-specific classes all start with win32_ (eg. win32_child). Wouldn't it make more sense to use windows_ instead (like windows_child) or a windows namespace? First other libraries use windows_ (see Boost.Interprocess). Second there should be no reason why a class like win32_child can't be used on a 64-bit Windows. Boris

Boris Schaeling wrote:
I've written an article about Boost.Process: http://www.highscore.de/cpp/process/
Hi Boris, I've quickly looked at your article; here is some feedback: First a general comment about how I would prefer that portable system API wrappers are implemented. No doubt other would have different views. Ideally, I'd like to see separate thin wrappers for Windows and POSIX that just "C++-ify" those APIs. Then on top of that implement a portability layer. My reason is this: I rarely if ever care about Windows and I already know how the POSIX APIs work (i.e. what the functions are called and what the semantics are). So please don't hide the bottom level thin wrappers that you must have as an implementation detail. Re the environment: I'm not enthusiastic that you have copied the environment into your own data structure. Why can't your environment type just wrap getenv() and setenv()? i.e. class environment { struct env_var_proxy { const char* varname; env_var_proxy(const char* varname_): varname(varname_) {} operator const char*() const { return getenv(varname); } void operator=(const char* newval) { setenv(varname,newval,1); } } public: env_var_proxy operator[](const char* varname) { return env_var_proxy(varname); } void erase(const char* varname) { unsetenv(varname); } }; Re starting processes: This example code: std::string exec = find_executable_in_path("notepad.exe"); std::vector<std::string> args = boost::assign::list_of("notepad.exe"); context ctx; ctx.environment = self::get_environment(); launch(exec, args, ctx); Is actually more verbose than directly calling the POSIX functions: int child_pid = fork(); if (child_pid == -1) throw "fork failed"; if (child_pid == 0) { execlp("notepad.exe","notepad.exe",NULL); _exit(1); } Why don't you boil it down to: launch_process("notepad.exe"); ? You write:
it is very important to refer to the executable with an absolute path - on all platforms including POSIX systems
Why do you say that? It's not clear to me if your terminate() calls wait() or not. Beware of processes that ignore SIGCHLD.
3) Currently Boost.Process is really only useful for creating child processes. It's not possible for example to detect and iterate over other processes on a system. I guess Boost.Process should be enhanced here. Then one day the well-known utility 'ps' on POSIX systems could be implemented in Boost C++. Any comments?
What is the use case for this?
4) When a process is created the standard streams (stdin, stdout and stderr) are closed by default. Is this a good choice?
No, IMHO.
If it isn't what should happen otherwise? Inherting standard streams (which is the default behavior with fork on POSIX systems)?
Yes.
5) Boost.Process requires to specify an absolute path to an executable which should be started. There is a helper function which returns the absolute path for an executable name (it uses the environment variable PATH). The question is if this should be done automatically if no absolute path is given?
exec*p() does that for you.
8) There is a method wait() which makes it possible to wait for another process to exit. Currently wait() is provided by the class child though. Shouldn't it really be defined by the class process (so you can wait not only for child processes to exit)?
Are you certain that wait() can be used to monitor non-child processes? I don't believe that it can.
9) As wait() blocks an asynchronous alternative should be provided.
Yes. When I've done this sort of thing I've used a SIGCHLD signal handler that pokes the main event loop; that loop calls a non-blocking wait(). It has to be non-blocking because you may get only one SIGCHLD for multiple children, so the event loop needs to keep calling wait() until there is nothing to wait for. Regards, Phil.

On Tue, 21 Apr 2009 20:15:11 +0200, Phil Endecott <spam_from_boost_dev@chezphil.org> wrote:
[...]First a general comment about how I would prefer that portable system API wrappers are implemented. No doubt other would have different views. Ideally, I'd like to see separate thin wrappers for Windows and POSIX that just "C++-ify" those APIs. Then on top of that implement a portability layer. My reason is this: I rarely if ever care about Windows and I already know how the POSIX APIs work (i.e. what the functions are called and what the semantics are). So please don't hide the bottom level thin wrappers that you must have as an implementation detail.
I see. It reminds me a bit of the free-standing functions in Boost.Filesystem which I think simply call POSIX and Windows API functions? I'm not sure though if I can work on such a layer - I hardly have time to work on the upper layer. :-/
Re the environment: I'm not enthusiastic that you have copied the environment into your own data structure. Why can't your environment type just wrap getenv() and setenv()? i.e.
Before I answer your questions I should note that I did not design Boost.Process. I went through the entire code last year to make sure that what we have works (fixing countless bugs and testing the library with numerous compilers and platforms). I try to answer your questions as far as I understand the rationale of the design.
class environment { struct env_var_proxy { const char* varname; env_var_proxy(const char* varname_): varname(varname_) {} operator const char*() const { return getenv(varname); } void operator=(const char* newval) { setenv(varname,newval,1); } }
The type environment is also used to setup a new environment for a child process, eg.: context ctx; ctx.environment.insert(environment::value_type("PATH", "/newpath")); launch(..., ctx);
[...]This example code:
std::string exec = find_executable_in_path("notepad.exe"); std::vector<std::string> args = boost::assign::list_of("notepad.exe"); context ctx; ctx.environment = self::get_environment(); launch(exec, args, ctx);
Is actually more verbose than directly calling the POSIX functions:
int child_pid = fork(); if (child_pid == -1) throw "fork failed"; if (child_pid == 0) { execlp("notepad.exe","notepad.exe",NULL); _exit(1); }
Why don't you boil it down to:
launch_process("notepad.exe");
Currently the environment variables of the current process are not automatically inherited by a child process. I agree that it should be easier to launch a new process. But then environment variables should probably be inherited by default?
You write:
it is very important to refer to the executable with an absolute path - on all platforms including POSIX systems
Why do you say that?
It's a requirement of Boost.Process. It doesn't need to be an absolute path but can also be a relative path of course. What's not guaranteed though is that an executable is automatically found when only a filename is given. There is a function which searches for an executable and returns an absolute path. Would you like to see Boost.Process search for an executable automatically?
It's not clear to me if your terminate() calls wait() or not. Beware of processes that ignore SIGCHLD.
Currently terminate() only calls kill().
3) Currently Boost.Process is really only useful for creating child processes. It's not possible for example to detect and iterate over other processes on a system. I guess Boost.Process should be enhanced here. Then one day the well-known utility 'ps' on POSIX systems could be implemented in Boost C++. Any comments?
What is the use case for this?
I asked myself the other way round: Why should a library for process management only support child processes? But I'd agree that accessing other processes has a lower priority.
[...]
8) There is a method wait() which makes it possible to wait for another process to exit. Currently wait() is provided by the class child though. Shouldn't it really be defined by the class process (so you can wait not only for child processes to exit)?
Are you certain that wait() can be used to monitor non-child processes? I don't believe that it can.
Hm, on POSIX systems it doesn't work but on Windows it works. Another problem to think about. Boris

Boris Schaeling wrote:
On Tue, 21 Apr 2009 20:15:11 +0200, Phil Endecott wrote:
Re the environment: I'm not enthusiastic that you have copied the environment into your own data structure. Why can't your environment type just wrap getenv() and setenv()? i.e.
The type environment is also used to setup a new environment for a child process, eg.:
context ctx; ctx.environment.insert(environment::value_type("PATH", "/newpath")); launch(..., ctx);
Typically I would write: fork(); setenv("PATH","/newpath"); exec(); So the new child process gets the parent's environment with any new variables added or changed. This seems a lot simpler than copying the whole environment.
Why don't you boil it down to:
launch_process("notepad.exe");
Currently the environment variables of the current process are not automatically inherited by a child process. I agree that it should be easier to launch a new process. But then environment variables should probably be inherited by default?
The environment is inherited across fork() and all those versions of exec() that don't explicitly set a new environment.
it is very important to refer to the executable with an absolute path - on all platforms including POSIX systems
Why do you say that?
It's a requirement of Boost.Process. It doesn't need to be an absolute path but can also be a relative path of course.
Right, so it doesn't need to be an absolute path.
What's not guaranteed though is that an executable is automatically found when only a filename is given. There is a function which searches for an executable and returns an absolute path. Would you like to see Boost.Process search for an executable automatically?
No, I'd like to see it use one of the versions of exec() that does this for you. Regards, Phil.

On Sat, 25 Apr 2009 16:13:16 +0200, Phil Endecott <spam_from_boost_dev@chezphil.org> wrote:
[...]Typically I would write:
fork(); setenv("PATH","/newpath"); exec();
So the new child process gets the parent's environment with any new variables added or changed. This seems a lot simpler than copying the whole environment.
I also find myself copying the environment variables all the time (and if it's only for shared libraries to be found by the child process). I'll change the default behavior.
[...]The environment is inherited across fork() and all those versions of exec() that don't explicitly set a new environment.
Yes. But I'm really trying to find out first what behavior should be guaranteed on all platforms.
[...]
It's a requirement of Boost.Process. It doesn't need to be an absolute path but can also be a relative path of course.
Right, so it doesn't need to be an absolute path.
You are right, I should have called it fully specified path. Boris

Hi, ----- Original Message ----- From: "Phil Endecott" <spam_from_boost_dev@chezphil.org> To: <boost@lists.boost.org> Sent: Saturday, April 25, 2009 4:13 PM Subject: Re: [boost] Boost.Process article: Tutorial and request for comments
Boris Schaeling wrote:
On Tue, 21 Apr 2009 20:15:11 +0200, Phil Endecott wrote:
Re the environment: I'm not enthusiastic that you have copied the environment into your own data structure. Why can't your environment type just wrap getenv() and setenv()? i.e.
The type environment is also used to setup a new environment for a child process, eg.:
context ctx; ctx.environment.insert(environment::value_type("PATH", "/newpath")); launch(..., ctx);
Typically I would write:
fork(); setenv("PATH","/newpath"); exec();
So the new child process gets the parent's environment with any new variables added or changed. This seems a lot simpler than copying the whole environment.
Yes, this seems much more efficient. So the Process library should use the environement filed as a list of environment variable to set on the child process.
Why don't you boil it down to:
launch_process("notepad.exe");
Currently the environment variables of the current process are not automatically inherited by a child process. I agree that it should be easier to launch a new process. But then environment variables should probably be inherited by default?
The environment is inherited across fork() and all those versions of exec() that don't explicitly set a new environment.
I agree that the default must inherit the environement variables, no need to state this explictly, not need to copy it.
it is very important to refer to the executable with an absolute path - on all platforms including POSIX systems
Why do you say that?
It's a requirement of Boost.Process. It doesn't need to be an absolute path but can also be a relative path of course.
Right, so it doesn't need to be an absolute path.
What's not guaranteed though is that an executable is automatically found when only a filename is given. There is a function which searches for an executable and returns an absolute path. Would you like to see Boost.Process search for an executable automatically?
No, I'd like to see it use one of the versions of exec() that does this for you.
I agree again. Best, Vicente

Hi, Excellent idea to write this article. I have not used the library neither read the documentation deply. I have however some suggestion about the library design. I don't see the need to separate between several specific platform classes. A program using the Proces library will run only with one platform available, so classes as process and child could include conditionaly the specific features. The current design has the advantage of making explicit the non portable parts, but how do you provide features common to two platforms that are not available to a third one? In addition the risk to make specific classes would be that the underlying concepts are too close to the concrete feautures provided by one platform. I would prefer to have a specific define for each non portable feature. I will organize the specific classes on a specific platform namespace common for the portable part namespace process { namespace common { class process; class child; } posix for the posix based OS #ifdef BOOST_PLATFORM_POSIX // or something like namespace process { namespace posix { class process : public common::process{...}; class child: public common::child{...}; } } #endif window for the micosoft OS based platforms #ifdef BOOST_PLATFORM_WINDOWS // or something like namespace process { namespace windows { class process : public common::process{...}; class child: public common::child{...}; } } #endif // other platforms and have typedef or a using directive at the namespace process level for each one of the available classes as namespace process { #if defined(__POSIX__) // or something like typedef posix::process process; // or using posix::process; #elsif defined() // other patforms ... #else typedef common::process process; or // using common::process; #endif } When the user use a non portable feature on a portable programm it will need to check conditionally for the non portable feature as #ifdef BOOST_PROCESS_HAS_SPECIFC_FEATURE_1 // ... #else // ... #endif Your proposal about iterating over all the process working on a machine is a must to have. The issue respect to the map environement could be abstracted by defining a specific class that manage the synchronization between the settings and the collection. Even if the following could be done in a higher library (Shell), I would like to think about the context concept as a shell which can be nested and have a stack of shells. This stack can be stored on a thread specific context, so when a new process is created it will inherit from the current context. In the shell context a command could find a place. For example, shell::cd("/tmp"); will change the current directory to "/tmp" on the top context of the stack.. If the command fails it can be recovered Commands could be composed using pipe "|", shell::cat("/tmp.file.txt") | shell::wc(); redirection ">" shell::cat("/tmp.file.txt") | shell::wc() > str; tee shell::cat("/tmp.file.txt") | shell::grep () | shell::tee("file"); conditional concatenation "&", shell::cd("/tmp") & shell::ls(); which executes ls only if cd /tmp succeeds. Portability is not only related to the platform features, but also about the features provided by specific programs. I would like to see an example of how the user would write portable code when he wants to execute a program that has different syntax on different platforms but provides the same feature, e.g the equivalent of "ping" on solaris id "ping -c 1" on linux. Last some minor questions: What about renaming handle by native_handle? What is wrong on using the Boost.Filesystem library to idientify executables? Why the do we need to duplicate the name of the executables as an argument? Respect to relative or absolute path of executables,why not let the undelying platform to handle with this? HTH, Vicente ----- Original Message ----- From: "Boris Schaeling" <boris@highscore.de> To: <boost@lists.boost.org> Cc: <boost-users@lists.boost.org> Sent: Tuesday, April 21, 2009 2:01 PM Subject: [boost] Boost.Process article: Tutorial and request for comments
I've written an article about Boost.Process: http://www.highscore.de/cpp/process/
I've also fixed a few bugs in Boost.Process (which is now 0.31): http://www.highscore.de/boost/process.zip
And the latest source code can also be found in the sandbox: https://svn.boost.org/svn/boost/sandbox/process/
For developers not familar with Boost.Process the article is a tutorial to get more easily started. For developers familiar with Boost.Process it is a request for comments.
I've been looking at the big picture finally trying to find out where and how Boost.Process should be changed and in which direction it should move to become a complete library one day. As not everyone will read the entire article here are my thoughts:
1) Boost.Process is based on a few concepts like 'process' and 'context'. While those make sense there are other concepts where I wonder if they should be dropped:
* 'executable' is the executable name which in practice must be a std::string. I think the original idea was to support numerous string types. This leads to the question how to support different Unicode encodings which I think is still unanswered in the Boost community. Other libraries, eg. Boost.Interprocess, simply stick to one string type (typically std::string).
* 'handle' is the underlying native file handle of a stream (file descriptor on POSIX systems, handle on Windows). The concept is based on a class in the detail namespace of Boost.Process. There is just one class and it's not meant to be used by users of the library either. Thus I'm not sure what's the benefit of having an explicit 'handle' concept at all.
2) Environment variables must be sorted on Windows but not on POSIX systems. On Windows the type environment could be a typedef for std::map and on POSIX systems for std::unordered_map. Currently it's always a typedef for std::map. Is that ok? Should it be changed? Should a macro be provided in case developers want to use different typedefs?
3) Currently Boost.Process is really only useful for creating child processes. It's not possible for example to detect and iterate over other processes on a system. I guess Boost.Process should be enhanced here. Then one day the well-known utility 'ps' on POSIX systems could be implemented in Boost C++. Any comments?
4) When a process is created the standard streams (stdin, stdout and stderr) are closed by default. Is this a good choice? If it isn't what should happen otherwise? Inherting standard streams (which is the default behavior with fork on POSIX systems)?
5) Boost.Process requires to specify an absolute path to an executable which should be started. There is a helper function which returns the absolute path for an executable name (it uses the environment variable PATH). The question is if this should be done automatically if no absolute path is given?
6) When a stream of a child process is captured Boost.Process automatically creates a pipe. As pipes don't support asynchronous operations on Windows a named pipe is required (called FIFO on POSIX systems). Currently a macro must be defined to make Boost.Process use a named pipe on Windows. It might make more sense to enable developers to choose either a pipe or a FIFO explicitly - not only on Windows but on all platforms. Any comments?
7) It's possible to capture and access streams of a child process. Those streams are derived from std::istream and std::ostream and thus only support blocking I/O operations. Currently the underlying handle has to be fetched to initialize a generic I/O object from Boost.Asio to use asynchronous I/O operations. How should the library be changed to make it easier to use asynchronous I/O?
8) There is a method wait() which makes it possible to wait for another process to exit. Currently wait() is provided by the class child though. Shouldn't it really be defined by the class process (so you can wait not only for child processes to exit)?
9) As wait() blocks an asynchronous alternative should be provided.
10) The Windows-specific classes all start with win32_ (eg. win32_child). Wouldn't it make more sense to use windows_ instead (like windows_child) or a windows namespace? First other libraries use windows_ (see Boost.Interprocess). Second there should be no reason why a class like win32_child can't be used on a 64-bit Windows.
Boris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Tue, 21 Apr 2009 23:21:14 +0200, vicente.botet <vicente.botet@wanadoo.fr> wrote:
[...]I will organize the specific classes on a specific platform namespace common for the portable part
namespace process { namespace common { class process; class child; }
posix for the posix based OS
#ifdef BOOST_PLATFORM_POSIX // or something like namespace process { namespace posix { class process : public common::process{...}; class child: public common::child{...}; } } #endif
window for the micosoft OS based platforms
#ifdef BOOST_PLATFORM_WINDOWS // or something like namespace process { namespace windows { class process : public common::process{...}; class child: public common::child{...}; } } #endif
// other platforms
and have typedef or a using directive at the namespace process level for each one of the available classes as
namespace process { #if defined(__POSIX__) // or something like typedef posix::process process; // or using posix::process; #elsif defined() // other patforms ... #else typedef common::process process; or // using common::process; #endif }
When the user use a non portable feature on a portable programm it will need to check conditionally for the non portable feature as
#ifdef BOOST_PROCESS_HAS_SPECIFC_FEATURE_1 // ... #else // ... #endif
I see. Thanks for the suggestion! I don't know if this design has been considered by the original developers or if they had good reasons to reject it and go with the current design. The problems with Boost.Process really seem to be unique as too many features are platform-dependent. I don't know any other Boost library which had to face similar problems.
[...]If the command fails it can be recovered
Commands could be composed using pipe "|", shell::cat("/tmp.file.txt") | shell::wc();
redirection ">"
shell::cat("/tmp.file.txt") | shell::wc() > str;
tee
shell::cat("/tmp.file.txt") | shell::grep () | shell::tee("file");
conditional concatenation "&",
shell::cd("/tmp") & shell::ls();
which executes ls only if cd /tmp succeeds.
Another nice idea! However I'm pretty sure that I unfortunately won't be able to work on this in the near future given the amount of more fundamental problems. :-/
[...]Last some minor questions: What about renaming handle by native_handle?
I'm open to it. You want native_handle to be a first-class citizen? Then it should move out of the detail namespace, too?
What is wrong on using the Boost.Filesystem library to idientify executables?
Support for boost::filesystem::path could be added, too. However it doesn't solve the narrow/wide string problem if that's what you meant?
Why the do we need to duplicate the name of the executables as an argument?
There's no way to distinguish otherwise if the first argument is the executable name or if it's meant to be the first command line argument. Currently Boost.Process simply copies all arguments into the array which is passed as a parameter to main(int argc, char *argv[]).
Respect to relative or absolute path of executables,why not let the undelying platform to handle with this?
Then developers would need to resolve paths themselves (or use a helper function) if they want to write platform-independent code? Boris
[...]

Hi Boris, ----- Original Message ----- From: "Boris Schaeling" <boris@highscore.de> To: <boost@lists.boost.org> Sent: Saturday, April 25, 2009 5:48 PM Subject: Re: [boost] Boost.Process article: Tutorial and request for comments
On Tue, 21 Apr 2009 23:21:14 +0200, vicente.botet <vicente.botet@wanadoo.fr> wrote:
[...]I will organize the specific classes on a specific platform namespace common for the portable part
namespace process { namespace common { class process; class child; }
posix for the posix based OS
#ifdef BOOST_PLATFORM_POSIX // or something like namespace process { namespace posix { class process : public common::process{...}; class child: public common::child{...}; } } #endif
<snip>
and have typedef or a using directive at the namespace process level for each one of the available classes as
namespace process { #if defined(__POSIX__) // or something like typedef posix::process process; // or using posix::process; #elsif defined() // other patforms ... #else typedef common::process process; or // using common::process; #endif }
When the user use a non portable feature on a portable programm it will need to check conditionally for the non portable feature as
#ifdef BOOST_PROCESS_HAS_SPECIFC_FEATURE_1 // ... #else // ... #endif
I see. Thanks for the suggestion! I don't know if this design has been considered by the original developers or if they had good reasons to reject it and go with the current design. The problems with Boost.Process really seem to be unique as too many features are platform-dependent. I don't know any other Boost library which had to face similar problems.
I think that you need to write some examples using non portables features that needs to run on several platforms. When you try this, you see that it is not really interesting to work with classes like process_windows and process_posix. Just a class named process is needed. The design platform specific namespaces is secondary. You can get the same using conditional compilation. What it is more important to me is that the library must provide a class process wich provided all the feautures available on the current platform. Of course the user can do himself this using declaration depending on the platform but I would prefer the library provides it. Imagine this use case. One application use the portable class 'children' because all the uses are portable. The user needs to add a new feature that force to use a non portable feature. With the current interface the user needs to change the use of 'children' for the specific child by 'children_posix' or 'chindren_windows' depending on the platform. With my using import directive, no change will be needed.
[...]If the command fails it can be recovered
Commands could be composed using pipe "|", shell::cat("/tmp.file.txt") | shell::wc();
redirection ">"
shell::cat("/tmp.file.txt") | shell::wc() > str;
tee
shell::cat("/tmp.file.txt") | shell::grep () | shell::tee("file");
conditional concatenation "&&",
shell::cd("/tmp") && shell::ls();
which executes ls only if cd /tmp succeeds.
Another nice idea! However I'm pretty sure that I unfortunately won't be able to work on this in the near future given the amount of more fundamental problems. :-/
If you don't mind and as these seems orthogonal I will try to see how all these shell related features can be implemented on top of the Process library.
[...]Last some minor questions: What about renaming handle by native_handle?
I'm open to it. You want native_handle to be a first-class citizen? Then it should move out of the detail namespace, too?
Yes, for all the system related libraries we should have a native_handle_t type and a function native_handle to get it. This is needed to manage with all the features not covered by the library. You should follow the design of the Thread library for which there are handles for the threads, mutex, ... system classes.
What is wrong on using the Boost.Filesystem library to idientify executables?
Support for boost::filesystem::path could be added, too. However it doesn't solve the narrow/wide string problem if that's what you meant?
Yes, I don't know Boost.Filesystem, but I expected that it provided a way to name any file. If this is not the case, forget my comment: :(
Why the do we need to duplicate the name of the executables as an argument?
There's no way to distinguish otherwise if the first argument is the executable name or if it's meant to be the first command line argument. Currently Boost.Process simply copies all arguments into the array which is passed as a parameter to main(int argc, char *argv[]).
Why this is not hidden by the library?
Respect to relative or absolute path of executables,why not let the undelying platform to handle with this?
Then developers would need to resolve paths themselves (or use a helper function) if they want to write platform-independent code?
I don't think that using absolute paths help to write platform independent code. IMO it is better to rely on the PATH environment variable (at least on unix-like OS exec takes this already in account). Best, Vicente

on Tue Apr 21 2009, "Boris Schaeling" <boris-AT-highscore.de> wrote:
I've written an article about Boost.Process: http://www.highscore.de/cpp/process/
Hi Boris, Looks like a really useful library, and the docs are very readable. Sorry, I know this will seem trivial, and I see that you took care to note it on your webpage, but when posting about a library that is not yet in Boost, I'd appreciate it if you could make that clear. Just starting with "I've written an article about the proposed Boost.Process" could be enough, though it's also be good to get an indicator in the subject line. Thanks! -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Fri, 29 May 2009 18:10:23 +0200, David Abrahams <dave@boostpro.com> wrote: Hi Dave,
[...]Looks like a really useful library, and the docs are very readable. Sorry, I know this will seem trivial, and I see that you took care to note it on your webpage, but when posting about a library that is not yet in Boost, I'd appreciate it if you could make that clear. Just starting with "I've written an article about the proposed Boost.Process" could be enough, though it's also be good to get an indicator in the subject line.
ok, no problem. There have been so many Boost.Process forks last year though that it took me quite some time to merge them. Unless someone else really plans to take over responsibility for Boost.Process and doesn't change only minor things (thus creating another fork) I hope that this proposed Boost.Process library remains the only one. There is no problem if a differently designed Boost.Process library is proposed. But in the past years all the Boost.Process libraries were forks and you didn't really know which one to pick (which lead me to merge them all). Boris
participants (4)
-
Boris Schaeling
-
David Abrahams
-
Phil Endecott
-
vicente.botet