• Advertisement
Sign in to follow this  

What c++ does for you, behind the scenes

This topic is 3029 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I was talking to someone in the game industry the other day, and he mentioned that (in his opinion) good c++ programmers think about how much c++ does for you, i.e. how it knows where to find the correct implementation of a virtual function etc. and how to decrease the amount of code that is created by the compiler to handle this. (this is obviously quite important in console programming). I was just wondering if anyone knows of any good links where I could read up on this? Thank you, Adam

Share this post


Link to post
Share on other sites
Advertisement
Unfortunately, your source is either ignorant of certain facts about C++, or glossing over them in speaking to you (or you missed them). I'll give him the benefit of the doubt and go with the latter.

Much of the things he is alluding to, such as the implementation of virtual functions and dynamic dispatch, are not called out by C++. They are, in fact, compiler-specific. Similarly, other things end up being platform specific (that is, dependent on the OS version or chip architecture). For Windows, MSDN contains a lot of this stuff directly, and typically has links to other useful places as well.

The best source for this information is thus the technical documentation for the OS, chip, or compiler in question... again, stressing the fact that this is no longer really having anything to do with C++.

For actual C++-related stuff, you can look for drafts of the standard. Or just buy yourself a copy. There also the C++ FAQ Lite.

You usually don't need to worry about this kind of thing though. It's mostly a curiosity for beginners, and when you need to know about it you will probably have experienced it first-hand. That's how good C++ programmers think about this stuff -- not because good C++ programmers think about it, but because good programmers (who use C++ or perhaps other languages) have usually experienced it.

Certainly, indulge your curiosity about it if you like -- but don't think it's important for you to start caring about it in order to learn C++ effectively.

Share this post


Link to post
Share on other sites
It can also be pretty instructive to look at the generated assembly produced by your compiler. Most compilers have switches to compile to assembly rather than object code. For MSVC it's the /FA family of switches and gcc uses -S.

Share this post


Link to post
Share on other sites
I probably misheard some of what was being said, as it was a loud environment. Thank you for your advice though, I guess I will keep it in mind, but until I am working with one specific compiler/chip/OS there is probably not a lot of point in me knowing much about it, as I probably wouldn't have an effective way of reducing the workload anyway.

SiCrane, I will look into the assembly creation out of curiosity I think!

I appreciate your fast responses.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
You usually don't need to worry about this kind of thing though. It's mostly a curiosity for beginners, and when you need to know about it you will probably have experienced it first-hand.
Ehhhh.

The internal implementation of virtual functions is hands down the most common interview question I've ever gotten, and I've frequently been grilled on all kinds of internal details and standard behavior details. The most extreme version was when I was basically asked to reimplement a bunch of C++ features (virtual calls, inheritance, multiple inheritance, etc) in strict C.

I suppose the practical use of this type of knowledge is debatable, although personally I like having it in my arsenal. But if getting a job is included in "worry about this kind of thing", then it's awfully helpful.

Share this post


Link to post
Share on other sites
While I agree that the knowledge is important, I think it tends to come with experience rather than needing to be sought out, particularly at the early stages of programming.

I have never gone looking for lots of in depth information on how C++ features are implemented under the hood. Yet, through my time reading and learning about C++ and especially listening to some of the smart people like yourself here on gamedev.net I have picked up most of the details.

I think that was what jpetrie was alluding to in his second last paragraph.

A beginner typically has a mental model of a computer that is incorrect enough that delving into this sort of detail too early can do more harm than good. We see this sometimes when beginners over worry about the cost of piddly virtual functions when the frequency of the calls is so low that its not worth worrying, without looking at the "big picture" of optimisation that tends to have a more tangible effect.

Share this post


Link to post
Share on other sites
Quote:
Original post by Promit
The internal implementation of virtual functions is hands down the most common interview question I've ever gotten, and I've frequently been grilled on all kinds of internal details and standard behavior details. The most extreme version was when I was basically asked to reimplement a bunch of C++ features (virtual calls, inheritance, multiple inheritance, etc) in strict C.
Are you at liberty to divulge what sort of position you were interviewing for when these questions tend to arise?

My gut feeling is that you are much more likely to receive this type of question if you are applying for a systems programming position, or a job in embedded development (where the C++ compilers tend to be less capable).

Share this post


Link to post
Share on other sites
It sounds like what this guy was talking about was providing your own copy of the c std libs.

Some people do this to reduce the size of your .exe, although in most cases it really doesn't matter unless you are really really tight for executable size restrictions. For instance, an executable that doesn't do anything commonly weighs in at 40k. It goes beyond virtual functions and into all of the functions implicitly called for you like the registration, initialization, and destruction of the global objects amongst other things. Also providing implementations for whatever c std functions you call explicitly.

