What's the simplest way to satisfy a pure abstract method with methods from other base classes
Asked Answered
S

5

8

Edit: Per some comments, by simple I mean a) less code, b) easy to maintain, and c) hard to get wrong.

Edit #2: Also, using containment instead of private inheritance is not objectionable if it does indeed simplify the implementation of InterfaceImpl.

Currently, the only way I know to do this is to have the implementer define the abstract method and delegate the call to the target base type's method. Example:

#include <iostream>
#include <memory>

class Interface
{
public:
    virtual void method1() = 0;
    virtual void method2(int x) = 0;
};

class MethodOneImpl
{
 private:
    void method1(int x)
    { std::cout << "MethodOneImpl::method1() " << x << std::endl; }

 public:
    void method1() { method1(0); }
};

class MethodTwoImpl
{
 public:
    void myFunc(int x)
    { std::cout << "MethodTwoImpl::myFunc(x)" << x << std::endl; }
};

class InterfaceImpl : public Interface
                    , private MethodOneImpl
                    , private MethodTwoImpl
{
public:    
    virtual void method1() { MethodOneImpl::method1(); }
    virtual void method2(int x) { MethodTwoImpl::myFunc(x); }
};

int main()
{
    std::unique_ptr<Interface> inf;
    inf.reset(new InterfaceImpl);
    inf->method1();
    inf->method2(0);

    // This should be disallowed!
    // std::unique_ptr<MethodOneImpl> moi;
    // moi.reset(new InterfaceImpl);
}

At first, I thought that perhaps this might solve the problem:

class InterfaceImpl : public Interface
                    , private MethodOneImpl
                    , private MethodTwoImpl
{
public:    
    using MethodOneImpl::method1;
    // Obviously this wouldn't work as the method names don't match.
    //using MethodTwoImpl::??? 
};

The first using statement will make both MethodOneImpl::method1 methods be public, but it actually doesn't fulfill the contract with Interface, and it modifies the accessibility of MethodOneImpl::method1(int). And obviously we couldn't use this solution with method2 as the names don't match up.

FWIW, I have what I think is a solution, but it is not part of the standard at all (in other words it won't compile). I was thinking of making a proposal to the C++ committee; if anyone has any advice, I'd appreciate any comments below (but please dont' submit the advice as an answer).

Sufficient answered 12/11, 2011 at 15:32 Comment(17)
What is the problem with the delegating code? Why don't you want to use that?Tadeo
@Tadeo Because there might be more than two abstract methods, and I'd rather not have half the bytes in my source be hard-to-maintain boilerplate.Sufficient
Hard to maintain? That's write-once one-liners. You'd have to maintain whatever mapping syntax you can come up with too.Tadeo
Why can't you make the two Impl classes derive from Interface and then make the concrete class inherit from the desired intermediate implementation class?Iron
@Tadeo Granted, the solution I have in mind suffers some of the same brittle design, but it would a) reduce typing, b) be resistant to formal parameter name changes, and c) could perhaps be better optimized by the compiler (think defaulted constructors vs hand-rolling an empty constructor).Sufficient
What sort of syntax are you proposing? (Your a) is pretty weak, the only potentially "extraneous" chars in the delegate thing are the curlies, for b) formal parameter names? the types are what matters, the names are essentially just for humans, c) can you come up with a real example?)Tadeo
@KerrekSB I might not control the classes that I want to use as implementation. Also, that might require me to change how I derive from the implementers (I'd like to keep private inheritance).Sufficient
@Tadeo The amount you type is important. Every character you type is an opportunity to make a mistake. If there is a non-void return type, that's an extra return keyword, and if you have 20 params, that's extra typing as you have to forward those params by name to the implementer. I cant think of a better optimization example than the defaulted functions... there is a reason they added defaulted and deleted functions in the latest standard.Sufficient
This idea is similar to what I proposed in C#, which I called roles. Similar to traits, they'd offer a way to resolve conflicts, with aliases and exclusions. Also, take a look at NRoles.Viscosity
@Jordão Interesting. Your Roles idea is an elevation of the template method pattern into the language, and you are correct that there are some similarities with what I'm asking, particularly the "impose" behavior you propose. In my example, method2 is begging for something like concept maps from the Concepts proposal that was dropped from C++ 11, but instead of being externally defined, a class definition is participating directly in the mapping.Sufficient
Michael - Could you explain in the question whether it would make a difference to you (if the maintainability and semantics stayed the same) if the (now) private base classes would actually be held as members of the InterfaceImpl class?Fortunetelling
@Martin - I actually prefer containment over private inheritance, but I don't quite see how that would be an improvement, since then you also have to provide an identifier to name each implementing class. I can make a clarification in the question. Thanks for pointing that out.Sufficient
At first I was a little unconviced by your arguments, but then I realized that all you asked for was just a way to alias a member function (give it another name), which seems sensible: namespace aliases have made my life easier more than once. Template aliases also have made their way into the language, so why not allow aliases on method names? The syntax could be similar to the one used for templates: using method2 = MethodTwoImpl::myFunc. However, I suppose that there could be many potential issues to take into account, but it is worth thinking about in my opinion.Armhole
To add on my previous comment, we already have a way of aliasing objects (references), types (typedefs), templates (templates aliases), namespaces (namespaces aliases), so why not functions?Armhole
@LucTouraille - :-) This is exactly where I'm headed with this... I was considering basing it off of the deleted and defaulted method syntax instead.Sufficient
I'm still thoroughly confused. Are you trying to simplify the Wrapper Pattern? Or are you trying to make your Interface more "idiot proof"? "Less code" does not necessarily make things easier to maintain; it often works opposite of "hard to get wrong" (c.f. "Obfuscated C").Shrubbery
@JohnPrice - Wrapper, Decorator, Adaptor, Template-Method; whatever pattern you want to call it, I want to reduce the redundancy of implementing a function that simply calls a another function with exactly the same a parameters in exactly the same order.Sufficient
V
4

