[[derived]] attribute for CRTP.

107 views
Skip to first unread message

Giovanni Piero Deretta

unread,
Sep 29, 2015, 10:27:43 AM9/29/15
to ISO C++ Standard - Future Proposals
Hi all,

There is an ongoing effort to define a subset of C++ which is statically checkable as memory safe. In particular the CppCoreGuidelines ban the use of static_cast for down casting.

One common use of static_cast is for the CRTP pattern:

template<class Derived> struct Base
{
     void foo() {
           ...
           static_cast<Derived*>(this)->bar();
     }
};

struct Derived : Base<Derived> {
     void bar() { ... };
};

Which allow replacing some usages of the virtual Template Pattern with templates, for performance and better type checking.
As shown, the patter is safe, as long as the Derived parameter of Base is an actual Derived type of Base *and* the dynamic type of this is actually derived. To conform to the guidelines, the static_cast would need to be replaced with a dynamic_cast and a virtual function added in Base. This can have performance consequences in addition of opening the road to failures at runtime.

A simple way to enforce the restriction is to use an attribute:

template<class Derived>
[[derived(Derived)]]
struct Base {....};

Such attribute asserts that for any object of type T2 which has a base class of type 'Base<T1>', either T2 is T1 or T1 is a direct or indirect base of T2 and Base<T1> is a direct or indirect base of T1.

The semantic of the class definition with or without the attribute is exactly the same. A compiler or static checker decide enforces such an attribute will emit an error every time an object definition, new expression or temporary would violate the assertion.

The alternative is to give the error at type definition instead of object definition, which would help catch the error early and at the place it actually occurs, but it might prevent otherwise safe constructs:

struct Intermediate : Base<Derived> {...}
struct Derived : Intermediate {};

Intermediate x; // this is invalid
Derived y; // this is ok

-- gpd



Ville Voutilainen

unread,
Sep 29, 2015, 10:30:50 AM9/29/15
to ISO C++ Standard - Future Proposals
On 29 September 2015 at 17:27, Giovanni Piero Deretta
<gpde...@gmail.com> wrote:
> Hi all,
>
> There is an ongoing effort to define a subset of C++ which is statically
> checkable as memory safe. In particular the CppCoreGuidelines ban the use of
> static_cast for down casting.

See https://github.com/isocpp/CppCoreGuidelines/issues/83

> A simple way to enforce the restriction is to use an attribute:
>
> template<class Derived>
> [[derived(Derived)]]
> struct Base {....};
>
> Such attribute asserts that for any object of type T2 which has a base class
> of type 'Base<T1>', either T2 is T1 or T1 is a direct or indirect base of T2
> and Base<T1> is a direct or indirect base of T1.

Why don't you just make the constructor of Base protected?

Tony V E

unread,
Sep 29, 2015, 5:07:54 PM9/29/15
to Standard Proposals

Could you just use static_assert and is_base_of ?
(and make sure static analysis tools recognize what you are doing)



--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Arthur O'Dwyer

unread,
Sep 29, 2015, 10:16:14 PM9/29/15
to ISO C++ Standard - Future Proposals
On Tuesday, September 29, 2015 at 7:27:43 AM UTC-7, Giovanni Piero Deretta wrote:

There is an ongoing effort to define a subset of C++ which is statically checkable as memory safe. In particular the CppCoreGuidelines ban the use of static_cast for down casting.

One common use of static_cast is for the CRTP pattern:

template<class Derived> struct Base
{
     void foo() {
           ...
           static_cast<Derived*>(this)->bar();
     }
};

As I understand it from Bjarne and Herb's CppCon talks about the CppCoreGuidelines, this isn't an egregious violation of the Guidelines.
Or rather, it *is*, but it's a violation committed by the implementor of the CRTP base class, not the user of the CRTP base class.
If I'm implementing a CRTP base class, then yeah, I'm going to have to use a downward static_cast; just as if I'm implementing a smart pointer class, yeah, I'm going to have to use an owning raw pointer. At a certain low level, you have to break the guidelines in order to help implement them.

