0

I'm currently working on a project where one of the 4 versions of the same algorithm (involving different parallel programing frameworks + its sequential analogous) can be called each time the program runs.

The arguments for all these 4 are the same and the parallel framework details would be handled inside the functions themselves.

I'm all new to C++ so really don't know if it is possible to change which code the function() points to and select the right version based on a command-line argument.

Warning: hideous pseudocode ahead:

switch(version){
    case OMP:
        // make function() point to 1st version
    case CUDA:
        // ... to function2()
    case OCL:
        // ... to function3()
    case SEQ:
    default:
        // ... to function0()
}

// ...

while(condition){
    // call function()
}

So I'm trying to figure out an elegant way of doing this while having the different algorithms in separate files and just handling I/O in the main routine. Some behavioral pattern?

Alfageme
  • 2,075
  • 1
  • 23
  • 30
  • 1
    The OOP approach would be to make an interface class with a pure-virtual method, then make one subclass for each of the for cases, and have each subclass implement that method. Dynamically allocate an object of the appropriate subclass at startup and call the virtual method on that object as necessary. The C-style approach would be similar, except you'd set a function pointer instead of a pointer to an object. – Jeremy Friesner Jul 13 '16 at 03:39

3 Answers3

4

If you have plain functions and don't feel the need for a class hierarchy, you can use std::function:

std::function<void (int, float)> target;

switch(version){
    case OMP: target = function1; break;
    case CUDA: target = std::bind(function2, "hello", _1, _2); break;
    case OCL: target = function3; break;
    case SEQ: target = function4; break;
    default: target = function0; break;
}

target(100, 3.14);
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Ick `std::bind`: rarely worth it, bind is way to quirky imo. Just use lambda. – Yakk - Adam Nevraumont Jul 13 '16 at 03:48
  • 1
    @Yakk: That's subjective. You're welcome to post it as a competing answer. – John Zwinck Jul 13 '16 at 03:51
  • I've tried your solution with my function signature (`std::function target` ) but it's giving me some compilation problems D:. Idk if they involve the use of XCode's clang or just that I can't use a template that long/with those types. – Alfageme Jul 13 '16 at 16:10
  • The particular error: `error: implicit instantiation of undefined template 'std::__1::function'` – Alfageme Jul 13 '16 at 16:18
  • 1
    @Samué: see http://stackoverflow.com/questions/20654790/error-on-clang-xcode-5-0-2-with-stdfunctional-that-has-more-than-three-param – John Zwinck Jul 13 '16 at 16:22
1

This is a classic example of Strategy pattern. The basic idea of Strategy pattern is that you will have an abstract Strategy which will have a pure virtual execute or run method. There will be concrete subclasses for each different algorithms or strategies.

For your case the outline can be like this:

class BaseStrategy {
    virtual void execute(params...) = 0;
}

class OMPStrategy : public BaseStrategy {
    // implement execute method for OMP
}

class CUDAStrategy : public BaseStrategy {
    // implement execute method for CUDA
}

// etc

Now inside the method from where you need to select one version, you will instantiate the proper strategy object based on your input and call execute on it.

Something like this:

BaseStrategy *strategy;

switch(version){
    case OMP:
        strategy = new OMPStrategy();
        break;
    case CUDA:
        strategy = new CUDAStrategy();
        break;
    case OCL:
        strategy = new OCLStrategy();
        break;
    case SEQ:
        strategy = new SEQStrategy();
        break;
    default:
        strategy = new DefaultStrategy();
}

// now call it

while(condition){
    strategy->execute();
}

// delete strategy when done
delete strategy;

In this way you can have clean separate implementation of different algorithms in different classes.

Note: As pointed in the comments, there can be memory leak if an exception is thrown. If you are using c++11 then using std::shared_ptr or std::unique_ptr will be better than using raw pointer.

taskinoor
  • 45,586
  • 12
  • 116
  • 142
1

If you are on Linux (and possibly other Unixes), you can load the correct library functions with dlopen() in runtime, see 'man 3 dlopen'.

The dlopen family is also useful in the case where you have a main application and want to be able to hot-patch with e.g. a newer version of a plugin library. In principle, the main application would not even have to be restarted to upgrade the plugin.

It has been a while since I hacked in Windows, but there are good chances something similar to dlopen exists in Windows and Mac too. (Mac is based on a BSD variant, so even dlopen() may work).

Erik Alapää
  • 2,585
  • 1
  • 14
  • 25