An other option (at least if using MS VC++) is to use virtual inheritance:

struct MyInterface
{
    virtual void Method1() = 0;
    virtual void Method2() = 0;
};

class Method1Impl : public virtual MyInterface
{
    virtual void Method1() { _tprintf( _T("Method1\n") ); }
};

class Method2Impl : public virtual MyInterface
{
    virtual void Method2() { _tprintf( _T("Method2\n") ); }
};

class InterfaceImpl : public virtual MyInterface,
                      private Method1Impl,
                      private Method2Impl
{
};

void TestWeirdInterfaceImpl()
{
    MyInterface*    pItf = new InterfaceImpl();

    pItf->Method1();
    pItf->Method2();
}

While this seems to work and satisfy what you are looking for (asside from C4250 warning that you will have to suppress with a #pragma), this wouldn't be my approach. (I believe virtual inheritance is still not something that supported across all compilers, but I could be wrong).

I would probably go with containment and once boilerplate code is identifier, wrap it into some kind of macro map (similar to maps in ATL or MFC) that would make it really, really difficult to ever screw it up.

So this would be my macro approach:

struct MyInterface
{
    virtual float Method1( int x ) = 0;
    virtual int Method2( float a, float b ) = 0;
    virtual void Method3( const TCHAR* sz ) = 0;
};

class Method1Impl
{
public:
    float Method1( int x ) {
        _tprintf( _T("Method1: %d\n"), x ); return 5.0;
    }
};

class Method2and3Impl
{
public:
    int Method2( float a, float b ) {
        _tprintf( _T("Method2: %f, %f\n"), a, b ); return 666;
    }

    void Method3( const TCHAR* sz ) {
        _tprintf( _T("Method3: %s"), sz );
    }
};


#define DECLARE_METHOD0( MethodName, Obj, R )   \
    virtual R MethodName() { return Obj.MethodName(); }

#define DECLARE_METHOD1( MethodName, Obj, R, A1 )   \
    virtual R MethodName( A1 a1 ) { return Obj.MethodName( a1 ); }

#define DECLARE_METHOD2( MethodName, Obj, R, A1, A2 )   \
    virtual R MethodName( A1 a1, A2 a2 ) { return Obj.MethodName( a1, a2 ); }


class InterfaceImpl : public MyInterface
{
public:
    DECLARE_METHOD1( Method1, m_method1Impl, float, int );
    DECLARE_METHOD2( Method2, m_method2and3Impl, int, float, float );
    DECLARE_METHOD1( Method3, m_method2and3Impl, void, const TCHAR* );

private:
    Method1Impl         m_method1Impl;
    Method2and3Impl     m_method2and3Impl;
};

void TestWeirdInterfaceImpl()
{
    MyInterface*    pItf = new InterfaceImpl();

    pItf->Method1( 86 );
    pItf->Method2( 42.0, 24.0 );
    pItf->Method3( _T("hi") );
}

Until C++ gods grace us with variadic macros, you'll have to declare one for each number of parameters you have. Also if you used multiple inheritance, potentially you wouldn't need the second "Obj" param, but as I've said before, I'd avoid multiple inheritance if there's another solution, which in this case is one extra param.

Yet a third option could be something that authors of Pragmatic Programmer seem to advocate a lot. If you have a ton of cookie cutter code that you don't want to repeat because, as you pointed out, it introduces human error. Define your own language and write a code generator script (python, perl...) to auto-create the actual code. In this case you could almost point at an interface, and have the script write the text out for you. I haven't tried doing this kind of thing myself, but lately have been wanting to use it somewhere just to see and evaluate the outcome.

Visitation answered 14/11, 2011 at 17:25 Comment(10)
I don't want an InterfaceImpl object to ever be known as a Method1Impl object, which is what can happen with public inheritance. Even more so, I may not control all of the MethodXImpl classes that I'd like to use, so I can't just go make those derive from the interface class.Sufficient
well, I fixed your public vs. private issue. Now the class looks like it just has MyInterface and no one knows anything about MethodXImpl. However, if you have no control over MethodXImpl, then I think you are stuck with containment, which means mapping functions between interface and each of the classes. Unless you can force people to make MethodXImpl classes templates which accept "class TBase" as a template parameter, so you can specify any base you want. But again, you are forcing specific requirements onto MethodXImpl classes.Visitation
Actually, as much as I dislike macros, this could be a situation where it would simplify the code in InterfaceImpl. I don't think that I could accept an answer that imposes restrictions (other than perhaps method signature) on the implementing classes. If you want to submit a second answer that uses macros, I'd be inclined to consider it as the best current answer.Sufficient
are you using C++0x (or C++11) compiler?Visitation
yeah, nvm on C++0x. no help here (that I can see atm)Visitation
If there are any features of C++11 (which is really just C++ now) that can help, that would be beautiful.Sufficient
yeah, I played around with few things, but forgot that template functions can't override virtual ones. So in the end you have your implementation class which is deriving from an interface. No matter how you slice it, you need to declare a function with matching return value and parameter types. And macro, or some other code generation method is the only way that i can see (for now)Visitation
+1 for macro proposal. Sometimes macros are not that bad at all. (And note that MSVC and GCC both have variadic macros.)Fortunetelling
@Martin - I agree, the availability of variadic macros makes this solution quite attractive.Sufficient
not the way they are currently implemented. I checked them out after Martin mentioned variadic macros. There current functionality is quite limited and in this case, you'll still have to do what I did (i.e. define a macro for each number of params). Still learned something new, thanks Martin :)Visitation
M
0

This is sort of ugly and may bloat the executable size, but what about

#include <iostream>

class Interface
{
public:
    virtual void method1() = 0;
    virtual void method2(int x) = 0;
};

template<typename T>
class MethodOneImpl : public T
{
 private:
    void method1(int x)
    { std::cout << "MethodOneImpl::method1() " << x << std::endl; }

 public:
    void method1() { method1(0); }
};

template<typename T>
class MethodTwoImpl : public T
{
 public:
    void method2(int x)
    { std::cout << "MethodTwoImpl::myFunc(x)" << x << std::endl; }
};

class InterfaceImpl : public MethodTwoImpl<MethodOneImpl<Interface> >
{
};

int main()
{
    InterfaceImpl impl;
    impl.method1();
    impl.method2(0);
}
Marikomaril answered 14/11, 2011 at 16:45 Comment(4)
I don't want an InterfaceImpl to be a MethodTwoImpl or a MethodOneImpl though. Thus the reason for private inheritance. I will edit the question to clarify the use cases that I want to disallow.Sufficient
I tried DXM's virtual class idea and it doesn't work with private inheritance on the compilers I tried.Marikomaril
my solution sucks :) I'd never use it myself. In fact, I don't think I ever used virtual base classes. However, as I've stated before, it does appear to work with MSVC and everything I posted, I have executed.Visitation
@Visitation The one you have now works fine. I don't know what I was doing wrong – the scratch buffer is long gone.Marikomaril
T
0
class AbsInterface
{
    // this is a simple interface class.
public:
    virtual void Method1() = 0;
    virtual void Method2() = 0;
};

