Productivity in programming of C++ programmers

S

Siemel Naran

lilburne said:
Siemel Naran wrote:

One of my pet hates.

I make all developers remove inline functions from their code unless
they can demonstrate a measurable speed up. Even then they don't get to
stuff them in the header.

Even if it just returns a member variable by value?
 
P

Phlip

lilburne said:
Siemel Naran wrote:
One of my pet hates.

I make all developers remove inline functions from their code unless
they can demonstrate a measurable speed up. Even then they don't get to
stuff them in the header.

C++ has an annoying tradeoff between cognitive and physical efficiency.

If you inline all methods inside classes unless profiling reveals the need
to outline them, your code is more cognitively efficient.

If you put all classes in .cpp files without a reason to publish them, then
your header files don't get obese and cause cascading recompiles at change
time.

The rules that all classes go in header files deteriorates compiler
performance. Then rules to address the symptoms, such as putting all
implementations into .cpp files, deteriorates programmer performance

(You are lilburne, not James Kanze, so I ain't gonna do what you say! ;-)
 
L

lilburne

Siemel said:
Even if it just returns a member variable by value?

Yes! It keeps the rule simple

"Don't make functions inline unless you can show
a measurable speed up by doing so"

start adding more caveats and they'll think that maybe you
aren't serious.

Now you don't want to stick your inline definitions within
the class itself like

class A{
int m_i;
public:
A(int i) : m_i(i) {};
};

because you'll never be able to debug when A::A(int i) gets
called. So you'll write something like

class A{
int m_i;
public:
A(int i);
};

#include A.i

and within the A.i file you write

#ifndef OUTLINE
inline A::A(int i) : m_i(i) {}
#endif

but then you need a third file to create outline versions of
the file so that you can debug the damn things:


#ifdef DEBUG
#define inline
#undef OUTLINE

#include A.i

#endif

all of which is a burden on the build system and best
avoided, unless there is a performance advantage.
 
L

lilburne

Phlip said:
If you inline all methods inside classes unless profiling reveals the need
to outline them, your code is more cognitively efficient.


Nah! It just clutters up the thing making it impossible to
read. I want to see API not implementation.

If you put all classes in .cpp files without a reason to publish them, then
your header files don't get obese and cause cascading recompiles at change
time.

Don't you just hate it when that happens, and 20% of the
time the dependency is spurious anyway.
 
S

Siemel Naran

lilburne said:
Siemel Naran wrote:

Yes! It keeps the rule simple

"Don't make functions inline unless you can show
a measurable speed up by doing so"

start adding more caveats and they'll think that maybe you
aren't serious.

Now you don't want to stick your inline definitions within
the class itself like

class A{
int m_i;
public:
A(int i) : m_i(i) {};
};

because you'll never be able to debug when A::A(int i) gets
called. So you'll write something like

class A{
int m_i;
public:
A(int i);
};

#include A.i

and within the A.i file you write

#ifndef OUTLINE
inline A::A(int i) : m_i(i) {}
#endif

All my compilers let you debug inline functions (Borland 6, MSVC 7, MSVC 6,
gdb g++).
 
S

Siemel Naran

lilburne said:
Siemel Naran wrote:

Hmmm! Can you set a breakpoint or just step in?

Both, at least on Borland 6! I just tried to set a breakpoint. I put
breakpoints in non-template functions. My file structure is: source code in
src\libname\file.cpp, and the headers are include\libname\various.hpp. Put
a breakpoint in any of the various files in the include directory, and the
debugger will hit the breakpoint. Thinking about it, I think that in debug
build the compiler makes all functions non-inline, so of course you should
be able to step through it. Presumably if you debug the release .exe or
..lib or .dll, stepping through individual assembly instructions, you won't
hit the breakpoint because if the code is really inline it won't be in the
..exe. Anyway, I don't have the bandwidth to test this.

Nevertheless, the rule about making all functions non-inline unless
profiling says to do so is a good one. I break the rule so that I don't
have to write lots of little short get/set functions in the cpp file. It's
a timesaver, and worth it. Though by the premise of the original post of
this thread it means I'm un unproductive programmer because I have fewer
lines of code :). However, if I have a file that has just one get/set
function, while the rest are long functions defined in the cpp file, then
I'll make the one get/set function pair (two functions), non-inline and
defined in the cpp file, just for consistency with the other functions.
 
L

lilburne

