-1

I have a snippet of code in my system, put it short, there are three classes, A, B extends A, and a singleton service C. C implemented one function, and I want it's behavior customizable based on caller.

class A {

void abort() {
  C::doSomething();
}

}

class B : public A {}

class C {

void doSomething() {
  ... a bunch of logics here.
}

}

There are multiple child classes extending A, and the current implementation is they enumrate all cases in doSomething, but I realized the cases are actually related to the caller. So I want to make the code looks like:

class A {

virtual void abort() = 0;

}

class B : public A {

void abort() override {
  C::doSomething<B>();
}

}

class C {

template <class T>
void dosomething() {
  ...T specific logic.
}

}

And since the overrided abort function only do one thing that is related to the implementing class, I wonder if there is a mechanism to make this automatic that I don't need to make every child class implement it (because I need to teach other people more about how to integrate into the system, and I prefer it works with least amount of code added.)

Thanks.

AuBee
  • 86
  • 9
  • 2
    How does C know what B-specific action to perform, and why can't B perform it itself? – n. m. could be an AI Apr 16 '23 at 07:10
  • Why do you want `C` to provide functionality that is specific to each calling class? The caller will already know its own type, so why not provide some `virtual` function in `A` (say `abort_action()`), override it as needed in derived classes, and call it from `C::doSomething()`? All you need to do is pass an `A *` to `C::doSomething()`. Then `abort()` can be a non-virtual that calls `singleton->doSomething(this)`, and `C::doSomething()` can do something like `void doSomething(A *object) {object->abort_action();}` – Peter Apr 16 '23 at 07:53

1 Answers1

1

One way you might implement this is via the Curiously Recurring Template Pattern.... below is an example showing how two different subclasses of A get C::doSomething<TheExpectedSubclass>() called, without each subclass having to manually override the abort() method:

#include <typeinfo>
#include <stdio.h>

class C
{
public:
   template<class T> static void doSomething(T & caller)
   {
      printf("doSomething for caller %p of type %s\n", &caller, typeid(caller).name());
   }
};

template<class SubclassType> class A
{
public:
   A() {}

   void abort() {C::doSomething<SubclassType>(static_cast<SubclassType &>(*this));}
};

class B1 : public A<B1>
{
public:
   B1() {}
};

class B2 : public A<B2>
{
public:
   B2() {}
};


int main(int, char **)
{
   B1 b1;
   b1.abort();

   B2 b2;
   b2.abort();

   return 0;
}
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • TIL. But will this generate multiple version of class A code, or one class A code with multiple implementation of 'doSomething'? Thanks. – AuBee Apr 16 '23 at 07:02
  • @AuBee It is true, If this is a problem, you can create an intermediate class template, say `A1`, which derives from `A` and *only* implements `doSomething` (the rest of the functionality stays in `A`). Child classes inherit from `A1`, say `class B : public A1`. – n. m. could be an AI Apr 16 '23 at 07:14
  • With templating there isn’t a singular class A to speak of, rather various classes A for different values of X. But n.m. is right, you could have A inherit from another non-templated base class if you want. – Jeremy Friesner Apr 16 '23 at 07:36
  • 1
    Im not sure, but you could try if this can be done with C++23 deducing this. – gerum Apr 16 '23 at 08:11
  • 1
    @gerum amazing, I think [this](https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/) is a good solution. – AuBee Apr 16 '23 at 22:49