Interest in a "non derivable" utility

Is there any interest in a small utility which let developers to define pseudo-final classes, meaning that it is not possible to instantiate objects belonging to derived classes. Unfortunately, I could test my tool only on a Linux platform with the compiler gcc 4.1.1 and I am not sure the technique I used is portable to other compilers. Usage --------------------------------------------------------------------// Suppose you want to define a final class Foo, then you have to write class Foo : BOOST_NON_DERIVABLE { // .... }; Now, you try to subclass it class Goo : public Foo { // ... }; When you use inheritance it is quite obvious, you want to instantiate objects of the classes involved in the hierarchy; thus, somewhere you will probably write Goo goo; // a variant or a member of type Goo But the compiler will produce an error like that utility/nonderivable.hpp: In constructor 'Goo::Goo()': utility/nonderivable.hpp:23: error: 'boost::nonderivable_helper::nonderivable_helper()' is protected main.cpp:14: error: within this context utility/nonderivable.hpp:30: error: 'boost::nonderivable_helper::~nonderivable_helper()' is protected main.cpp:14: error: within this context main.cpp:14: error: no matching function for call to 'Foo::Foo()' main.cpp:9: note: candidates are: Foo::Foo(int) main.cpp:8: note: Foo::Foo(const Foo&) main.cpp: In function 'int main()': main.cpp:26: note: synthesized method 'Goo::Goo()' first required here utility/nonderivable.hpp: In destructor 'Goo::~Goo()': utility/nonderivable.hpp:30: error: 'boost::nonderivable_helper::~nonderivable_helper()' is protected main.cpp:14: error: within this context main.cpp: In function 'int main()': main.cpp:26: note: synthesized method 'Goo::~Goo()' first required here Best regards, Manuel Fiorelli