Regardless of being near useless from a practical standpoint it is decent knowledge to know about.

You can try googling libctiny for some efforts of others to reduce exe size but in a more generic way. Or try "size reduction executable c++"

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
Are you at liberty to divulge what sort of position you were interviewing for when these questions tend to arise?

My gut feeling is that you are much more likely to receive this type of question if you are applying for a systems programming position, or a job in embedded development (where the C++ compilers tend to be less capable).


I cannot speak for Promit, but I have seen this as a standard question as well for all engineers/programmers at these companies. One thing to realize is that the person asking the questions doesn't always expect you to get all of them right, but rather they want to see how extensive your knowledge goes. For the most part, how virtual functions work is taught in schools, but there are followup questions to see exactly how much you know about the inner workings.

Now, I am sure for some positions they do expect you to get the question correct.

Share this post


Link to post
Share on other sites
Quote:
Original post by dclyde
One thing to realize is that the person asking the questions doesn't always expect you to get all of them right, but rather they want to see how extensive your knowledge goes.


Yeah, I think this was the situation in this case.

Share this post


Link to post
Share on other sites
The "stuff behind your back" goes way deeper than puny vtable.

Consider:
a = b;
What does this code do?

In C, a will have the value of b.

In C++:
- assignment operator is invoked
- copy constructor is called to make a temporary
- 3 internal pointers are assigned to std::auto_ptr
- 3 new calls are made
-- each new call is routed via custom overloaded new
-- which in turn calls malloc
-- which updates allocation table
--- which strcpy()s __FILE__ and __LINE__
- std::swap is called between local variables and temporary
- old state is delete[]d
- auto pointers are reset
- this is returned

And no, this is not far fetched, it is basically the default implementation of a default assignment operator of a class containing 3 exception-safe members, say, strings. For example:
struct Foo {
std::string x;
std::string y;
std::string z;
};


And that is trivial case - double the complexity of the above could apply to every deep copy made.

This is primary reason why system programmers shun C++. Too much magic. And it really depends on perspective - this magic might be far from a good thing.

Share this post


Link to post
Share on other sites
Quote:
Original post by dclyde
Quote:
Original post by swiftcoder
Are you at liberty to divulge what sort of position you were interviewing for when these questions tend to arise?

My gut feeling is that you are much more likely to receive this type of question if you are applying for a systems programming position, or a job in embedded development (where the C++ compilers tend to be less capable).


I cannot speak for Promit, but I have seen this as a standard question as well for all engineers/programmers at these companies. One thing to realize is that the person asking the questions doesn't always expect you to get all of them right, but rather they want to see how extensive your knowledge goes. For the most part, how virtual functions work is taught in schools, but there are followup questions to see exactly how much you know about the inner workings.

Now, I am sure for some positions they do expect you to get the question correct.
Twice (maybe thrice) for normal game programmer positions, once for my spot on the NVIDIA driver team. The internal workings of virtual memory itself are a popular question as well.

I like these questions because on a systems level, I have a very extensive background, especially for my age and experience level. And that extends to the kernel, the .NET Framework, even the drivers and to a limited extent the hardware. Any time I get asked a systems question, I pretty much just grin and hit the throttle, cause at that point the interview is mine [grin]

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
In C++:
- assignment operator is invoked
- copy constructor is called to make a temporary
- 3 internal pointers are assigned to std::auto_ptr
- 3 new calls are made
-- each new call is routed via custom overloaded new
-- which in turn calls malloc
-- which updates allocation table
--- which strcpy()s __FILE__ and __LINE__
- std::swap is called between local variables and temporary
- old state is delete[]d
- auto pointers are reset
- this is returned


Out of curiosity, isnt that what the new 0x standard fixes using rvalue and lvalue references? At least thats what I got from what I read. I'm nowhere near an expert though so dont quote me =p

Share this post


Link to post
Share on other sites
"Are you at liberty to divulge what sort of position you were interviewing for when these questions tend to arise?"

Oh god, I get asked these sorts of questions all the time -- and I'm usually interviewing for team lead/architect type roles these days.

Job interviews are generally really poor -- there's a lot of questions doing the rounds that frankly people can learn out of books. "What's a virtual function?", "What's the difference between a struct and a class?". Stuff like that. That's not even daily bread for developers. That stuff should be like air. You need it, but when was the last time you actually looked at it?