Maybe it's worth catching, since a dumb user could write

    class Foo : Base<Bar> { ... };

and then Base<Bar>::foo() would try to downcast itself to a Bar and everything would break. That's not the CRTP-base-class author's fault; that's really the user's fault, and so it would be nice to be able to catch the bug mechanically.

What is the current state-of-the-art for catching CRTP-engendered bugs such as

    struct S : std::enable_shared_from_this<S> { auto f() { return shared_from_this(); } };
    struct T : std::enable_shared_from_this<S> { auto f() { return shared_from_this(); } };

?

Finally(!), I'll suggest that the obvious spelling for this feature would be

    template<class D> struct Base final(D) {
        // ...
    };

i.e., just as an unadorned "final" in that position means "nobody can inherit from me", "final(Ts...)" in that position should mean "nobody but Ts... (or anyone who visibly inherits from Ts...) can inherit from me."

I don't think this feature is a good idea (I don't like C++11 "final" because it breaks a lot of cute metaprogramming tricks involving inheritance, and because IMO it should have been an attribute to begin with), but if this feature were to be adopted, that would be the spelling of it, IMHO.

–Arthur

Giovanni Piero Deretta

unread,
Sep 30, 2015, 6:13:05 AM9/30/15
to ISO C++ Standard - Future Proposals


On Tuesday, September 29, 2015 at 3:30:50 PM UTC+1, Ville Voutilainen wrote:
On 29 September 2015 at 17:27, Giovanni Piero Deretta
<gpde...@gmail.com> wrote:
> Hi all,
>
> There is an ongoing effort to define a subset of C++ which is statically
> checkable as memory safe. In particular the CppCoreGuidelines ban the use of
> static_cast for down casting.

See https://github.com/isocpp/CppCoreGuidelines/issues/83

 
yes, and the exception need to marked explicitly so that the static checker can validate it.
 
> A simple way to enforce the restriction is to use an attribute:
>
> template<class Derived>
> [[derived(Derived)]]
> struct Base {....};
>
> Such attribute asserts that for any object of type T2 which has a base class
> of type 'Base<T1>', either T2 is T1 or T1 is a direct or indirect base of T2
> and Base<T1> is a direct or indirect base of T1.

Why don't you just make the constructor of Base protected?

That's fine to mitigate issues, but the whole point of the attribute is to statically validating the memory safety of as much code as possible. A protected constructor wouldn't help in this example:

  struct Foo;
  struct Bad : Base<Foo> {} x;

-- gpd


 

Ville Voutilainen

unread,
Sep 30, 2015, 6:33:16 AM9/30/15
to ISO C++ Standard - Future Proposals
On 30 September 2015 at 13:13, Giovanni Piero Deretta
<gpde...@gmail.com> wrote:
>
>
> On Tuesday, September 29, 2015 at 3:30:50 PM UTC+1, Ville Voutilainen wrote:
>>
>> On 29 September 2015 at 17:27, Giovanni Piero Deretta
>> <gpde...@gmail.com> wrote:
>> > Hi all,
>> >
>> > There is an ongoing effort to define a subset of C++ which is statically
>> > checkable as memory safe. In particular the CppCoreGuidelines ban the
>> > use of
>> > static_cast for down casting.
>>
>> See https://github.com/isocpp/CppCoreGuidelines/issues/83
>>
>
> yes, and the exception need to marked explicitly so that the static checker
> can validate it.

Unless the checker will be more lenient about CRTP cases.

>> > A simple way to enforce the restriction is to use an attribute:
>> >
>> > template<class Derived>
>> > [[derived(Derived)]]
>> > struct Base {....};
>> >
>> > Such attribute asserts that for any object of type T2 which has a base
>> > class
>> > of type 'Base<T1>', either T2 is T1 or T1 is a direct or indirect base
>> > of T2
>> > and Base<T1> is a direct or indirect base of T1.
>>
>> Why don't you just make the constructor of Base protected?
>
>
> That's fine to mitigate issues, but the whole point of the attribute is to
> statically validating the memory safety of as much code as possible. A
> protected constructor wouldn't help in this example:
>
> struct Foo;
> struct Bad : Base<Foo> {} x;

