Inline All Methods Wherever Possible

Part of the CppHeresy.


Why is this a good idea? You get whammed when you:

-- SunirShah

It's a good idea because ... well, let me quote CppHeresy:

You'll still die in a subtle and unpleasant way when you use method local statics, say if you're writing a Singleton with the familiar instance() method. Either C++ is stupid or MSVC++6 is stupid, but I have found that the compiler will inline the static local's construction everywhere instead of constructing the object once like it's supposed to. Anyone have the standard to check?

What's the problem with implementation files anyway? -- ss

when you use method local statics

FWIW, this is a bug in the microsoft compiler only when optimizations are enabled. It has nothing to do with the limitations of using static data with inlined methods. -- rad


The bad thing about this is that it doesn't allow you to separate implementation from interface. Sounds like you want to be using Java, which is a different language with different strengths. The method proposed here makes it bloody impossible for a client to dynamically link to different implementations of the same declaration file! You've broken just about every good thing about CeePlusPlus deployment, upgrades, dynamically linked libraries, and so on. This is not heresy, it's just ConsideredHarmful. -- RobertDiFalco

Um, if you're right, Java has no way to separate implementation from interface. That is clearly not true, and the Java solution works quite happily in C++. Namely, use inheritance. You have a pure base class with nothing but virtual method declarations, and a concrete derived class which implements them. Client code is written in terms of the base and is insulated from changes to the derived.

When you want different implementations, you use different derived classes. The notion of different configurations using the same name for what are actually different classes, is dubious.

Obviously we're not getting any efficiency benefits from the inline code, but that isn't the aim. Also obviously, in this case we would end up with 2 source files anyway. However, we would be getting more insulation than the usual headers because the usual headers would include instance variables which the base class wouldn't. -- DaveHarris

Agreed, plus, with regard to the dynamic linking, that's quite rare on a per class basis. Where you want it, of course you do want to separate interface from implementation using one of the above techniques. But then again, using AlternateHardAndSoftLayers plus dynamic linkage obviates that need too. As to whether I want to be working in just Java or just Python instead ... no, I want to be working in AlternateHardAndSoftLayers, because in my experience that's faster to code and much more maintainable than just working in a hard language like Java, and yet runs just as fast as regular C++. CppHeresy is meaningless without AlternateHardAndSoftLayers. -- PeterMerel


If you want CeePlusPlus to be Java or Python, why not just use Java or Python!? The fact is that Java uses a different idiom from CeePlusPlus and does not have the same ability to separate declaration from definition. It doesn't need to. Because of its virtual machine, it has other way to achieve dynamic linking. However, if you use all inline methods in CeePlusPlus, you break the only way it has to do this.

Let's take an example. You use some vendor library and it uses the method you propose. All the implementation is with the declarations with no separation. Now, they release a new update that fixes some bugs in a DLL and Library form. How do you take advantage of these updates without recompiling? You purposely recompiled with the previous implementation. What you are missing is that Java doesn't put the definition with the declaration, it puts the declaration with the definition. There is a big difference. You can't do this in CeePlusPlus so you try to approximate it by putting the definition in with the declarations, but by doing this you destroy your ability to dynamically link with new implementations. Java and CeePlusPlus are very different. It is just because of their syntax that people naively think they are so similar. -- RobertDiFalco

I have the feeling that my point about virtual base classes has been completely ignored. Java has a way to separate implementation from declaration which does not depend on the virtual machine. You can use the same thing in C++. It gives cleaner separation than the usual way. It does not correspond to your vendor DLL situation - that is a StrawMan.

Your cry, "why not just use Java or Python", seems to me to be a very general argument. I would prefer specific objections against this particular suggestion. C++ is a multiparadigm language which we, as a community, are still learning. What is traditional today may not be so tomorrow. Perhaps we can take what we have learned from using Java or Python and apply it within C++, to reveal unsuspected new strengths and paradigms. Your BlocksInJava is another attempt at cross-fertilization. Why is that good and this bad? -- DaveHarris

Dave, I didn't see any point about virtual base classes here - I may have missed it. But how can you call shared libraries a StrawMan? In many deployments you are required to use shared libraries - take RedHat and glib for example. I designed BlocksInJava in a way that, while bringing something new, would not fundamentally violate good Java programming. If I did violate a good general idiom, I would want to know so I could attempt to avoid it. I don't revel in deviations but work hard to avoid them - each deviation complicates usage. I tried to find a way that would be most natural for Java - that would exploit its strengths. When I use CeePlusPlus, I try to implement designs in ways that are natural for CeePlusPlus. I don't try to fight its nature - at least I don't think I do. So, to me, the separation of declaration from definition (regardless of whether or not that definition is declared inline) is a very strong and important idiom in CeePlusPlus. Specific solutions may demand a different approach, but in general, this is a powerful concept in C and C++. As for CeePlusPlus being a multiparadigmatic language, we absolutely agree on that. However, these different paradigms should be driven by a problem/solution pair. There should be QualityAttributes that require a generalized idiom to be broken. To simply say that all methods should be inline with their declarations regardless of the solution or problem domains doesn't seem like a good idea to me. But, as always, I could be totally off base and end up changing my opinion tomorrow! -- RobertDiFalco