There are actually really few jobs where you need to do things like implement virtual dispatching in C or swap a string in place. I've done everything from video bioses to content delivery networks passing through City financials and logistics companies on the way and I've never actually needed either of them.

I've often said that I could get my sister past most software interviews with a couple of hours training on the rote answers to those questions -- whereas there's no way I could pass for a auditing accountant of her level without actually have done all the exams.

"What's the difference between a pointer and a reference" shows up a lot as an interview question as well. And it's amazing how many people think that a reference can't be null because that's what some of the textbooks say[1].

And don't get me started on the "How do you get four programmers who walk at different speeds across a bridge in the dark with only one torch" type crap...

I particularly like the way the people that do that say that they do it because Microsoft does it[2] rather than for any actual explainable reason -- that's not an interview technique, that's a cargo cult.

And then there's the people who just show code snippets and want to know why it won't compile or won't work -- usually based on exotic edge cases of C++. I went to one a few months back that contained TWO HOURS of that stuff with some autistic guy doing the interview. Brr. I went home, had a bath, washed my hair and STILL I could feel nerdiness clinging to me. My take on that crap is to steer well away from the edges of C++. We're in the business of writing software, not finding new and exciting compiler errors.


This is one of my issues with the software industry. It's like you're hiring a chief of surgery at a hospital, and the interview consists entirely of asking them if they can identify different types of scalpel, not whether they're any good at saving patients. It shouldn't really be a surprise that half the industries projects die on the table, although we can all name the kind of scalpel we used to open with.


[1] You can create a null pointer of the right type and * it into a reference of that type. Works just fine. You can even call methods through it and they'll work as long as they're not virtual. It will then crash on the first member access. "this" will, of course, be null. References are really just syntactically sugared pointers.

[2] But they don't give their developers individual offices to work in peace. Like Microsoft does...

Share this post


Link to post
Share on other sites
Did you know that in x86 machine code, the 1011 opcode represents a "move" operation? Are you going to remember all x86 and x64 opcodes, just in case you need to read some machine code with the naked eye?

What you should strive for is not detailed knowledge of every single things that happens when you program. This might make you an useful, over-specialized expert on a small problem domain (make no mistake, there's no way to be an over-specialized expert on a large problem domain) but will harm you in the long term, as the marginal value of more specialization decreases sharply, and leaves you with less knowledge of other technologies (or real-life issues) than other people.

Programming knowledge is often structured as a framework for knowing what things tend to work and what things don't (not necessarily how they work) and ways to find missing information when you actually need it. Being able to read and understand a third-party API or file format in an hour or two is far more valuable (as far as being a versatile programmer goes) than knowing the bloody details of C++ compilers.

Plus, as others have already said, don't bother with this right now: even if you learn the high-level concepts of C++, you are bound to stumble upon low-level details sooner or later.

Quote:
Original post by Katie
"What's the difference between a pointer and a reference" shows up a lot as an interview question as well. And it's amazing how many people think that a reference can't be null because that's what some of the textbooks say[1].
In a well-behaved C++ program, references can't be null. Prefixing every single statement in a book about C++ with «in a well-behaved C++ program, » would be silly, so the reader is expected to keep that in mind while reading.

In fact, if you make a blanket statement about C++ without specifying otherwise, people will assume that you are discussing well-behaved programs. Saying "references can be null" is just going to lead to misunderstandings and pointless arguments, even though it's technically true that in misbehaving C++ programs you can manage to create a reference r such that &r is a null pointer.

Share this post


Link to post
Share on other sites
First to make it work, then to make it work faster.
Intel optimization tricks...
http://www.agner.org
I like to code in C++ then find the slow spots and
replace them with some hand rolled assembly code.
A good assembler can beat the best compiler every time.
Now that we have SIMD technology it gets a bit trickier.

Share this post


Link to post
Share on other sites
Quote:

Quote:

Original post by Antheus
In C++:
- assignment operator is invoked
- copy constructor is called to make a temporary
- 3 internal pointers are assigned to std::auto_ptr
- 3 new calls are made
-- each new call is routed via custom overloaded new
-- which in turn calls malloc
-- which updates allocation table
--- which strcpy()s __FILE__ and __LINE__
- std::swap is called between local variables and temporary
- old state is delete[]d
- auto pointers are reset
- this is returned



Out of curiosity, isnt that what the new 0x standard fixes using rvalue and lvalue references? At least thats what I got from what I read. I'm nowhere near an expert though so dont quote me =p


Sort of, but this concerns temporaries. If you need two deep copies of something, you still need to copy it.

I don't think it is necessarily that bad for C++. If you really need failure-safe deep copies in C, it's not going to be a = b, so it is rather a comparison of apples and oranges.

Share this post


Link to post
Share on other sites
Learning a bit of Assembly will give you some insight into how a compiler works. :) I'm currently taking a college class on Assembly at my university where we've been instructed to "translate" (compile) high-level language code (C++/Java/etc) into Assembly. It's a very eye-opening experience.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
The "stuff behind your back" goes way deeper than puny vtable.

