Static Polymorphism

closed account (o3hC5Di1)
Hi everyone,

As the topic indicates, I have a few questions regarding static polymorphism using the CRTP. Please consider following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
template <typename DerivedType>
class base
{
    static_assert(std::is_base_of<base, DerivedType>::value, "Base requires its DerivedType"
                                             " parameter to be a type inheriting from base.");

    public:
        void print_stuff(const std::string& s)
        {
            static_cast<DerivedType*>(this)->print_stuff_impl(s);
        }
};

class derived_a : base<derived_a>
{
    public:
        friend base<derived_a>;

    private:
        void print_stuff_impl(const std::string& s)
        {
            std::cout << "Derived A prints: " << s;
        }
};

class derived_b : base<derived_b>
{
    public:
        friend base<derived_b>;

    private:
        void print_stuff_impl(const std::string& s)
        {
            std::cout << "Derived B prints: " << s;
        }
};



My questions:

Q1: Would this be a good use-case for friend classes, if I want to only provide the interface of base?

Q2: In a program where the type of base to instantiate depends on the platform (for instance 32bit/64 bit, just to give an example), would one do something like:

1
2
3
4
5
6
7
8
#ifdef 32_BIT
    base<derived_a> foo;
#endif
#ifdef 64_BIT
    base<derived_b> foo;
#endif

foo.print_stuff();


That seems to be a bit cluttered, but I wouldn't mind if that's the only way. Also, the values such as 32_BIT would be defined before actually compiling, would this be done by creating a file config.h in a little configure program to be run before make (like ./configure)?

Q3: When would one use Policy-based Design over the CRTP to achieve static polymorphism?

Q4: Since the base class functions basically just wrap a call to their implementation functions, I'm guessing this would be a good time to use perfect forwarding?

Q5: It seems logical to me that static polymorphism is faster than dynamic polymorphism, but as always there are people who have their own thoughts on this: http://stackoverflow.com/a/262276 The answer seems to be leaving out vtable lookup and possible lack of inlining virtual functions? As always performance is a rather subjective topic, so I thought I'd ask your expert opinions on that as well while we're on the topic.

Sorry for another long post with many questions, any answers or comments would be greatly appreciated, as always.

Thanks very much in advance,
All the best,
NwN
Last edited on
closed account (o3hC5Di1)
*polite little bump*

Thanks in advance,
NwN
i love how you've put this in the beginners section :)
> Q3: When would one use Policy-based Design over the CRTP to achieve static polymorphism?

I personally prefer policy parameterization over CRTP (a la the standard library).

The place where CRTP is indispensable seems to be:
In general, CRTP is useful to factor out implementations of interfaces that can only be member functions (for example, constructor, destructors, and subscript operators)
- Nicolai M. Josuttis and David Vandevoorde in 'C++ Templates: The Complete Guide'
http://www.informit.com/articles/article.aspx?p=31473&seqNum=1

To draw an analogy with object-oriented programming:

CRTP is the compile time equivalent of the private (protected) virtual function which can be overriden by a derived class (the template method pattern).

Policy parameterization uses compile-time object composition via a polymorphic interface; for instance std::type_traits<>, std::numpunct<> etc. (the stratyegy pattern).

Yeah, these are anything but beginner questions. If you could move this to General that'd be great.

Q1: Yes, I think it is.

Q2: That's one way of doing it, though you could merge your #endif and #ifdef on lines 3 and 4 into an #elif. You could also save yourself a lot of hastle with pre-compile configurations by using a pre-defined compiler macro for the architecture you're compiling for.
http://sourceforge.net/p/predef/wiki/Architectures/

Q3: One tackles problems from a divide-and-conquer perspective by allowing you to essentially piece together different policies to create a specialized class, and one is a system that allows you to derive from a base class and specialize it for different situations. Pick whichever suits the task at hand better. Personally, I prefer policy-based design.

Q4: If you really care that much about performance, which I admit in some cases is an issue.

Q5: CRTP will have a higher compile-time overhead, but should theoretically be faster than dynamic polymorphism when calling overloaded functions.

