boost-process icon indicating copy to clipboard operation
boost-process copied to clipboard

Virtualise initializers

Open hotgloupi opened this issue 9 years ago • 2 comments

First of all, thanks for the good work here, the API is very clean, and yet extensible and practical, which is a good thing when trying to abstract something so different across OSes.

However, I don't fully understand why the code is so much static. Why did you choose to use compile time polymorphism (i.e. templates) instead of runtime polymorphism (i.e. vtables) ? IMHO the overhead of spawning a process makes the cost of a vtable access ridiculous.

My problem, somewhat related to the previous question, is how to create a set of initializers, and then spawn a process. I would like to do something like:

std::vector<Initializer> initializers;
if (some_condition)
    initializers.push_back(some_initializer);
auto child = boost::process::executor()(args, initializers);

In order to create the type Initializer, I would have to do complicated things:


template<typename Executor>
struct InitializerBase
{
      virtual void on_fork_setup(Executor& e) = 0;
     // ... other methods here
};

template<typename Executor, typename ConcreteInitializer>
struct InitializerWrapper : InitializerBase<Executor>
{
    ConcreteInitializer _initializer;
    template<typename... Args
    explicit InitializerWrapper(Args&&... args)
        : _initializer(std::forward<Args>(args)...)
    {}
    void on_fork_setup(Executor& e) override { _initializer.on_fork_setup(e); }
     // ... other methods here
};

template<typename Executor>
struct _Initializer
{
    typedef Executor executor_type;
    std::unique_ptr<InitializerBase> _initializer;
    void on_fork_setup(Executor& e) { _initializer.on_fork_setup(e); }
   // ... other methods here
}

#ifdef POSIX_SOMETHING
typedef _Initializer<PosixExecutor> Initializer;
#else
// something else
#endif

template<typename ConcreteInitializer, typename... Args>
Initializer make_initializer(Args&&... args)
{
  return Initializer(new InitializerWrapper<typename Initializer::executor_type, ConcreteInitializer>(
     std::forward<Args>(args)...);
}

(I omitted the boilerplate code for constructors and destructors)

That's pretty much re-virtualizing your code ... Do you know a better way ?

hotgloupi avatar Mar 12 '15 19:03 hotgloupi

First thank you for your feedback on the API! Credit goes to Jeff Flinn who proposed the current API back in 2011.

Regarding static code: I think it's just that no one had a requirement so far to detect at run-time which initializers to use. As people knew their requirements at compile-time, they wanted to use static code. However I can see how a small initializer hierarchy with a base class could be useful (especially as performance indeed doesn't really matter).

As I'm not working on the library anymore (at least there is nothing planned for now), I'm afraid I can't offer any help. But you are very much invited to fork and change the library!

BorisSchaeling avatar Mar 19 '15 17:03 BorisSchaeling

Please see https://github.com/BorisSchaeling/boost-process/pull/8 for a solution using Boost.TypeErasure.

nat-goodspeed avatar Nov 24 '15 13:11 nat-goodspeed