0

I have following abstract class in C++

class Converter
{

    public:
        virtual void enable(void)                          = 0;
        virtual void setReferenceValue(double)             = 0;
        virtual void registerAlarmListener(AlarmListener*) = 0;
        virtual void confirmAlarm()                        = 0;
        virtual void notifyParameterChange()               = 0;
        virtual void update(void)                          = 0;

}

My intention was that this abstract class will be a common interface for all kinds of power electronics converters in my application. Unfortunatelly I have hit a problem how to model a situation when all kinds of power electronics converters has a set of parameters and a set of alarms but the items of those sets depend on each type of the converter. So I don't know how to fully declare the methods confirmAlarm and notifyParameterChange because I don't know how to declare type of the formal parameters. I would probably need to define some type which will model a common list of parameters and common list of alarms. Can anybody give me an advice how to do that? Thanks in advance for any ideas.

Steve
  • 805
  • 7
  • 27
  • `confirmAlarm` has no inputs and no outputs - you can write it however you'd like. – erip Oct 29 '20 at 16:59
  • Your question is not super clear to me. Are saying your derived classes need to take different arguments for the `confirmAlarm()` and `notifyParameterChange()` functions? – AVH Oct 29 '20 at 17:00
  • @erip thank you for your reaction. I have probably explained the problem in misleading manner. The declaration of the confirmAlarm and notifyParameterChange methods are not complete - the formal parameters are missing. My problem is that I don't know how to declare them. – Steve Oct 29 '20 at 17:02
  • 4
    The problem with a common interface is the function parameters all must be the same. If you can't make all of the classes that need to implement the functions implement the functions with the same parameters, even if some are ignored, you need to go back to your design and figure out where you are going wrong. – user4581301 Oct 29 '20 at 17:05
  • @Darhuuk thank you for your reaction. I am sorry in case my description is not clear. My intention was that the confirmAlarm and notifyParameterChange will accept same type of arguments for all the derived classes. But each derived class have its own set of alarms and its own set of parameters. – Steve Oct 29 '20 at 17:07
  • @user4581301 thank you for your reaction. I see your note. Do you have any idea how to solve my problem? – Steve Oct 29 '20 at 17:13
  • 2
    Vlad has a workable solution in his answer below, but know that the downside of this approach is it the onus is on the caller to know what derived object they hold and pass the appropriate parameters. It's not that much different than using a `dynamic_cast` and then calling a function that's NOT in the interface. The presence of "uncommon" functions in a common interface suggests [the Liskov Substitution Principle](https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle) is being violated, so be careful. – user4581301 Oct 29 '20 at 17:17
  • The derived class should know what it needs to know and be able to handle the differences between it and other classes derived from the same base with no outside help. Is there any way you can provide the custom parameters when the derived class instance is constructed and store them until they are needed? – user4581301 Oct 29 '20 at 17:23
  • I am not sure whether I understand your question. But I have got an idea to create a templated abstract class where template parameter will be the list of parameters and list of alarms (both of them modeled as a class). Do you think this could work? – Steve Oct 29 '20 at 17:32
  • 1
    @Steve If you template the interface, you won't have a "common" interface anymore. It's up to you to decide whether that works for your use case. – AVH Oct 29 '20 at 17:34
  • @Steve Where I'm going is say you have a set of parameters you pass into `confirmAlarm` that differs for some classes derived from `Converter`. If you know these parameters at construction time, pass them in with the constructor and hold onto them so the caller can later `confirmAlarm ` without having to know or pass any class-specific parameters. I admit this is often a pretty big if. But a combination of it and adding a few extra parameters to the prototype to widen the utility of the interface. – user4581301 Oct 29 '20 at 18:16
  • Say only half of the derived classes use the `frob` parameter. If you always pass it in, those who need `frob` have it and those who don't don't care that they have it. The caller doesn't care either way. Everybody gets `frob`. When you have many wildly different options and the parameter list gets long to handle all of them, you probably don't have as cohesive an interface as you thought you did and should stop and rethink. – user4581301 Oct 29 '20 at 18:16
  • If not all of the paths to `confirmAlarm` have `frob` available, you probably have the abstraction in the wrong place and whatever calls `confirmAlarm` should probably replace `confirmAlarm` in the interface. – user4581301 Oct 29 '20 at 18:19
  • Smurf. At this point I probably should have written an answer. – user4581301 Oct 29 '20 at 18:20

1 Answers1

1

You can do that with polymorphism: define BaseParam and BaseAlarm classes and use vectors of pointers to them as an argument to your functions.

Alternatively, you can use vector of std::variant

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27