Search code examples
c++inlineconstexpr

Replacing define statement with actual code


I know that #define is not really good to use, I am not sure if that's a duplicate but I couldn't find what's the best way is to do this:

I have a program that uses a definition like:

#define True GetObject(true)

I need to replace the define statement with actual code

but I can't think of a way to make it so the following code:

int main() {

  auto c = True;

  return 0;
}

turns into this at compile time:

int main() {

  auto c = GetObject(true);

  return 0;
}

Summary: I want an exact replacement of "define" as code, I found something like an inline function could help, but is there a way to make an inline variable?

I tried the following but ended up with an error

inline Object True = GetObject(true);

NOTE: I can't make Object/GetObject a constexpr class

NOTE 2: I'd like to avoid turning True to True() if that's possible

This is basically a question for educational purposes but I would like to use it in a small library I am writing, If you could tell me what would be the best way to do this I'd be really happy

Thanks!!!

EDIT 1

As the first above is not quite clear, I'd like True to call GetObject(true) function every time

The returned value is going to be the same but the function call is necessary

EDIT 2

I didn't think it is necessary to explain this but, the library I am creating is a simple layer (that's not really important for this),

The macro name True is completely random, it could be named something completely different (I am just using it for testing)

The macro is used to create a Class that I need to use a lot in my code and I also need it to create a new instance of the class (not just a copy)

I also need to update the class a lot so to add more constants in the constructor I would need to have some simple way to do, I don't think it would be good to go in each of my 10 headers/sources and replace every instance

with the values that represent 'True' and other states.

the part about removing () is because I don't think it's convenient to see a lot of parenthesis in something that looks like a variable (or maybe some kind of compile-time constant?)


Solution

  • The following is probably similar in principle to your code. It is solved with #define and sets a serialno to each Object and prints something out to the console:

    #define True GetObject(true)
    #define False GetObject(false)
    
    #include <iostream>
    using namespace std;
    
    class Object {
    public:
        Object(bool b, int serial) : _b(b), _serialno(serial) {};
    
        bool _b;
        int _serialno;
    };
    
    Object GetObject(bool b) {
        static int curserial = 0;
    
        Object result(b, ++curserial);
    
        cout << "Created Object with serialno " << result._serialno << " and value " << boolalpha << result._b << endl;
        return result;
    }
    
    int main() {
        auto c = True;
        auto d = True;
        auto e = False;
    
        return 0;
    }
    

    which generates the output

    Created Object with serialno 1 and value true
    Created Object with serialno 2 and value true
    Created Object with serialno 3 and value false
    

    Now we change it to the same result without '#define':

    #include <iostream>
    using namespace std;
    
    class Object;
    void GotObject(Object& o);
    
    class Object {
    public:
        Object(bool b) : _b(b), _serialno(++curserial) {};
        Object(const Object& other) : _b(other._b), _serialno(++curserial) {
            GotObject(*this);
        };
    
        bool _b;
        int _serialno;
    
        inline static int curserial = 0;
    };
    
    void GotObject(Object& o) {
        cout << "Created Object with serialno " << o._serialno << " and value " << boolalpha << o._b << endl;
    }
    
    inline Object True(true);
    inline Object False(false);
    
    int main() {
        auto c = True;
        auto d = True;
        auto e = False;
    
        return 0;
    }
    

    Output:

    Created Object with serialno 3 and value true
    Created Object with serialno 4 and value true
    Created Object with serialno 5 and value false
    

    Each time we assign the values of True or False to new variables the copy constructor is called and can do, whatever you did in GetObject.

    Small variant: If we choose this alternative custom constructor instead of the one in the code,

        Object(bool b) : _b(b), _serialno(0) {};
    

    we would get as output:

    Created Object with serialno 1 and value true
    Created Object with serialno 2 and value true
    Created Object with serialno 3 and value false
    

    The difference is, whether the serialno is also counted up for True and False themselves or only after assigning those to a variable.

    For Generating True and False the first constructor is called, for the following assignments to other variables, the second constructor.

    You could also keep a bool _original variable inside Object to only call GetObject(), which states whether you copy from the original True or False. It is true only for True and False. You can recognize those by them calling a special constructor. If you want to make it safe to use, you can make that constructor private, so it can only be called by friend functions or by static factory methods.

    In the following code, GotObject is not called from assigning from c to f.

    #include <iostream>
    using namespace std;
    
    class Object;
    void GotObject(Object& o);
    
    class Object {
    public:
        Object(bool b) : _b(b), _serialno(0), _original(true) {};
        Object(const Object& other) : _b(other._b), _original(false), _serialno(0) {
            if (other._original)
                GotObject(*this);
        };
    
        bool _original;
        bool _b;
        int _serialno;
    };
    
    void GotObject(Object& o) {
        static int curserial = 0;
        o._serialno = ++curserial;
    
        cout << "Created Object with serialno " << o._serialno << " and value " << boolalpha << o._b << endl;
    }
    
    inline Object True(true);
    inline Object False(false);
    
    int main() {
        auto c = True;
        auto d = True;
        auto e = False;
        auto f = c;
    
        return 0;
    }
    

    Output:

    Created Object with serialno 1 and value true
    Created Object with serialno 2 and value true
    Created Object with serialno 3 and value false