Note that my C++ is still somewhat rusty, and my answers to questions 1, 4, and 5 may be complete hogwash.

-Albatross
Last edited on
0) is_base_of requires that Derived is a complete type , this code sample will not compile. You could friend your Derived and declare base's constructor private if you want to prevent incorrect instantiations.
Private inheritance hides your base's public member function, so you can't call stuff() on any derived as written (inherit publicly)
print_stuff and print_stuff_impl should be const

1) it's okay, you could make it simpler by writing friend base; (or, for variety, friend only the base::print_stuff member function). Also, friend declarations don't care for member access specifiers, no need to put them into public:

2) What would "32_BIT" and "64_BIT" mean? Are those sizes of pointers? Sizes of long int? sizes of something else? Perhaps you could use the sizes (or parametrizing on them)?

3) CRTP is just syntax, it can be used to implement Template Pattern (which is the case here: the CRTP base defines a non-virtual interface and provides customization points for the derived classes). It can also be used to implement those policies that need access to the host type. It can also be used to implement expression templates, etc.. That's why it's "recurring"

4) It doesn't appear to gain anything in this case.

5) not every answer on Stackoverflow is a good answer, but as always with performance, measure it
closed account (o3hC5Di1)
Hi everyone,

Thanks very much for your replies. Sorry I didn't get back to you earlier, I got caught up with a couple of things. Also my apologies for posting this in the Beginners section, I still consider myself very much a beginner and it's my first babysteps with static polymorphism. Anyway, moved it to General.

Thank you Cubbi for your explanations regarding the static_assert / is_base_of, I hadn't considered that it wouldn't work with incomplete types. Also thanks for the tip on private inheritance, seems that's an easy way to do the trick.

I'll copy the original questions in just for easier reference.

I wrote:
Q1: Would this be a good use-case for friend classes, if I want to only provide the interface of base?


Interesting that friend base; works. I would have expected for the compiler to complain that "base" expects template parameters?


I wrote:
Q2: In a program where the type of base to instantiate depends on the platform (for instance 32bit/64 bit, just to give an example)...


Thanks Albatross for that link, I wasn't aware of those. Cubbi was probably right in that 32_BIT and 64_BIT don't mean much, it was a bad example on my part. What I plan on using this for is having different data storage backends, as to separate the data backend from the other code, ie:

1
2
3
4
5
6
7
8
#ifdef RDBMS
    base_mapper<rdbms_mapper> m;
#endif
#ifdef  XML
    base_mapper<xml_mapper> m;
#endif

m.create(stuff);


I wrote:
Q3: When would one use Policy-based Design over the CRTP to achieve static polymorphism?


Thanks JLBorges for your comparison to OO, that made it click. I'm taking away from all your explanations that CRTP is for providing a single interface, implemented differently in derived classes and Policy Based Design is more of a "mix and match" thing where you put together the interface you need from the available parts.

I wrote:
Q4: Since the base class functions basically just wrap a call to their implementation functions, I'm guessing this would be a good time to use perfect forwarding?


Cubbi wrote:
4) It doesn't appear to gain anything in this case.


Could you please elaborate a little on that? Note that I just corrected a typo, I forgot to pass on the argument to the _impl function, might that be the reason? I'm just asking to check whether I have a flwed understanding of perfect forwarding.


Thanks very much once again everyone for your kind replies, very much appreciated.

All the best,
NwN


Interesting that friend base; works

it's a C++11 thing, in C++98 you would write friend class base;.

It doesn't appear to gain anything in this case.
Could you please elaborate a little on that?

well, it's a string, on which you're only calling const member functions, it's passed by reference to const, which works equally well with lvalues and rvalues.
Now if you were to make a local copy of your parameter, then perfect forwarding would make it possible to copy from lvalues and move from rvalues, but "print_stuff" doesn't sound like it would need that.
closed account (o3hC5Di1)
Ah yes, I see, that makes sense. I think I need to put a little more thought into my examples next time.

Thanks very much for taking the time to explain.

All the best,
NwN
Topic archived. No new replies allowed.