user8153630
user8153630

Reputation: 301

Selectively inheriting from any of multiple classes at runtime

I'm doing scientific computing and I'm beginner in c++. MyNLP is a class which contain all the problem data and methods. I'm using third party libraries for numerical optimization. Each third party is a specific algorithm to solve my problem. In order to use each library, my MyNLP class need to inherit corresponding class from third party library.

For example,

Class MyNLP :public IPOPT
{
};

enable me to use IPOPT algorithm to solve my problem. Similarly,

class MyNLP: public SQP
{
};

enable me to use SQP algorithm.

But in my case, only at the run-time, program decides which class it should inherit. I have to inherit either one of third party class. Could any one give a technique to achieve this in cpp?

Upvotes: 12

Views: 2259

Answers (4)

Roger Gee
Roger Gee

Reputation: 871

C++ class inheritance is a compile-time construct: it's fixed at runtime. The compiler has to have this information available to it at compile time to determine how to allocate objects and ensure type safety.

You should consider instead implementing a factory. In this design, a factory object or function decides which instance to generate based on input available at runtime. You should create a common base class that provides an interface to the required functionality. This should consist of one or more virtual functions that are used to dynamically call the correct implementation (this is called dynamic dispatch).

Here's a quick example based on your question:

class nlp_interface
{
public:
    virtual ~nlp_interface() = default;

    // This pure-virtual function has no implementation, forcing the
    // class to be abstract and for derived classes to implement the
    // member function.
    virtual void do_numeric_optimization(/* ... */) = 0;
};

class MyNLP_IPOPT:
    public nlp_interface,
    public IPOPT
{
public:
    // Provide a specific implementation.
    virtual void do_numeric_optimization(/* ... */);
};

class MyNLP_SQP:
    public nlp_interface,
    public SQP
{
public:
    // Provide a specific implementation.
    virtual void do_numeric_optimization(/* ... */);
};

Here I use multiple inheritance to provide a well-known interface to the MyNLP_* classes. This is because I can't assume the third party base classes have a common virtual interface that can be used. If they do then just create instances of the third party classes directly. However you seemed to allude to the fact that you had to subclass them for one reason or another.

Here's the factory.

#include <memory>
#include <exception>

using nlp_pointer = std::unique_ptr<nlp_interface>;
nlp_pointer factory_function(const std::string& input)
{
    if (input == "IPOPT") {
        return nlp_pointer( new MyNLP_IPOPT );
    }
    if (input == "SQP") {
        return nlp_pointer( new MyNLP_SQP );
    }

    throw std::runtime_error("unrecognized algorithm kind");
}

To use the factory, call factory_function() and invoke the do_numeric_optimization() member function on the returned nlp_interface instance (which is wrapped in a smart pointer). It will call the correct version via dynamic dispatch.

Upvotes: 5

user0042
user0042

Reputation: 8018

You cannot select inheritance at runtime, because the resulting type is always determined by the compiler at compile time.

What you can do is to apply the Strategy Pattern:

enter image description here

The idea is to have an abstract class that represents the algorithm used in MyNLP:

class Data;

class NLPAlgo {
public:
    virtual ~NLPAlgo() = default;
    virtual void Apply(Data&) = 0;
};

and provide concrete classes that use IPOPT and SQP:

class IPOPTAlgo : public NLPAlgo {
    IPOPT ipopt;
public:
    void Apply(Data& data) {
        // Use ipopt to realize the implementation
    }
}; 

class SQPAlgo : public NLPAlgo {
    SQP sqp;
public:
    void Apply(Data& data) {
        // Use sqp to realize the implementation
    }
}; 

Further take that abstract class as parameter for MyNLP

class MyNLP {
    std::unique_ptr<NLPAlgo> algo_;
public:
    MyNLP(std::unique_ptr<NLPAlgo> algo) : algo_(algo) {}
    void Apply(Data& data) {
        algo->Apply(data);
    }
};

Then you can configure at runtime which algorithm should be used with MyNLP:

// Note:
// That code could be factored out to an Abstract Factory:
// https://sourcemaking.com/design_patterns/abstract_factory
// That is figured out in more detail in this answer:
// https://stackoverflow.com/a/44985054/8242698
std::unique_ptr<NLPAlgo> algo;
if(condIPOPT) {
    algo = std::make_unique<IPOPTAlgo>();
}
else if(condSQP) {
    algo = std::make_unique<SQPAlgo>();
}

Data data;
MyNLP myNLP(algo);