Siemel said:
Both, at least on Borland 6! I just tried to set a breakpoint. I put
breakpoints in non-template functions. My file structure is: source code in
src\libname\file.cpp, and the headers are include\libname\various.hpp. Put
a breakpoint in any of the various files in the include directory, and the
debugger will hit the breakpoint. Thinking about it, I think that in debug
build the compiler makes all functions non-inline, so of course you should
be able to step through it. Presumably if you debug the release .exe or
.lib or .dll, stepping through individual assembly instructions, you won't
hit the breakpoint because if the code is really inline it won't be in the
.exe. Anyway, I don't have the bandwidth to test this.

Probably some weirdness in the linker too to make it work
across libraries.
Nevertheless, the rule about making all functions non-inline unless
profiling says to do so is a good one. I break the rule so that I don't
have to write lots of little short get/set functions in the cpp file. It's
a timesaver, and worth it. Though by the premise of the original post of
this thread it means I'm un unproductive programmer because I have fewer
lines of code :). However, if I have a file that has just one get/set
function, while the rest are long functions defined in the cpp file, then
I'll make the one get/set function pair (two functions), non-inline and
defined in the cpp file, just for consistency with the other functions.

Consistency is a good thing. Personally I prefer not to have
implementation details in the headers just the API. I can
just about tolerate member variables, but to each their own.
I prefer to look in just one place (the source file) for
implementation rather than first looking in the source and
then having to go to the header. Anyone equating LOC and
programmer productivity is talking out of their arse.
 
S

Siemel Naran

Consistency is a good thing. Personally I prefer not to have
implementation details in the headers just the API. I can
just about tolerate member variables, but to each their own.
I prefer to look in just one place (the source file) for
implementation rather than first looking in the source and
then having to go to the header. Anyone equating LOC and
programmer productivity is talking out of their arse.

You can have member variables in the cpp file. Look up the pointer to
implementation concept, where in the header you declare for example a nested
struct Imp, give your class a pointer or smart pointer to this Imp, and
freely define it in the cpp file. You insuluate derived classes from length
recompiles, plus you can easily add reference counting to your class now.

What is LOC?
 
S

Siemel Naran

lilburne said:
Siemel Naran wrote:

Probably some weirdness in the linker too to make it work
across libraries.

I think the reason is that I compiled my library parse.lib in debug mode
into a debug lib file. Then I compiled my example.exe, and linked against
the debug parse.lib file, and I was therefore able to put breakpoints in the
inline function. Had I linked against the release parse.lib file, that
would probably not have been the case.

In the past, I found that creating a release parse.lib file, then compiling
example.exe in debug mode and linking against the release parse.lib file
caused my program to mysteriously crash at runtime. The reason I think is
that the class headers of the parse library define STL member variables,
like std::string, std::deque, etc. The parse.lib compiled in release mode
links against the release STL files, performs various compiler
optimizations, etc. My example.cpp includes the same headers, but compiles
in debug mode, so it does not do various optimizations, assumes that the
parse.lib file will link against the debug STL files, etc. So if both
parse.lib and example.exe are compiled in release mode, or both in debug
mode, then it works fine.

When I get more time, I'm going to use the pointer to implementation idea,
and define a nested struct Imp, and define it with all the STL member
variables in the cpp file. This might allow me to compile and run a debug
example.exe linked against a debug parse.lib.
 
L

lilburne

Siemel said:
I think the reason is that I compiled my library parse.lib in debug mode
into a debug lib file. Then I compiled my example.exe, and linked against
the debug parse.lib file, and I was therefore able to put breakpoints in the
inline function. Had I linked against the release parse.lib file, that
would probably not have been the case.

I'd sort of assumed that. What I was thinking about was just
how the debugger/linker resolves the situation where you
have inline functions defined in libA which are used by both
libB and libC. Perhaps it ignores multiple definitions.
In the past, I found that creating a release parse.lib file, then compiling
example.exe in debug mode and linking against the release parse.lib file
caused my program to mysteriously crash at runtime.

I recall a number of years ago that mixing and matching
release/debug in VC++ didn't work too well (and we don't use
the STL). A colleague killed the idea of mixing modes all
together by making our class sizes differ between
release/debug anyway.
 
L

lilburne

Siemel said:
You can have member variables in the cpp file. Look up the pointer to
implementation concept, where in the header you declare for example a nested
struct Imp, give your class a pointer or smart pointer to this Imp, and
freely define it in the cpp file. You insuluate derived classes from length
recompiles, plus you can easily add reference counting to your class now.

There is something to be said for the Chesire Cat trick as
it stops inlining altogether. On the downside it can make
whitebox testing harder. Besides the lengthy recompiles
usually come about through changes to low level classes
which in my experience don't tend to change their
implementation much anyway.
What is LOC?

Lines Of Code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top