1

I have written a macro that can be added around a class definition that will effectively final-ize it (prevent subclassing). This method of finalization is a fairly common technique and is noted in the C++ FAQ:

#define FINALIZE(NAME,...) \
    NAME##Finalizer {\
        private:\
            friend class NAME;\
            inline NAME##Finalizer(void) {}\
    };\
    class NAME : public virtual NAME##Finalizer, __VA_ARGS__

For example:

class FINALIZE(Fred) {};
//The above line evaluates to:
//  class FredFinalizer {
//      private:
//          friend class Fred;
//          inline FredFinalizer(void) {}
//  };
//  class Fred : public virtual FredFinalizer, {};

class George {};
class FINALIZE(Fred2, public George) {};
//The above line evaluates to:
//  class Fred2Finalizer {
//      private:
//          friend class Fred2;
//          inline Fred2Finalizer(void) {}
//  };
//  class Fred2 : public virtual Fred2Finalizer, public George {};

class Velma : public Fred { Velma(void) {} /*Note compile error*/ };
class Velma2 : public Fred2 { Velma2(void) {} /*Note compile error*/ };

This works splendidly. The problem is that I would now like a way to "disable" the macro. That is:

class FINALIZE(Fred) {};
//The above line evaluates to:
//  class Fred {};

class George {};
class FINALIZE(Fred2, public George) {};
//The above line evaluates to:
//  class Fred2 : public George {};

The issue is that the class Fred2 : public George {}; clashes with the case where there's no inheritance (class Fred {};): in the first case, there must always be a colon, in the second case, there mustn't. Note that this isn't an issue when the macro is enabled because the class always inherits from at least the NAME##Finalizer class.

How can I write a macro that does this?

geometrian
  • 14,775
  • 10
  • 56
  • 132
  • Is there a reason that you want the solution to be a macro? – Drew Dormann Sep 15 '12 at 19:20
  • Would you accept a dummy base class? – nneonneo Sep 15 '12 at 19:39
  • Not that it matters to the question, but you may want to privatize the virtual base class if this is your ultimate goal (to prevent inheritance). It seems that would be a pretty key part of the description you linked. – WhozCraig Sep 15 '12 at 20:45
  • @DeadMG He's doing it to prevent a class be being a base to a derivation, something the likes of which a Java-holic is all-too-familiar with. – WhozCraig Sep 15 '12 at 20:48
  • CraigNelson, correct, although I object to the implicit notion that I like Java. On the contrary, with the exception of C#, I hate no modern language more. DeadMG, I've been getting complaints because users stupidly inherit from classes that are clearly marked as "do not inherit from". A compile-time solution is in order. – geometrian Sep 15 '12 at 23:28

1 Answers1

1

I'd advice to make two macros, one for preparing finalizer, one for using:

#define PREP_FINALIZE(NAME) \
    class NAME##Finalizer {\
        private:\
            friend class NAME;\
            inline NAME##Finalizer() {}\
    }

#define FINALIZE(NAME) virtual NAME##Finalizer

So in your example:

PREP_FINALIZE(Fred);
class Fred : FINALIZE(Fred) {};

class George {};
PREP_FINALIZE(Fred2);
class Fred2 : FINALIZE(Fred2), public George {};

But what is more important you forget about to make copy constructor private. Without it your way can be cheating very easily:

// cheating :D
class Velma : public Fred, FINALIZE(Fred) { 
   Velma() : FredFinalizer(*(FredFinalizer*)0) {} /*Who said this is compiler error */ 
};

Make copy constructor of finalizer private. Or use my answer to very similar question.

For C++11 just use final ;)

Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • Thanks; I was trying to avoid making two macros, just for aesthetic reasons. Are two macros really required? +1 for catching the copy constructor. – geometrian Sep 15 '12 at 23:33
  • I believe separate macros are just easier. However you can think of FINALIZE: for no base class cases and FINALIZEM for cases with bases classes. Still two macros but for every class you uses only one. – PiotrNycz Sep 15 '12 at 23:44
  • I couldn't find a better solution, so we'll go with it. Thanks, – geometrian Oct 21 '12 at 15:54