Where i can learn C++ Processor magics

Let considered this URL
https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc

here are the code that i grab from their

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifdef __cplusplus // Default
    #define INITIALIZER(f) \
        static void f(void); \
        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
        static void f(void)
#elif defined(_MSC_VER)  // MSVS
    #pragma section(".CRT$XCU",read)
    #define INITIALIZER2_(f,p) \
        static void f(void); \
        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void f(void)
    #ifdef _WIN64 // MinGW
        #define INITIALIZER(f) INITIALIZER2_(f,"")
    #else
        #define INITIALIZER(f) INITIALIZER2_(f,"_")
    #endif
#elif  // GCC, Clang
    #define INITIALIZER(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif 


Where i can learn these crazy things?

Thanks
Last edited on
Are you talking about the preprocessor? The snippet in question doesn't do anything special with it. The "magic" comes from understanding the runtime environment for the particular platform.
@helios look here its doing something https://github.com/cs50/libcs50/blob/develop/src/cs50.c#L455

Are you talking about the preprocessor? YES


"The "magic" comes from understanding the runtime environment for the particular platform."

So what you tell me where to learn these kind of things should i have to study operating system programming?
Let's walk through the code. This is just preprocessing done to prepare the file for different compilers. What it is achieving to do is to emulate the __attribute__((constructor)) attribute from gcc on multiple different compilers, and in standard C++ as well. The first part of the code:

1
2
3
4
5
#ifdef __cplusplus // Default
    #define INITIALIZER(f) \
        static void f(void); \
        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
        static void f(void) 


__cplusplus will be defined if the translation unit is being compiled by a C++ compiler (as opposed to a C compiler). This makes sure of this before the file is actually compiled

Next, a macro INITIALIZER() is defined. Its single parameter, f, is used to declare a structure and it is concatenated into the name of that structure using the ## preprocessing operator. For example, if it is called like this:

INITILIZER(hello) { /*...*/ }

The macro expands to this:

1
2
3
        static void hello(void); 
        struct hello_t_ { hello_t_(void) { hello(); } }; static hello_t_ hello_; 
        static void hello(void) {/*...*/}


So this basically declares a structure called hello_t_ and creates a static object. Since static objects are initialized before main()'s code begins running, it causes a constructor call to hello_t_, which in turn calls the hello() function that you are defining. This makes it so that code can run immediately before the start of main(), like a constructor call.

1
2
3
4
5
6
7
8
9
10
11
#elif defined(_MSC_VER)  // MSVS
    #pragma section(".CRT$XCU",read)
    #define INITIALIZER2_(f,p) \
        static void f(void); \
        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void f(void)
    #ifdef _WIN64 // MinGW
        #define INITIALIZER(f) INITIALIZER2_(f,"")
    #else
        #define INITIALIZER(f) INITIALIZER2_(f,"_") 


These all do the same thing as the first one, but they use compiler specific methods rather than the standard C++ method using compiler specific attributes. The stack overflow link explains the MSVC case:

For MSVC, this places a pointer to the function in the user initializer section (.CRT$XCU), basically the same thing the compiler does for the constructor calls for static C++ objects. For GCC, uses a constructor attribute.


The last one is for GCC and Clang:
1
2
3
4
5
#elif  // GCC, Clang
    #define INITIALIZER(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif 


It uses the __attribute((constructor)) attribute from gcc/clang to mark a function as an initializer function and does the same thing.
Last edited on
@helios look here its doing something https://github.com/cs50/libcs50/blob/develop/src/cs50.c#L455
You're pointing at a comment.

So what you tell me where to learn these kind of things should i have to study operating system programming?
I'm not sure what you mean by "these kinds of things", but the snippet doesn't do anything I'd call "OS programming".

Again, the snippet doesn't do anything special. It looks "crazy" because C macros are inherently difficult to read and because whoever wrote it has clearly spent some time refining it. Anyone who's written enough C has written macros that at first glance look crazy but are actually quite reasonable when you look at them carefully.

I guess what I should have asked first is, why do you want to write crazy-looking code? Any idiot can do that. Skilled coders can write code that does complex things but that is also easy to read and comprehend.
Line 18 of the original post (#elif // GCC, Clang ) bothered me. I would not expect a #elif without a condition to compile. So I went to the link that was provided (https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc). The equivalent line in that code is #else , which is what I suspected.

Additionally, the comments found in line 1, 6, 13 and 18 don't appear in the linked post. They appear to be based on the second link that was provided (https://github.com/cs50/libcs50/blob/develop/src/cs50.c#L455), but do not match completely.

Other than the differences already pointed out, the relevant code in the 3 snippets (original post, link1 and link2) all appear to be the same, but I did not go through them with a fine-toothed comb.

Just be aware that there may be errors in the above code and its comments that were introduced by hand-modifying cut-and-pastes from other sources.

I guess what I should have asked first is, why do you want to write crazy-looking code? Any idiot can do that. Skilled coders can write code that does complex things but that is also easy to read and comprehend.


^^ This.
back on topic, there are plenty of sites that can teach you C macros.
This is one of the places they are commonly used, changing things so the code works on a couple of different operating systems is not too uncommon though this is one of the most complicated versions of that I have seen in a while. A more common example is just
ifdef windows include<windows.h> else include<unistd.h> or something like that. The windows token is defined by the project or makefile and the local compiler will have the correct one defined (edit the makefile or project for the current OS before compile, following the instructions provided to build the code or if your own, you should know what to do here).


just do a google of "C macros example" and you should hit some tutorial sites.
C++, C, and various compilers have a number of existing 'variables' and macros already defined that you may see being used in more complicated examples, like __FILE__ may be the name of the current file supplied by the compiler to allow you to make debugging macros (error in this file name at line this line number type output, which interpreted languages supply when they crash but c++ does not do.

This is not operating system coding. Operating system coding at one level is using something the OS provides, like the unix unitstd header that simple does not work or exist in windows. Or if you meant how to write an operating system, that is a very large topic which may require buying a book or a great deal of study.

Avoid the temptation to use macros for actual code, though. They cause problems because macros are not strongly typed nor visible to the debugger, for two common problems. Nothing like debugger pointing to a statement that does not exist (because macro expansion is hidden) as a problem or having the wrong type of integer causing an overflow because you wanted int64 and the macro gave you int32 silently. There are better ways to inline a function if your goal is speed.
Last edited on
The preprocessor is really from C. C++ has always had a background project of being rid of it, but it's a hack they just can do without.

Where to learn about it? It's everywhere, C being ~50 years old.
https://en.wikibooks.org/wiki/C_Programming/Preprocessor_directives_and_macros
If you want to make some really crazy macro voodoo magic you should start reading here:
https://codecraft.co/2014/11/25/variadic-macros-tricks/

Know that macro magic is not cross compiler, one macro trick can work with gcc or llvm but it will fail in msvc.

There are workarounds ofc. after you read and understand things from above link your assignment is to write macros that do the following:

1. Extracts first n elements from __VA_ARGS__ up to a limit of count of __VA_ARGS__
2. Get nth element from __VA_ARGS__ (optionally up to the end of __VA_ARGS__)

Both of which must be cross compiler compatible.
Last edited on
Thanks you so much all
Topic archived. No new replies allowed.