class Functor1
{
public:
    void operator () ()
    {
        printf("This Is Void Functor1");
    }
};

class Functor2
{
public:
    void operator () ()
    {
    printf("This Is void Functor2");
    }
};

template <class T1, class T2>
class DerivedTemplateClass : public AbsInterface
{
public:
    virtual void Method1() { T1()(); }
    virtual void Method2() { T2()(); }
};

void main()
{
    DerivedTemplateClass<Stratege1, Stratege2> instance;
    instance.Method1();
    instance.Method2();
}

as you can see, I used Functor. You could work with template and functor.

Tungstic answered 15/11, 2011 at 8:45 Comment(1)
This answer totally misses the point. I want to simplify the virtual void Method1() { T1()(); } definition. Using templates makes the pattern more generic, but not simpler.Sufficient
T
0

Does this serve your purpose? It maintains the interface relationship and gives you maintainable code without having any consideration of client code.

Separating each method in functionoid and giving you the power to control the prototype of each method of the different base class.

#include <iostream>
#include <memory>
using namespace std;

    //No Control over this.
    class MethodOneImpl
    {
     private:
        void method1(int x)
        { std::cout << "MethodOneImpl::method1() " << x << std::endl; }

     public:
        void method1() { method1(0); }
    };

    class MethodTwoImpl
    {
     public:
        void myFunc(int x)
        { std::cout << "MethodTwoImpl::myFunc(x)" << x << std::endl; }
    };

    //*************************//

    class Interface
    {
    public:
        virtual void method1() = 0;
        virtual void method2(int x) = 0;
    };

    //This is what i would do. //

    class BaseFuncType
    {
    //no pure virtual
    void Call()
          {
              throw "error";
          }
    void Call(int x)
          {
              throw "error";
          }
    };


    class Method1: public BaseFuncType
    {
    auto_ptr<MethodOneImpl> MethodPtr;
    public:
    Method1()
    {
        MethodPtr.reset(new MethodOneImpl());
    }
    virtual int Call() 
    {
        MethodPtr->method1(); 
    }
    };

    class Method2: public BaseFuncType
    {
        auto_ptr<MethodTwoImpl> MethodPtr;
    public:
    Method2()
    {
       MethodPtr.reset(new MethodTwoImpl());
    }
    virtual int Call(int x) 
    {
        MethodPtr->myFunc(x);
    }
    };
    template <class T1>
    class MethodFactory
    {
    private:
       T1 methodObj;
    public:
    void CallMethod()
    {
       methodObj.Call();
    }
    void CallMethod(int x)
    {
       methodObj.Call(x);
    }

    };
    class InterfaceImpl : public Interface
    {
        auto_ptr<MethodFactory> factory;
    public:
        virtual void method1() 
        {   
            factory.reset(new MethodFactory<Method1>());
            factory->CallMethod(); 
        }
        virtual void method2(int x) 
        { 
            factory.reset(new MethodFactory<Method2>());
            factory->CallMethod(x); 
        }
    };

    int main()
    {
        auto_ptr<Interface> inf;
        inf.reset(new InterfaceImpl);
        inf->method1();
        inf->method2(10);

        // This should be disallowed!
        // std::unique_ptr<MethodOneImpl> moi;
        // moi.reset(new InterfaceImpl);
    }
