0

In the following code sample, the processes are assigned to different contexts. And each App object will be bound to only one context. Though all the processes run the same code, only those belonging to the context will execute the methods of App. In order to implement this goal, as shown in the code, I have to add if control statement in every method of the App class to check. So my question: are there some elegant ways to do the same work such as design patterns or code practices?

class Context {
    public:
        Context(int num_procs);
        bool ContainsCurrentProcess();
        ...
    private:
        std::vector<int> procs_;
        ...
};

bool Context::ContainsCurrentProcess() {
    if (current_process_id belongs to procs_ )
        return true;
    else 
        return false;
}

class App {
    public:
        App(Context *ctx, ...) {}
        void Method1();
        void Method2();
        void Method3();
        ...
    private:
        Context *ctx_;
        ...
};

void App::Method1() {
    if (ctx_->ContainsCurrentProcess()) {
        ...
    }
}

void App::Method2() {
    if (ctx_->ContainsCurrentProcess()) {
        ...
    }
}

void App::Method3() {
    if (ctx_->ContainsCurrentProcess()) {
        ...
    }
}

 // In the parallel program, all processes will run the following code.
 // However, each processes can have different behaviors based on their process IDs.
int main() {
  // Assume we have 8 processes
  Context ctx1(4); // Assign process 0, 1, 2, 3 to ctx1.
  Context ctx2(4); // Assign process 4, 5, 6, 7 to ctx2.
  App app1(ctx1, ...);
  App app2(ctx2, ...);
  // The app1 and app2 will use different groups of processes concurrently.
  app1.Method1();
  app2.Method2();
}
3

If your concern is duplicating the conditional expression in the if statement, then an option could be to change the Context::ContainsCurrentProcess into a function which accepts a lambda to run when the condition is true instead of returning the bool. For example:

void Context::Invoke(std::function<void()> func)
{
    if (/* current process id belongs to procs*/)
    {
        func();
    }
}

void App::Method1()
{
    ctx_->Invoke([]()
    {
        // Do Method 1 stuff..
    });
}

void App::Method2()
{
    ctx_->Invoke([]()
    {
        // Do Method 2 stuff..
    });
}

This takes away the need for other classes to know or care about the state of ContainsCurrentProcess, which fits the Tell Don't Ask principle.

However the extra level of indirection created by the lambda may have a cost in terms of readability - i.e. it isn't immediately clear to the reader that the Invoke method is checking the Context state.

You could argue that this may be a good or a bad thing - i.e. somebody reading the code probably shouldn't need to know what's actually going on inside Invoke in order to understand Method1 - but if they're trying to debug some strange behaviour in Method1 related to the state of Context, then it could get in the way.

0

This seems like a design issue. Why is an instance of App getting called to handle functionality for a Context that doesn't contain the current process? It seems like that Context should never get passed to that App. Can you change the constructor for App to throw if ctx->ContainsCurrentProcess() returns false? Or otherwise not even create an App with that Context?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.