Correct, it's not fool-proof - other people pointed out checking
is_base_of, which
would be an additional possibility to make problems less likely in CRTP bases.

Giovanni Piero Deretta

unread,
Sep 30, 2015, 6:56:01 AM9/30/15
to ISO C++ Standard - Future Proposals
On Wednesday, September 30, 2015 at 11:33:16 AM UTC+1, Ville Voutilainen wrote:
On 30 September 2015 at 13:13, Giovanni Piero Deretta
<gpde...@gmail.com> wrote:
>
>
> On Tuesday, September 29, 2015 at 3:30:50 PM UTC+1, Ville Voutilainen wrote:
>>
>> On 29 September 2015 at 17:27, Giovanni Piero Deretta
>> <gpde...@gmail.com> wrote:
>> > Hi all,
>> >
>> > There is an ongoing effort to define a subset of C++ which is statically
>> > checkable as memory safe. In particular the CppCoreGuidelines ban the
>> > use of
>> > static_cast for down casting.
>>
>> See https://github.com/isocpp/CppCoreGuidelines/issues/83
>>
>
> yes, and the exception need to marked explicitly so that the static checker
> can validate it.

Unless the checker will be more lenient about CRTP cases.


The whole point of the checker proposed by Stroustrup and Sutter is that it doesn't have false negatives (only false positives) *and* the checking doesn't require whole program analysis. The checker will have to flag any static_cast to derived in base unless something in the declaration of Base itself marks it safe.

-- gpd

Matthew Woehlke

unread,
Oct 2, 2015, 11:08:56 AM10/2/15
to std-pr...@isocpp.org
On 2015-09-29 10:27, Giovanni Piero Deretta wrote:
> There is an ongoing effort to define a subset of C++ which is statically
> checkable as memory safe. In particular the CppCoreGuidelines ban the use
> of static_cast for down casting.
>
> One common use of static_cast is for the CRTP pattern:
>
> template<class Derived> struct Base
> {
> void foo() {
> ...
> static_cast<Derived*>(this)->bar();
> }
> };

As noted in the inline classes thread¹, CRTP in general could use some
love. What I'd *really* prefer is a way to declare a CRTP class such
that 'this' is *implicitly* converted to the derived type on any use.
That is, name lookup is delayed until the derived type is known. IOW,
this would work:

/* new syntax */ Base
{
int foo()
{
return /*this->*/bar(); // note: no member 'bar' in Base
}
};

class Foo : Base // note: class type not repeated
{
int bar() { return 1; }
};

Foo f;
auto i = f.foo(); // Calls Foo::bar
assert(i == 1);

Now you just don't need to cast at all (not explicitly, anyway; the
compiler does it for you). Naturally, the compiler would also enforce
correct usage... which is *also* harder to screw up in the first place
because you aren't repeating the class name any more.

There is room for debate if derived members should be preferred always
over base members, or only if qualified, and if a qualified call shall
be an error if the derived class has no such member. (And of course
other syntax questions that I didn't even attempt to suggest.) I think
this would be far more useful than a mere attribute, however. Ideally,
we would be able to kill off CRTP entirely... not the concept, but in
the sense that it is "CRTP".


https://groups.google.com/a/isocpp.org/d/msg/std-proposals/u35GIuJECcQ/mtjuTF8ECAAJ)

--
Matthew

Bjorn Reese

unread,
Oct 2, 2015, 12:01:44 PM10/2/15
to std-pr...@isocpp.org
On 09/29/2015 04:27 PM, Giovanni Piero Deretta wrote:

> One common use of static_cast is for the CRTP pattern:

Another use is to cast from an underlying type to an enum class
(e.g. for protocol deserialization.)

Reply all
Reply to author
Forward
0 new messages