Consider:
a = b;
What does this code do?

In C, a will have the value of b.

In C++: ...


The net result of which is, a will have the value of b, in what is really the simplest safe way.

There's nothing about the implementation of std::string that requires a std::auto_ptr, BTW, although it is "managed" memory in a sense. There's also nothing in the spec that I know of that would mandate a re-allocation every time - if the string being copied over was longer, there's no reason the old storage couldn't be reused. You're also forgetting about short-string optimizations. And that __FILE__ and __LINE__ stuff is only going to happen in debug mode.

Quote:
it is basically the default implementation of a default assignment operator of a class containing 3 exception-safe members, say, strings.


Which you can't even create in C.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
In fact, if you make a blanket statement about C++ without specifying otherwise, people will assume that you are discussing well-behaved programs. Saying "references can be null" is just going to lead to misunderstandings and pointless arguments, even though it's technically true that in misbehaving C++ programs you can manage to create a reference r such that &r is a null pointer.


Some code to do just that:

#include <cstdio>

int main (int argc, char *argv[])
{
int *x = NULL;
int& y = *x;
printf("%d", x);
printf("%d", y);
return 0;
}





GCC/MinGW compiles that fine. When run, however, the program outputs "0" and then crashes.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman

There's nothing about the implementation of std::string that requires a std::auto_ptr, BTW, although it is "managed" memory in a sense. There's also nothing in the spec that I know of that would mandate a re-allocation every time - if the string being copied over was longer, there's no reason the old storage couldn't be reused. You're also forgetting about short-string optimizations. And that __FILE__ and __LINE__ stuff is only going to happen in debug mode.


I gave string only as an example.

In C, b is assigned to a. No allocations will ever happen, no magic, no nothing. Either literal value or pointer will be assigned. A memcpy might be involved.

And while it's true that in some cases, some compiler, some STL implementation might under certain circumstances choose to perform certain optimizations, avoid some allocations, might avoid making copies - this is exactly what "behind the scenes" means.

There are obviously optimizations that can happen, but if I give you a line of code, can you say what it does?
Foo bar() {
...
return x;
};
What does this do in C, and what all can happen in C++? How much memory will be allocated, how long will it take to run, how much code will be generated? What about C# or Java?

There are many acronymed optimizations that might apply to this case, but they are again left at specific compiler's discretion. In this respect, C++ is highly non-deterministic by design.

Quote:
Which you can't even create in C.

typedef struct FOO {
char * a;
char * b;
char * c;
} foo;

foo * make_safe_deep_copy(foo* x);
It can be done quite trivially, but the code in the end will be quite clear, and very explicit about what happens.

And I'm not trying to advocate C as superior, just pointing out why C still tends to be more popular in some domains.

Share this post


Link to post
Share on other sites
Actually that signature would work for implementing a copy constructor. I suppose assignment with deep copy might look something like this:


typedef struct FOO {
char * a;
char * b;
char * c;
} foo;

int safe_deep_assign(foo* x, const foo* y)
{
char *a = 0, *b = 0, *c = 0;
a = malloc(strlen(y->a) + 1);
b = malloc(strlen(y->b) + 1);
c = malloc(strlen(y->c) + 1);
if (a && b && c) {
strcpy(a, y->a);
strcpy(b, y->b);
strcpy(c, y->c);
free(x->a);
free(x->b);
free(x->c);
x->a = a;
x->b = b;
x->c = c;
return 1;
}
else {
free(a);
free(b);
free(c);
return 0;
}
}



There's also quite a lot going on under the hood, and it gets more complicated than that if you want to reuse the char buffer if the target's string is long enough. (BTW, what do you mean by exception-safe default assignment - do you mean exception-safe as in not leaking or exception-safe as in leaving target unchanged in case of failure, as the default assignment operator should not be concerned with the latter?)

In this particular case you seem to be talking more against operator overloading, assignment has nothing more going on under the hood for primitive types than it has in C.

Perhaps one place where significantly more might be going on is the C++ coders custom to store things by value in containers. Overhead of copying under the hood is more or less avoidable with the use of pointers, but you have a choice of trading some performance for a higher and simpler abstraction.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement