Actually that test program contradicts what I said --- my
IsDestructorPrivateOrDeleted produces exactly the same result as
!is_destructible,  and is_destructible does return 0 for the class with
private destructor. So you could just use that!

Benoit


2014-05-28 16:51 GMT-04:00 Benoit Jacob <jacob.benoi...@gmail.com>:

> Awesome work!
>
> By the way, I just figured a way that you could static_assert so that at
> least on supporting C++11 compilers, we would automatically catch this.
>
> The basic C++11 tool here is std::is_destructible from <type_traits>, but
> it has a problem: it only returns false if the destructor is deleted, it
> doesn't return false if the destructor is private. However, the example
> below shows how we can still achieve what we want by using wrapping the
> class that we are interested in as a member of a helper templated struct:
>
>
>
> #include <type_traits>
> #include <iostream>
>
> class ClassWithDeletedDtor {
>   ~ClassWithDeletedDtor() = delete;
> };
>
> class ClassWithPrivateDtor {
>   ~ClassWithPrivateDtor() {}
> };
>
> class ClassWithPublicDtor {
> public:
>   ~ClassWithPublicDtor() {}
> };
>
> template <typename T>
> class IsDestructorPrivateOrDeletedHelper {
>   T x;
> };
>
> template <typename T>
> struct IsDestructorPrivateOrDeleted
> {
>   static const bool value =
> !std::is_destructible<IsDestructorPrivateOrDeletedHelper<T>>::value;
> };
>
> int main() {
> #define PRINT(x) std::cerr << #x " = " << (x) << std::endl;
>
>   PRINT(std::is_destructible<ClassWithDeletedDtor>::value);
>   PRINT(std::is_destructible<ClassWithPrivateDtor>::value);
>   PRINT(std::is_destructible<ClassWithPublicDtor>::value);
>
>   std::cerr << std::endl;
>
>   PRINT(IsDestructorPrivateOrDeleted<ClassWithDeletedDtor>::value);
>   PRINT(IsDestructorPrivateOrDeleted<ClassWithPrivateDtor>::value);
>   PRINT(IsDestructorPrivateOrDeleted<ClassWithPublicDtor>::value);
> }
>
>
> Output:
>
>
> std::is_destructible<ClassWithDeletedDtor>::value = 0
> std::is_destructible<ClassWithPrivateDtor>::value = 0
> std::is_destructible<ClassWithPublicDtor>::value = 1
>
> IsDestructorPrivateOrDeleted<ClassWithDeletedDtor>::value = 1
> IsDestructorPrivateOrDeleted<ClassWithPrivateDtor>::value = 1
> IsDestructorPrivateOrDeleted<ClassWithPublicDtor>::value = 0
>
>
> If you also want to require classes to be final, C++11 <type_traits> also
> has std::is_final for that.
>
> Cheers,
> Benoit
>
>
> 2014-05-28 16:24 GMT-04:00 Daniel Holbert <dholb...@mozilla.com>:
>
> Hi dev-platform,
>>
>> PSA: if you are adding a concrete class with AddRef/Release
>> implementations (e.g. via NS_INLINE_DECL_REFCOUNTING), please be aware
>> of the following best-practices:
>>
>>  (a) Your class should have an explicitly-declared non-public
>> destructor. (should be 'private' or 'protected')
>>
>>  (b) Your class should be labeled as MOZ_FINAL (or, see below).
>>
>>
>> WHY THIS IS A GOOD IDEA
>> =======================
>> We'd like to ensure that refcounted objects are *only* deleted via their
>> ::Release() methods.  Otherwise, we're potentially susceptible to
>> double-free bugs.
>>
>> We can go a long way towards enforcing this rule at compile-time by
>> giving these classes non-public destructors.  This prevents a whole
>> category of double-free bugs.
>>
>> In particular: if your class has a public destructor (the default), then
>> it's easy for you or someone else to accidentally declare an instance on
>> the stack or as a member-variable in another class, like so:
>>     MyClass foo;
>> This is *extremely* dangerous. If any code wraps 'foo' in a nsRefPtr
>> (say, if some function that we pass 'foo' or '&foo' into declares a
>> nsRefPtr to it for some reason), then we'll get a double-free. The
>> object will be freed when the nsRefPtr goes out of scope, and then again
>> when the MyClass instance goes out of scope. But if we give MyClass a
>> non-public destructor, then it'll make it a compile error (in most code)
>> to declare a MyClass instance on the stack or as a member-variable.  So
>> we'd catch this bug immediately, at compile-time.
>>
>> So, that explains why a non-public destructor is a good idea. But why
>> MOZ_FINAL?  If your class isn't MOZ_FINAL, then that opens up another
>> route to trigger the same sort of bug -- someone can come along and add
>> a subclass, perhaps not realizing that they're subclassing a refcounted
>> class, and the subclass will (by default) have a public destructor,
>> which means then that anyone can declare
>>   MySubclass foo;
>> and run into the exact same problem with the subclass.  A MOZ_FINAL
>> annotation will prevent that by keeping people from naively adding
>> subclasses.
>>
>> BUT WHAT IF I NEED SUBCLASSES
>> =============================
>> First, if your class is abstract, then it shouldn't have AddRef/Release
>> implementations to begin with.  Those belong on the concrete subclasses
>> -- not on your abstract base class.
>>
>> But if your class is concrete and refcounted and needs to have
>> subclasses, then:
>>  - Your base class *and each of its subclasses* should have virtual,
>> protected destructors, to prevent the "MySubclass foo;" problem
>> mentioned above.
>>  - Your subclasses themselves should also probably be declared as
>> "MOZ_FINAL", to keep someone from naively adding another subclass
>> without recognizing the above.
>>  - Your subclasses should definitely *not* declare their own
>> AddRef/Release methods. (They should share the base class's methods &
>> refcount.)
>>
>> For more information, see
>> https://bugzilla.mozilla.org/show_bug.cgi?id=984786 , where I've fixed
>> this sort of thing in a bunch of existing classes.  I definitely didn't
>> catch everything there, so please feel encouraged to continue this work
>> in other bugs. (And if you catch any cases that look like potential
>> double-frees, mark them as security-sensitive.)
>>
>> Thanks!
>> ~Daniel
>> _______________________________________________
>> dev-platform mailing list
>> dev-platform@lists.mozilla.org
>> https://lists.mozilla.org/listinfo/dev-platform
>>
>
>
_______________________________________________
dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to