Compiler defines and reading the standard library code?

Apr 15, 2020 at 9:49pm
Hi guys,

So a two part question here,

First off I noticed a lot of C++ code and C code for that matter have strange defines like #ifndef, #ifdef, #define WIN32_LEAN_AND_MEAN etc etc, I have read that these are compiler defines, but how do these actually work?

what is the purpose of these defines? and how do these #defines actually change your code and change the way the compiler compiles your code?

secondly as I still consider myself a beginner++, when I read a lot of the standard template libraries code such as std::vector and other containers, they almost look cryptic to me, they contain a lot more #defines and other pre-processor jargon. The code looks nothing like if I was to create my own data structure, and the std:: code looks nothing like any of the code posted on here, is there a reason why the code looks so much different in the standard template library?

hope this wasn't a stupid question, thanks!


Apr 16, 2020 at 1:50am
what is the purpose of these defines? and how do these #defines actually change your code and change the way the compiler compiles your code?
Simply put, as the preprocessor (not the compiler) processes an input file, it keeps symbol table to keep track of the existence and meaning of symbols. For example, the table might record that "_DEBUG" exists but has no value, and it might also record that "M_PI" exists and has the value "3.14159265".
The preprocessor goes through the file (more or less) line-by-line. When it encounters a #define directive, it adds the symbol in question to the table (if the symbol is already present it may warn or error out). When it encounters an #undef directive it removes the symbol from the table (if the symbol doesn't exist in the table it may warn or error out). When it encounters an #if it evaluates the condition using the symbol table and based on the result either includes or omits the contained block.

is there a reason why the code looks so much different in the standard template library?
When you write your code for yourself you're only solving your own problem. You know to fairly well-defined bounds what sort of inputs your program will need to deal with.
When a library developer writes code they're solving a problem for a large number of people. They must assume as little as possible the possible inputs, so necessarily the code becomes more complex. Compiler developers have an unusually complex task, as well, because their code should not break (that is, it should both compile and produce correct behavior) for all standard-compliant code. The standard library will be used by almost everyone who uses the compiler, so there's basically no bigger audience than that.
Last edited on Apr 16, 2020 at 8:15pm
Apr 16, 2020 at 2:09am
WIN32_LEAN_AND_MEAN

A very specific Win32 API define that eliminates a large chunk of the Windows headers that most programs don't need.

https://stackoverflow.com/questions/11040133/what-does-defining-win32-lean-and-mean-exclude-exactly
Apr 16, 2020 at 12:37pm
There's a tutorial on preprocessor directives on this very site, that you'd find helpful:

http://www.cplusplus.com/doc/tutorial/preprocessor/
Apr 16, 2020 at 2:15pm
Speaking of preprocessor directives, it looks like #pragma once is becoming a de facto standard with most compilers.
https://en.wikipedia.org/wiki/Pragma_once#Portability

If I understand what C++20 Modules will do (probably not) when the standard is finally published using header guards will become legacy coding.
Apr 16, 2020 at 6:47pm
adam2016 wrote:
std::vector and other containers, they almost look cryptic to me

It's not that bad, you just have to learn to skip the bits that are irrelevant to your question.
Using your example, let's look at std::vector's and the question we'll ask is "what are the data members?"

There are three C++ standard libraries: GNU libstdc++, LLVM libc++, and Microsoft STL. All three are on github

LLVM: https://github.com/llvm-mirror/libcxx/blob/master/include/vector#L468-L470
1
2
3
template <class _Tp, class _Allocator /* = allocator<_Tp> */>
class _LIBCPP_TEMPLATE_VIS vector
    : private __vector_base<_Tp, _Allocator>
, where __vector_base has the members https://github.com/llvm-mirror/libcxx/blob/master/include/vector#L342-L344
1
2
3
    pointer                                         __begin_;
    pointer                                         __end_;
    __compressed_pair<pointer, allocator_type> __end_cap_;


GNU: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_vector.h#L385
1
2
  template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class vector : protected _Vector_base<_Tp, _Alloc>
, where _Vector_base has a member of type _Vector_impl
_Vector_impl _M_impl;, which is derived from _Vector_impl_data
struct _Vector_impl : public _Tp_alloc_type, public _Vector_impl_data,which has the members
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_vector.h#L89-L92
1
2
3
	pointer _M_start;
	pointer _M_finish;
	pointer _M_end_of_storage;


Microsoft: https://github.com/microsoft/STL/blob/master/stl/inc/vector#L411-413
1
2
3
4
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector { // varying size array of values
/* skip a few lines */
    _Compressed_pair<_Alty, _Scary_val> _Mypair;
, where _Scary_val is a typedef for _Vector_val, which has the familiar members https://github.com/microsoft/STL/blob/master/stl/inc/vector#L395-L397
1
2
3
    pointer _Myfirst; // pointer to beginning of array
    pointer _Mylast; // pointer to current end of sequence
    pointer _Myend; // pointer to end of array 


okay, vector is a bit scary, but look up a simple algorithm like std::fill or std::reverse and it's only a few lines in every library. Personally, I find LLVM libc++ to be the easiest read, since it is the newest and the least portable, they didn't have to complicate the code by too many special cases.
Last edited on Apr 16, 2020 at 6:55pm
Topic archived. No new replies allowed.