myNLP.Apply(data);

Upvotes: 25

Francis Cugler
Francis Cugler

Reputation: 7905

With a little bit of template magic (well there is no such thing as magic in programming) I think this would help you to achieve your goal in the manner in which you were asking. There were many other great answers out there such Strategy Pattern, Factory, Dispatching etc. but this is a version that uses templates and inheritance from said library while choosing which one to instantiate at run time through the use of template specialization.

#include <iostream>

class A {
public:
    int a = 1;
    A() {}
};

class B {
public:
    float b = 2.0f;
    B() {}
};

class C {
public:
    char c = 'c';
    C() {}
};

template<class T>
class D : public T {
public:
    D() : T() {}
};


int main( int argc, char** argv ) {
    D<A> dA;
    D<B> dB;
    D<C> dC;

    std::cout << "D<A>::a = " << dA.a << "\n";
    std::cout << "D<B>::b = " << dB.b << "\n";
    std::cout << "D<C>::c = " << dC.c << "\n";

    std::cout << "Press any key and enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}

Here I have shown 3 different concrete or complete types classes A,B, & C that can represent your 3 different possible libraries that you would use to perform the evaluations or calculations to solve your problems. Class D is a template type that represents your MyNLP class. Now you can have MyNLP<A> mynlpA use the first library as your class will now inherit from it, and so on.

However; this is done at compile time and not runtime, and you have to instantiate the class with the specific types. You could use this template and set it up with user input through if statements or a switch statement within some defined function to choose which version of your class to create and use during runtime. Also notice that I specialized the different class template constructors based on the inheriting class's base class. Run this snippet to see how I was able to get the class template D<T> to inherit from A, B, or C based on user input at runtime.

#include <iostream>
#include <string>
#include <algorithm>

class A {
public:
    int a = 1;
    A() {}
};

class B {
public:
    float b = 2.0f;
    B() {}
};

class C {
public:
    char c = 'c';
    C() {}
};

template<class T>
class D : public T {
public:
    D() : T() {}
};

template<>
D<A>::D() {
    std::cout << "Initialize Library A\n";
}

template<>
D<B>::D(){
    std::cout << "Initialize Library B\n";
}

template<>
D<C>::D() {
    std::cout << "Initialize Library C\n";
}       

int main( int argc, char** argv ) {    
    std::string entry;
    std::cout << "Please choose which library to chose from: `A`, `B` or `C`\n";
    std::cout << "Or `Q` to quit\n";

    std::cin >> entry;

    std::transform(entry.begin(), entry.end(), entry.begin(), ::toupper);

    while ( true ) {

        if (entry == std::string("Q")) {
            break;
        }

        if (entry == std::string("A")) {
            D<A> dA;
            std::cout << "D<A>::a = " << dA.a << "\n";
        }

        if (entry == std::string("B")) {
            D<B> dB;
            std::cout << "D<B>::b = " << dB.b << "\n";

        }

        if (entry == std::string("C")) {
            D<C> dC;
            std::cout << "D<C>::c = " << dC.c << "\n";
        }

        entry.clear();
        std::cout << "Please choose which library to chose from: `A`, `B` or `C`\n";
        std::cout << "Or `Q` to quit\n";

        std::cin >> entry;

        std::transform(entry.begin(), entry.end(), entry.begin(), ::toupper);

    } 

    std::cout << "Press any key and enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}

There is a caveat to this method though: If the base class has private members/functions that you may need to call or use directly, they will be inaccessible to you, but the good thing is you will have access to anything that is public or protected, but this is the idea of data encapsulation.

Upvotes: 7

Tobias Ribizel
Tobias Ribizel

Reputation: 5421

It seems to me this is an instance of the XY problem: You asked for a way to switch out the base classes while you actually want a way to use your code for different solvers with little adaption necessary.

Having implemented an NLP solver with IPOPT myself recently, I would recommend another approach:

Why don't you first implement a base class that implements all relevant methods for evaluating the NLP:

  • Objective value
  • Objective gradient
  • Jacobian of the (in)equality constraints
  • Lagrangian
  • Hessian of the Lagrangian

Since your solvers will likely use equivalent formats for scalars, vectors and also hopefully sparse matrices (in coordinate format: row vector, column vector, value vector) and the same floating-point type (double), you can then implement lightweight wrappers (based on the solver's interface) around this base class instead of trying to use some kind of some weird runtime polymorphism.

Upvotes: 5

Related Questions