Search code examples
c++ooppolymorphism

How can we change behaviour of c++ method depending on initialization parameter?


In python I can write something like

class C:
    def __init__(self, mode):
        if mode == 0:
            self.f = self.f0
        elif mode == 1:
            self.f = self.f2
        (...)
        else:
            raise KeyError

    def f0(self, a1, a2):
        <do stuff>

    def f1(self, a1, a2):
        <do other stuff>

    (...)

As an alternative to writing a parent class with subclasses C1, C2, ... that overwrite f. The short question is: Can i do something similar in C++ without summoning Cthulhu?

Before anyone asks "why":

This is useful, for example if I have another class D, that uses C, because it allows the parameter mode to simply be passed on to C rather than writing separate cases everywhere an instance of C is initialised.

I want to avoid a switch or if-tree that is evaluated every time f is called, because f is a small function that is called very many times in an already expensive calculation.


Solution

  • Thanks to @AlanBirtles and @Yksisarvinen !

    The working solution I ended up with was

    stuff.h:

    class C{
    
        C(int mode);
    
        const int mode;
        double f(int i, int j, int k);
    
        using FunctionType = double(C::*)(int, int, int);
        double f0(int i, int j, int k);
        double f1(int i, int j, int k);
        FunctionType f_p;
    };
    

    stuff.cpp:

    C::C(int mode) : mode{mode} {
        switch (mode){
        case 0:
            f_p = &C::f0;
            break;
        case 1:
            f_p = &C::f1;
            break;
        default: throw "Non-valid mode!";
        }
    }
    double C::f(int i, int j, int k) {std::invoke(f_p, this, i, j, k);}
    double C::f0(int i, int j, int k){ <do stuff> }
    double C::f1(int i, int j, int k){ <do other stuff>}
    

    As suggested by @mch, using a template class would probably have been just as simple. However (not specified in the question) I needed this to work with a python-wrapper using pybind11, so the above solution allowed me to avoid any trouble related to wrapping a template class.