Abusing the Preprocessor

I came across a git repo in which a person did something interesting with some preprocessor defines. Rather than using it to define constants, they used it to actually create their own interface to C/C++. They did it more as a joke since their repo is actually a bit offensively named, but it was interesting. https://github.com/klange/assholedoth

I've decided to make my own version in which I make some shorthand instructions. (not for code that I intend to share with others)

file: "shorthnd.h"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// no #includes necessary, preprocessor doesn't care if 
// functions/structs/types are defined beforehand...
// nor afterwords if you don't use the shorthand in the code.

#define pu public:
#define pr  private:

// I know it's goofy, but I don't want to overwrite int main() if used
#define mane int main(int argc, char ** argv)

// Getting tired of typing std:: ?
#define str std::string
#define vec std::vector
#define o std::cout <<   

...etc

So now I can call a vector of strings without "using namespace std" like this;
vec <str> names;

I'm not going to defend this code, I'm just trying to play a bit and it was fun.

I'm curious how you might abuse the preprocessor? Have you seen some interesting tricks?
Last edited on
For the most creative abuses, probably the best place to look is entries to IOCCC.

One time in a project I needed a little bit of reflection. I had a bunch of functions that I needed to expose to the outside and each had a related class that dealt with it. Basically I was going to need to do something with all the functions in a bunch of places, and always in the same order (or in an irrelevant order).
So I wrote a file that looked like this:
1
2
3
4
MACRO_F0(SomeFunctionName)
MACRO_FN(ADifferentFunctionName)
MACRO_FN(ADifferentFunctionNameEx)
//etc. 
The point of MACRO_F0 and MACRO_FN was that sometimes I needed to do something slightly different only for the first function.
Then to use the file I did something like this, for example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define MACRO_F0(name) \
    class name##_handler : public BaseHandler{ \
    public: \
        void call() override{ \
            name(); \
        } \
    };
#define MACRO_FN(name) MACRO_F0(name)
#include "the_file.h"
#undef MACRO_F0
#undef MACRO_FN

//...

#define MACRO_F0(name) some_map[#name] = new name##_handler;
#define MACRO_FN(name) MACRO_F0(name)
#include "the_file.h"
#undef MACRO_F0
#undef MACRO_FN 
Vesa Karvonen, one of the authors of Boost's Preprocessor library, wrote this:
https://github.com/rofl0r/order-pp
I found it a while ago and decided it contained enough magick to last quite a while.

You can find plenty of tricks in there, or in Boost.PP.

There's this nice little pattern - for example:
1
2
3
4
5
6
7
8
9
10
11
#define ENUM_XYZ(f) f(x) f(y) f(z)
// later
#define f(a) \
  class foo_##a : foo_base { /* etc */ };
ENUM_XYZ(f)
#undef f
// again:
#define f(a) \
  std::unique_ptr<foo_base> get_##a() { return std::make_unique<foo_##a>(); }
ENUM_XYZ(f)
#undef f 


I wouldn't consider that an abuse, but YMMV.
Last edited on
For macro abuse in production code used pretty much everywhere, look no further than openssl.

include/openssl/pkcs12.h
1
2
3
4
5
6
7
DECLARE_ASN1_FUNCTIONS(PKCS12)
DECLARE_ASN1_FUNCTIONS(PKCS12_MAC_DATA)
DECLARE_ASN1_FUNCTIONS(PKCS12_SAFEBAG)
DECLARE_ASN1_FUNCTIONS(PKCS12_BAGS)

DECLARE_ASN1_ITEM(PKCS12_SAFEBAGS)
DECLARE_ASN1_ITEM(PKCS12_AUTHSAFES)


include/openssl/asn1.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# define DECLARE_ASN1_FUNCTIONS(type) DECLARE_ASN1_FUNCTIONS_name(type, type)

# define DECLARE_ASN1_FUNCTIONS_name(type, name) \
        DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
        DECLARE_ASN1_ENCODE_FUNCTIONS(type, name, name)

# define DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \
        type *name##_new(void); \
        void name##_free(type *a);

# define DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name) \
        type *d2i_##name(type **a, const unsigned char **in, long len); \
        int i2d_##name(type *a, unsigned char **out); \
        DECLARE_ASN1_ITEM(itname)

....
repeated at nausea.

The upshot is, the openssl api functions cannot be found by searching the code, as the names are made up from macros. Most of the calls are arbitrary and there are few decent examples of how to use the api, coupled with breaking changes in almost all releases, makes openssl a true wonder of the computing world.
Last edited on
I think libuv also uses macros to hide the private data members from people reading the headers (and probably also from intellisense and similar tools).
Topic archived. No new replies allowed.