1. Never though about enforcing derivation before, but yes, it looks as it may be useful. Especially if the utility class does not increase the size of the final objects. 2. The technique of using protected constructor and virtual inheritance to stop derivation does not look compiler-specific, limited to gcc 4.1.1. Just in case, I tested your codes under MinGW gcc 3.4.2 and under MSVC++ 8. Yes, the utility stops derivation. The following line is OK: Foo foo; but line Goo goo; does not compile. 3. If the final class is itself derived, using the utility involves multiple inheritances. Fortunately, it looks as in this context it is fine. Utility class does not have data members, and, any case, "rhombic" inheritance is impossible. 4. I still do not see why you need protected destructor: is not enough to have a protected constructor? Why is it bad to destroy an object of a final class by pointer? 5. It probably would be better to put the utility in a nested namespace under boost::. See, for example, noncopyable.hhp. Sincerely, Yura. On 3/3/07, Manuel Fiorelli <manuel.fiorelli@gmail.com> wrote:
Is there any interest in a small utility which let developers to define pseudo-final classes, meaning that it is not possible to instantiate objects belonging to derived classes.
Unfortunately, I could test my tool only on a Linux platform with the compiler gcc 4.1.1 and I am not sure the technique I used is portable to other compilers.
Usage --------------------------------------------------------------------// Suppose you want to define a final class Foo, then you have to write
class Foo : BOOST_NON_DERIVABLE { // .... };
Now, you try to subclass it
class Goo : public Foo { // ... };
When you use inheritance it is quite obvious, you want to instantiate objects of the classes involved in the hierarchy; thus, somewhere you will probably write
Goo goo; // a variant or a member of type Goo
But the compiler will produce an error like that
utility/nonderivable.hpp: In constructor 'Goo::Goo()': utility/nonderivable.hpp:23: error: 'boost::nonderivable_helper::nonderivable_helper()' is protected main.cpp:14: error: within this context utility/nonderivable.hpp:30: error: 'boost::nonderivable_helper::~nonderivable_helper()' is protected main.cpp:14: error: within this context main.cpp:14: error: no matching function for call to 'Foo::Foo()' main.cpp:9: note: candidates are: Foo::Foo(int) main.cpp:8: note: Foo::Foo(const Foo&) main.cpp: In function 'int main()': main.cpp:26: note: synthesized method 'Goo::Goo()' first required here utility/nonderivable.hpp: In destructor 'Goo::~Goo()': utility/nonderivable.hpp:30: error: 'boost::nonderivable_helper::~nonderivable_helper()' is protected main.cpp:14: error: within this context main.cpp: In function 'int main()': main.cpp:26: note: synthesized method 'Goo::~Goo()' first required here
Best regards, Manuel Fiorelli
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

I thank you a lot for the suggestions and I want to say that: - I kept the original idea from Java Core Vol. 1 (by Cay Horstmann), where the author states that in C++ there aren't final classes, but they could be simulated through virtual inheritance....thus I tried to discovery that trick. - there is no reason to use a protected destructor; since I use private inheritance, automatic conversion of pointers from the derived class to the base class isn't possible, thus there is no reason to prevent accidental delete trough base class pointers...I realized it only after the submission... - unfortunately, I just discovered that the following code compiles (violating the "non derivable" semantics) struct Foo : BOOST_NON_DERIVABLE { }; struct Goo : public Foo, BOOST_NON_DERIVABLE { }; Foo foo; // a variant or a class member Goo goo; I suppose it is due to the fact that the same object of class nonderivable_helper is shared among foo and goo, thus violating the assumption that nonderivable_helper is not visible in most derived classes. I know that standard C++ prohibits template parameters and typedefs in friend declarations, thus the only way to eliminate the problem seems to use an ad-hoc non_derivable helper for each class, but this should require a less clean approach: #define final_class( class_name ) struct nonderivable_helper_ ## class_name \ {\ friend class class_name;\ private:\ nonderivable_helper_ ## class_name () {}\ };\ class class_name : private virtual nonderivable_helper_ ## class_name Now, one can rewrite the test as follows: final_class(Foo) { }; final_class(Goo), public Foo { }; Foo foo; // a variant or member Goo goo; and (as expected) it doesn't compile. However, I found this technique is not convenient since: 1) it violates common C++ syntax ( what is a final_class keyword? ) 2) it is not applicable to class template, which is the reason why I definitely disapprove this solution. I hope to find a way to enforce the library...maybe there is something useful in Boost Preprocessor Library. - Can someone say me what is the reason to use a nested namespace....what does it mean "unintended ADL" Best regards, Manuel Fiorelli www.fioreltech.net

AMDG Manuel Fiorelli <manuel.fiorelli <at> gmail.com> writes:
I know that standard C++ prohibits template parameters and typedefs in friend declarations, thus the only way to eliminate the problem seems to use an ad-hoc non_derivable helper for each class, but this should require a less clean approach:
Even though it is prohibited most compilers allow it. You might want to look at http://boost-consulting.com/vault/index.php?&direction=0&order=&directory=Generic%20Programming
- Can someone say me what is the reason to use a nested namespace....what does it mean "unintended ADL"
namespace boost { template<class T0> void f(const T0&); } namespace my_namespace { class A {}; class B : BOOST_NON_DERIVABLE, public A {}; void f(const A&); void f() { B b; f(b); //calls boost::f } } In Christ, Steven Watanabe

I didn't see the proposal in the vault.....I just reinvented the Wheel. Best regards, Manuel Fiorelli

Manuel Fiorelli wrote:
I thank you a lot for the suggestions and I want to say that:
- I kept the original idea from Java Core Vol. 1 (by Cay Horstmann), where the author states that in C++ there aren't final classes, but they could be simulated through virtual inheritance....thus I tried to discovery that trick. - there is no reason to use a protected destructor; since I use private inheritance, automatic conversion of pointers from the derived class to the base class isn't possible, thus there is no reason to prevent accidental delete trough base class pointers...I realized it only after the submission... - unfortunately, I just discovered that the following code compiles (violating the "non derivable" semantics)
struct Foo : BOOST_NON_DERIVABLE {
};
struct Goo : public Foo, BOOST_NON_DERIVABLE {
};
Foo foo; // a variant or a class member Goo goo;
But they have to purposefully violate the documented semantic use of the class. This is good IMHO. You've told the user, "Don't try this," and they're saying, "Well, I'm going to do it anyway." They know they aren't supposed to...they're on their own. You can, and should, only do so much semantic enforcement in C++.

I am very proud to see that a few people were interested in my proposal. I know that in the C++ programming language it is very unusual to define a class to be final....but I have to say that I wrote my tool mostly in order to understand certain low level mechanisms, which are involved in the "nonderivable" implementation. A few days ago I found a backdoor in my code, since a compiler would allow to subclass a final class simply by reapplying the BOOST_NON_DERIVABLE macro as follows: class Foo : BOOST_NON_DERIVABLE { }; class Goo : public Foo, BOOST_NON_DERIVABLE { }; Goo goo; // Compiles I first tried to reimplement the library from scratch, but later I realized that it was sufficient to prohibit that a class can derive twice from "nonderivable". In the new implementation, I use a smart trick, which may be explained as follows: I added a pure virtual method , called uniqueness, to the virtual base class nonderivable_helper, then in the intermediate class, called nonderivable, I override it with an empty function....note that the BOOST_NON_DERIVABLE makes the user to derive from the nonderivable....then if you try to subclass twice nonderivable (which is not virtual), then you have only one nonderivable_helper sub object (since it is a virtual base class) and two (o more) final overrider for the nonderivable_helper::uniqueness method, one for each nonderivable sub object. As expected, this causes a compilation error when I use GCC 4.1. If this mechanisms were portable (I ask other users for a test), I think this implementation should be 100% standard compliant and very strong. I developed this library independently and I didn't know that there was a similar tool (called noninheritable) in the Vault in the folder Generic Programming....I make you note that the library are very different: indeed, I believe (?) I used only standard facilities and that there could be interesting techniques in my work (.....I hope there is not another work in the vault :) Best Regards, Manuel Fiorelli www.fioreltech.net 2007/3/5, Noah Roberts <roberts.noah@gmail.com>:
I thank you a lot for the suggestions and I want to say that:
- I kept the original idea from Java Core Vol. 1 (by Cay Horstmann), where the author states that in C++ there aren't final classes, but
Manuel Fiorelli wrote: they
could be simulated through virtual inheritance....thus I tried to discovery that trick. - there is no reason to use a protected destructor; since I use private inheritance, automatic conversion of pointers from the derived class to the base class isn't possible, thus there is no reason to prevent accidental delete trough base class pointers...I realized it only after the submission... - unfortunately, I just discovered that the following code compiles (violating the "non derivable" semantics)
struct Foo : BOOST_NON_DERIVABLE {
};
struct Goo : public Foo, BOOST_NON_DERIVABLE {
};
Foo foo; // a variant or a class member Goo goo;
But they have to purposefully violate the documented semantic use of the class. This is good IMHO. You've told the user, "Don't try this," and they're saying, "Well, I'm going to do it anyway." They know they aren't supposed to...they're on their own. You can, and should, only do so much semantic enforcement in C++.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (4)
-
Manuel Fiorelli
-
Noah Roberts
-
Steven Watanabe
-
Yuriy Koblents-Mishke