Inlining code causes code bloat. Not using inline functions causes code bloat. Use inline when it is appropriate and don't use it when it's not. In general, I think using more inline code helps more than hinders by reducing the number of CPU level function calls and stack pushes and pops. I think the advise should be to inline methods whenever appropriate not wherever possible. -- WayneMack

P.S. Aren't C++ templates purely inlined code? It appears that inlining all functions may be explicitly condoned by the language.

Templates are compiler-supported CopyAndPasteProgramming (without the source code bloating). Best of luck if you ever need to debug templatized code, because some debuggers get so confused as to prevent source-level debugging.

I think what is being talked about here is not just the inline keyword but putting your class definitions in line with your class declaration. So technically speaking, even inlined methods don't have to be defined in-line with their declarations. I don't support trying to make CeePlusPlus more Java-like by defing a class in its declaration. This is a deployment nightmare. As for templates, no, absolutely not - they are not purely inlined code. The standard is pretty clear about this. Further more, template classes do not have to be defined in line with their class declarations nor even in the same file. In fact, when they are, it is very difficult to create redistributable libraries. Even when you don't define in-line, it is a real drag to deploy libraries with templates. My practice is to always separate definition from declaration regardless of whether the definition is marked inline or a template class. A simple way to do this is as follows:

Declaration

 #ifndef _PTR_H
 #define _PTR_H

template< class RepT > class Ptr_ { public: typedef RepT rep_type; typedef RepT* rep_pointer; ...

explicit Ptr_( RepT* rep );

rep_pointer get() const; ...

private:

RepT* m_rep; };

#endif/*_PTR_H*/

Definition

 // file: ptr.cxx

#include "ptr.h"

template< class RepT > Ptr_<RepT>::rep_pointer Ptr_<RepT>::get() const { return m_rep; }

[...]

// eof: ptr.cxx

This isn't a great example, but you get the idea. Then, when creating archives, DLL's, or shared libraries you can create explicit instantiations for some of the most basic template argument types:

'Definition Instantiation

 // file: ptr.cpp

#include "ptr.cxx"

template class Ptr_<int>; template class Ptr_<char>; template class Ptr_<void>; ...

// eof: ptr.cpp

This is also a good technique for debugging, i.e. making sure all your templates compile for all the desired types. It's not a great solution, but it works. If CeePlusPlus compilers every implement the template repository talked about by Stroustrup, updating encapsulated template implementation without impact or requiring a recompile in client code will become much easier. Right now it is a nirvana - you can only get so much of a template implementation into an archive or shared library. There will always be some customize instantiation that cannot be explicitly instantiated by the template provider. However, most compilers map variations on void* in order to reduce code-bloat, so you can catch a lot just by explicitly instantiating this one. -- RobertDiFalco


Not that I'm a compile time optimizer, but there are limits to my patience. How do you keep the compile times down to reasonable levels if the implementation is in the header file? Every time you touch the code all the dependents will have to recompile. Separating the implementation from the interface only forces large recompiles when the interface changes. -- SunirShah

I'm fairly certain that PM and co. were saying that you can dodge the compile time hit by using a combination of inheritance and some feature of the Python-C++ bridge that reduced depended-upon headers. As PeterMerel said, the heresy stuff isn't so meaningful without the nifty bridge business. That's just my interpretation, I'm working more-or-less fact-free here, not having used the technology.--JoeOsborn

That's interesting, though, because I am, for an experiment, working entirely with header files, and finding that the (admittedly tiny) system that I'm working on is quite readable, especially coming at this immediately from deep immersion in IoLanguage. My build times are low because of the size of my project, but when I bind my C++ project closer to IoLanguage I expect to be doing less and less in C++ and more and more in Io. And hence will maintain low build times. But for the moment, unbound, my C++ project is quite readable and easily manageable with a toolset that could be as simple as a braindead command-line text editor (I'm thinking pico, really) and a compiler. It's made a big difference to my ability to conceptualize more of the system in one go-- your mileage may vary. -- RobRix

Yes, reduced compile time is another benefit when you AlternateHardAndSoftLayers.


CategoryCpp


EditText of this page (last edited June 10, 2005) or FindPage with title or text search