-1

In following example notice the TERMINATOR which is instance of AbstractMiddleware:

#include <iostream>

class AbstractMiddleware { // base class for middleware
public:
    AbstractMiddleware();
    virtual ~AbstractMiddleware() { }
    virtual void call() = 0; // typical middleware does something and calls next one in chain
    AbstractMiddleware* next;
};

static class : public AbstractMiddleware {
public:
    void call() override {
        std::cout << "TERMINATE" << std::endl;
    }
} TERMINATOR; // dummy middleware to terminate the chain

AbstractMiddleware::AbstractMiddleware():
    next(&TERMINATOR) // each middleware is terminated by default
{ }

I don't like that TERMINATOR is declared outside of AbstractMiddleware. In fact, it's the only place where TERMINATOR is used (for now). Ideally I would like to hide it within AbstractMiddleware as a static field AbstractMiddleware::TERMINATOR, but don't know how.

[EDIT]

It turns out that I was not clear enough in my original question.

As correctly guessed by StoryTeller and Dialecticus, my initial concern was about hiding TERMINATOR instance from common namespace while keeping it visible to AbstractMiddleware and its descendants.

I thought that it will be possible to put it into AbstractMiddleware as static like it's done here: https://stackoverflow.com/a/21197907/947418 But it turns out that it doesn't work in case of abstract classes.

Community
  • 1
  • 1
teq
  • 1,494
  • 1
  • 11
  • 12
  • Is the terminator not a dummy? Does it have an additional interface that needs to be accessible to the descendants? – StoryTeller - Unslander Monica Dec 07 '16 at 12:49
  • TERMINATOR is an instance of anonymous class derived from AbstractMiddleware with call() method defined to do nothing. For all descendants TERMINATOR is instance of AbstractMiddleware. – teq Dec 07 '16 at 13:09

3 Answers3

1

If I understood correctly. You have a tightly coupled construct of the class defined outside of it, and it bothers you. C++ doesn't allow defining inner sub-classes. So why not just expose the pointer itself for clients to use as a terminator?

Header:

class AbstractMiddleware { // base class for middleware
public:
    AbstractMiddleware();
    virtual ~AbstractMiddleware() { }
    virtual void call() = 0; // typical middleware does something and calls next one in chain
    AbstractMiddleware* next;

protected:
  static AbstractMiddleware * const TERMINATOR;
};

Source:

static class : public AbstractMiddleware {
public:
    void call() override {
        std::cout << "TERMINATE" << std::endl;
    }
} TERMINATOR_OBJ; // dummy middleware to terminate the chain

AbstractMiddleware * const AbstractMiddleware::TERMINATOR = &TERMINATOR_OBJ;

AbstractMiddleware::AbstractMiddleware():
    next(TERMINATOR) // each middleware is terminated by default
{ }

The terminator object is now well hidden inside the translation unit of AbstractMiddleware.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Thank you, your approach suits great in my case. The only thing I wanted to clarify is what you mean by "C++ doesn't allow defining inner sub-classes" ? Do you mean defining class B inside class A which inherits from class A ? (Like here: http://stackoverflow.com/questions/2706129/can-a-c-class-include-itself-as-an-attribute) – teq Dec 07 '16 at 12:51
  • 1
    @teq, That's exactly what I meant. But note that the example you linked doesn't define a new class (which requires knowing the complete definition of the base), but rather adds pointers to the base (which requires only knowing the name of the base). – StoryTeller - Unslander Monica Dec 07 '16 at 12:53
  • Just found out that the same approach works if TERMINATOR field is reference: https://gist.github.com/Teq/75153e79bc036d841cdba7ebb44861ff. For me it looks a bit better because it is impossible to not initialize it – teq Dec 09 '16 at 11:38
1

You could move the TERMINATOR code to anonymous (or nameless) namespace somewhere in .cpp file between #include "AbstractMiddleware.h" and constructor definition. That way no one outside of AbstractMiddleware.cpp will know about it:

#include "AbstractMiddleware.h"

namespace
{
    static class : public AbstractMiddleware {
    public:
        void call() override {
            std::cout << "TERMINATE" << std::endl;
        }
    } TERMINATOR; // dummy middleware to terminate the chain
}

AbstractMiddleware::AbstractMiddleware():
    next(&TERMINATOR) // each middleware is terminated by default
{ }
Dialecticus
  • 16,400
  • 7
  • 43
  • 103
  • Thank you. You pointed me to a feature of namespaces which I wasn't aware about. I didn't mention that I need to access that TERMINATOR in descendants of AbstractMiddleware, sorry for that. StoryTeller approach works better in my case. But +1 anyway! – teq Dec 07 '16 at 12:58
1

Does AbstractMiddleware need to be strictly abstract?

class AbstractMiddleware {
protected:
    AbstractMiddleware();
public:
    virtual ~AbstractMiddleware() { }
    virtual void call() { std::cout << "TERMINATE" << std::endl; }
    AbstractMiddleware* next;

private:
  static AbstractMiddleware TERMINATOR;
};

AbstractMiddleware::AbstractMiddleware():
    next(&TERMINATOR)
{ }

AbstractMiddleware AbstractMiddleware::TERMINATOR;
Jeremy
  • 5,055
  • 1
  • 28
  • 44
  • In fact it doesn't and your approach works too. In the end I decided to throw away that TERMINATOR and just check if pointer to next middleware is null before performing actual jump. Main part of the question was about if it's possible to define a static field with type of abstract class within that abstract class. The closest solution I found while investifgating this is to define it as a reference: https://gist.github.com/Teq/75153e79bc036d841cdba7ebb44861ff – teq Dec 09 '16 at 11:47
  • "If it's possible to define a static field with type of abstract class within that abstract class." That's impossible by definition, because (as you allude to in your edit) the static field would need to be an instance of the uninstantiable abstract class. – Jeremy Dec 10 '16 at 08:01