Issues with boost::process from the sandbox and the boost::process::child semantics
Hi folks, this is my first post to this list, so I hope I'll include enough information to make it useful. I'd like to write a class with the following interface: class Process { public: Process(const std::vectorstd::string &args); std::string readEverythingReceivedSoFar(); void write(const std::string &data); private: something childProcess; }; and when looking for building blocks to use, I came across the process library in boost's sandbox [1]. On a first sight, it looks great and implements much more than what I need now, and has a nice documentation -- so far so good. But when I actually tried to use it, I wasn't able to get it working without resorting to something which is an ugly hack to me. It's obvious that I have to have some access to "the child process" from the reading/writing methods, and -- because I don't like leaving zombie processes behind me -- also from my destructor to be able to kill it. In this library, there's a boost::process::child which is intended to be used for exactly this purpose, so I tried to add that as that "something" member. For technical reasons (got to do some postprocessing to the user-supplied arguments before I actually launch my process), I can't initialize the childProcess in the initializer list before the actual constructor body, which means that the compiler will have to initialize it to something for me. It turns out that the boost::process::child class is missing a default constructor, so my member can't be of that type (unless I wanted to create a very artificial instance with a faked PID, fabricated FDs etc). So I had to use pointers for that, which is IMHO bad because it needs dynamic memory allocation without a real reason. I realize that we're talking about a fork() here, so a dynamic memory allocation is not a problem from the performance point of view, but I prefer not to use it unless I have to. The only solution I could come up is the following: private: std::tr1::shared_ptrboost::process::child childProcess; and in the constructor: childProcess.reset(new boost::process::child(boost::process::launch(exe, arguments, ctx))); ...because I get a boost::process::child by value, so I have to invoke a copy constructor and put that object on the heap, if I understand this issue correctly. That smells fishy. So, I'd highly appreciate if you could tell me whether my understanding is correct, and if there's a better way out of here. In case I missed something obvious, I'd be very happy to learn about that, too. With kind regards, Jan [1] `svn co http://svn.boost.org/svn/boost/sandbox/process/` -- Trojita, a fast e-mail client -- http://trojita.flaska.net/
On 27/03/2011 18:01, Jan Kundrát wrote:
The only solution I could come up is the following:
private: std::tr1::shared_ptrboost::process::child childProcess; You could always use Boost.Optional for that, which is stack based.
K-ballo.-
On 03/28/11 02:33, Agustín Bergé wrote:
You could always use Boost.Optional for that, which is stack based.
Thanks for your tip, I've switched to boost::optional. Just a note, though -- based on my understanding of the rationale for the boost::optional, my usage is rather strange here. I do not really want to have an "optional" boost::process::child instance, I want to have it all the time. I realize that from a purely technical point of view, I do use the optionality, as it is left uninitialized for some time in the constructor, but I consider that a technical hack. In short, when there's a review for possible integration of this library to main boost, I'd like the reviewers to note this limitation and the fact that the semantics was *very* surprising for me as a programmer. Cheers, Jan -- Trojita, a fast e-mail client -- http://trojita.flaska.net/
On Sun, Mar 27, 2011 at 4:01 PM, Jan Kundrát
[...] For technical reasons (got to do some postprocessing to the user-supplied arguments before I actually launch my process), I can't initialize the childProcess in the initializer list before the actual constructor body, which means that the compiler will have to initialize it to something for me. It turns out that the boost::process::child class is missing a default constructor
Why not do your arg-processing in a static function returning a new args vector by value? That way you can initialize Process(...) : childProcess(..., preprocess(args)) {...} Or simply using a shared_ptrboost::process::child (or even scoped_ptrboost::process::child) instead of a boost::process::child value, to delay creating the actual boost::process::child instance to after the initializer? Sounds more logical to me than using boost::optional. I don't see the lack of a default constructor as a design flaw here. --DD
On Mon, Mar 28, 2011 at 12:24 PM, Dominique Devienne
On Sun, Mar 27, 2011 at 4:01 PM, Jan Kundrát
wrote:
It turns out that the boost::process::child class is missing a default constructor...
I don't see the lack of a default constructor as a design flaw here.
I agree. What should be the meaning of a default-constructed boost::process::child? Which of the normal invariants still hold when the object is default-constructed? It seems to me that you're asking for the 'child' class to store a flag: is this a real instance or a placeholder instance? Then every access to that object must check the flag. By factoring out the flag to boost::optional, the semantics of the boost::process::child class are improved.
On 03/28/11 19:37, Nat Linden wrote:
It seems to me that you're asking for the 'child' class to store a flag: is this a real instance or a placeholder instance? Then every access to that object must check the flag.
Hi Nat and others, based on your arguments, it seems that I was wrong in my evaluation. Thank you for clarifying the design choices, I now realize that it indeed makes sense. Cheers, Jan -- Trojita, a fast e-mail client -- http://trojita.flaska.net/
Hi all,
On Mon, Mar 28, 2011 at 6:58 PM, Jan Kundrát
On 03/28/11 19:37, Nat Linden wrote:
It seems to me that you're asking for the 'child' class to store a flag: is this a real instance or a placeholder instance? Then every access to that object must check the flag.
Hi Nat and others, based on your arguments, it seems that I was wrong in my evaluation. Thank you for clarifying the design choices, I now realize that it indeed makes sense.
I agree with previous suggestions that boost::optional is probably the way to go with the process library in its current form. It's worth noting that when this issue was brought up a couple months ago, Boris (the most recent developer to spend some time on this project) indicated that a default-constructed invalid process wouldn't be too bad since process objects in the code may reference terminated processes [1]. I'm not sure if/when that change will happen. HTH, Nate [1] http://lists.boost.org/Archives/boost/2011/02/176958.php
participants (6)
-
Agustín Bergé
-
Dominique Devienne
-
Jan Kundrát
-
Klaim - Joël Lamotte
-
Nat Linden
-
Nathan Crookston