Theogony answered 15/11, 2011 at 20:25 Comment(2)
This code has memory leaks out the wazoo. What happens if method2 evolves to take an additional parameter? Doesn't the addition of the MethodFactory just create more work to do when something about the interface changes?Sufficient
Is memory leak really your problem? That can be easily fixed. I wanted to show the minimal code that addresses your problem. Another thing is, you code to the interface which normally are fixed before you would write the code. If method2 evolves to have another parameter or your interface changes all of sudden because we all are agile, you dont change the InterfaceImpl, Only change would be in the MethodFactory.Theogony
D
0

It seems impossible to bring MethodOneImpl / MethodTwoImpl into the scope of Interface without having them inherit from Interface because they will not fill the Virtual Table if they don't. C++ misses something like the keyword implements from other languages.

So you are stuck with the virtual inheritence thing unless realize/accept that what you are looking for is just a bridge pattern, which does not satisfy requirement a) (you shall write more code), midly b) (code not necessarly difficult to maintain) and may satisfy c).

Here (another) possible solution (with only method though to reduce bloat)

class Interface 
{ public:
 virtual void method1() {return impl_->method1();}
 private:
     Interface() {}
 protected:
     struct Impl {
         virtual void method1() = 0; };
     std::shared_ptr<Impl> impl_;
     Interface(const std::shared_ptr<Impl> &impl) : impl_(impl) {}
};

  class InterfaceImpl : public Interface
{
  struct Impl : public Interface::Impl {
      void method1()  { std::cout << "InterfaceImpl::method1() " << std::endl; }  } ;
public:
    InterfaceImpl() : Interface(std::shared_ptr<Impl> (new Impl)) {}
      };

  template <class T>
  class GenericInterfaceImpl :  public Interface {
      struct Impl : public Interface::Impl {
          Impl( T &t) : t_(t) {}
          void method1() { t_.method1() ; } 
          T t_; };
public:
    GenericInterfaceImpl() : Interface(std::shared_ptr<Impl> (new Impl(T()))) {}
      };

 struct AMethod1Impl {
     void method1() { std::cout << "AMethod1Impl::method1() " << std::endl; }  } ;

struct AnotherMethod1Impl_not_working {
     void method1_not_present() { std::cout << "AnotherMethod1Impl_not_working ::method1_not_present() " << std::endl; }  } ;

int main() {
 // compilation of next line would fail 
 // (lame attempt to simulate ompilation fail when pure function not implemented)
 // Interface inf;

 std::unique_ptr<Interface> inf;
 inf.reset(new InterfaceImpl);
 inf->method1();
 inf.reset(new GenericInterfaceImpl<AMethod1Impl>() );
 inf->method1();

 // compilation of next line would fail
 // inf.reset(new GenericInterfaceImpl<AnotherMethod1Impl_not_working>() );

    }
Dawnedawson answered 17/11, 2011 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.