Home > Back-end >  C Functions Select Behavior Based on Passed Parameter Derived Class?
C Functions Select Behavior Based on Passed Parameter Derived Class?

Time:12-13

What I'm trying to do is to have a base class that has a primary functionality, as well as multiple derived classes that have various other additional functions/variables. The main functionality of all these derived classes will behave very similarly no matter what object of one of the derived classes is passed to it, but with slight changes based on what the derived class is.

So a background here is that I'm mostly experienced with Fortran programming but am trying to break into C more. I'm trying to do something here that is pretty easy in Fortran but am having trouble in C . Basically my code defining my classes looks something like this

class base_class{
  public:
    void prim_func(base_class &my_obj);
};

class derived_class_1: public base_class{
  public:
    int a_func(int arg1);
};

class derived_class_2: public base_class{
  public:
    double a_func(double arg2);
};

And then the void class method looks something like (right now, I know this isn't right)

void base_class::prim_func(base_class &my_obj){
  // a bunch of stuff for all classes
  // if my_obj class is derived_class_1
    my_obj.a_func(1);
    // some more stuff specific to using derived_class_1
  // if my_obj class is derived_class_2
    my_obj.a_func(1.5);
    // some more stuff specific to using derived_class_2
  // a bunch of stuff for all classes
}

I want that prim_func to have (slightly) different behaviors based on what the actual derived class that is passed to it is. So the main code would look like this

derived_class_1 def_obj_1;
derived_class_2 def_obj_2;
main(){
  def_obj_1.prim_func(def_obj_1);
  def_obj_2.prim_func(def_obj_2);
}

So I would like to slightly modify the behavior in this primary functionality based on what the derived class of the passed object actually is. In Fortran there is a SELECT TYPE functionality (https://www.intel.com/content/www/us/en/develop/documentation/fortran-compiler-oneapi-dev-guide-and-reference/top/language-reference/a-to-z-reference/s-1/select-type.html) that allows this, but I can't seem to find something similar in C ?

I know one workaround could be to just make one big class that contains overloaded versions of all the different functions, and all the different variables that the various derived class objects would need, and then just have an indicator variable to let it know which functionality it should be using. But this would be extremely inelegant and would potentially cause some other issues, so I would like to avoid it.

CodePudding user response:

You can't do that with plain C . You can't have derived classes with overriden functions with different signatures.

What you can do is using templates.

Use a templated Base Class that provides the base function as a pure virtual. You can then write a wrapper around that function as a template:

template<typename T>
class Base {
    public:
    virtual T func(T param) = 0;
};

class DerivedA : public Base<int> {

    public:
    int func(int param) override {
        return param;
    };
};

class DerivedB : public Base<double> {

    public:
    double func(double param) override {

        return param;
    };
};

template<typename T>
T prim_func(Base<T>& base, T param) {
    return base.func(param);
}


int main() {
    DerivedA a;
    DerivedB b;

    auto c = prim_func(a,4);
    auto d = prim_func(b,4.0);
}

CodePudding user response:

Ok, so it turns out there is a way to do this in C it just involves dynamic casting a pointer using the passed object (but it does have to be a polymorphic object). So the way I made it work was doing something like this (comparing to the previous incomplete code I had).

class base_class{
  public:
    virtual void a_func(){};
    void prim_func(base_class &my_obj);
};

class derived_class_1: public base_class{
  public:
    int a_func(int arg1);
};

class derived_class_2: public base_class{
  public:
    double a_func(double arg2);
};

void base_class::prim_func(base_class &my_obj){
  // a bunch of stuff for all classes
  if(derived_class_1* class_ptr = dynamic_cast<derived_class_1*>(&my_obj)){
    class_ptr.a_func(1);
    // some more stuff specific to using derived_class_1
  }
  else if(derived_class_2* class_ptr = dynamic_cast<derived_class_2*>(&my_obj)){
    class_ptr.a_func(1.5);
    // some more stuff specific to using derived_class_2
  }
  // a bunch of stuff for all classes
}

derived_class_1 def_obj_1;
derived_class_2 def_obj_2;
main(){
  def_obj_1.prim_func(def_obj_1);
  def_obj_2.prim_func(def_obj_2);
}

To be clear, this still won't compile/work since some of the functions need definitions and what not, but this is a general description of how to do it. A working example can be found in MFEM's code here: https://docs.mfem.org/4.5/amgxsolver_8cpp_source.html#l00859

  • Related