In the last hour, you saw how tags were used as replacements for dynamic content. A client first requests a server response file (SRF) from IIS. The Web server parses the file until a replacement tag is found. Once the tag has been parsed and is shown to be syntactically correct, IIS then hands control over to a replacement handler. It is then up to the replacement handler to either return an HTTP_CODE or render content to the response stream.
In much the same way ATL Server uses a replacement methodology for tags, the C++ compiler performs a similar function using attributes. An attribute is placed within a source file and is surrounded by bracket symbols ([attribute]). When you invoke the build process, the source file is first parsed, and a table of symbols within that file is created. Once the compiler encounters an attribute, it parses the attribute and makes sure it is syntactically correct. If so, the compiler then hands control over to an attribute provider. The attribute provider is then responsible for replacing the attributes it supports in the file with the code that implements that attribute's functionality. The attribute provider then releases control to the linker, and all the symbols are brought together to create the final component binary file. This process can be seen in Figure 15.1.
One question you may find yourself asking is, what's the difference between attributes and C++ macros? A macro, specified by using the #define keyword, is limited in that the source code that is replaced with the macro is fairly static. In other words, a macro does not allow you to inject large amounts of code based on different conditional parameters. Attributes, on the other hand, are quite flexible and due to being context aware can generate advanced code that macros simply cannot.