Patrick vD
Patrick vD

Reputation: 789

How do I initialize a constant global variable from a runtime arguments?

So, I've seen questions here on how to set global constant variables at runtime by prompting the user for input then using cin. However, I need to be able to set a constant global variable based on one of the arguments passed to main (ie from argc/argv). All of the solutions I found involved having a constructor function defined above the const variable's declaration, but this won't work if I can only access argc and argv from main.

Is this at all possible? How could I implement this?

Edit: Here's the question that talks about cin, for reference: Are there any tricks to use std::cin to initialize a const variable?

Upvotes: 2

Views: 2522

Answers (2)

LHLaurini
LHLaurini

Reputation: 2570

It is not possible to initialize global constants at main. By the time your program reaches it, all globals must have already been initialized.

However, it is possible to write a class that contains a value and only allows you to set it once. The check will have to be done at runtime, however.

Something like this (std::optional requires C++17, but you don't have to use it):

#include <optional>

template <typename T>
class LateConstant
{
public:
    LateConstant() = default;
    LateConstant(const LateConstant&) = delete;
    LateConstant& operator=(const LateConstant&) = delete;

    LateConstant& operator=(const T& x)
    {
        if (!v.has_value())
        {
            v = std::make_optional<T>(x);
        }
        else
        {
            std::terminate();
        }

        return *this;
    }

    bool has_value() const
    {
        return v.has_value();
    }

    const T& value() const
    {
        return v.value();
    }

private:
    std::optional<T> v;
};

With this, if you try to assign a value more than once, your program intentionally crashes. It's not a constant, but it may still be useful.

Now, to be honest, you'd better avoid globals anyway. I haven't used them in years, after I've been bitten several times. If you decide not to use globals, you can use constant locals instead or collect the data you need in a class and pass that around.

Another possibility is to use something like

int whatever(int i = 0)
{
    static const int stored = i;
    return stored;
}

If you try to set it more than once, it will just ignore the value, which could be a bad idea.

Upvotes: 2

Sam Varshavchik
Sam Varshavchik

Reputation: 118445

Objects in global scope gets constructed before main() gets invoked, so it's logically impossible to use main's argc and argv to construct them. This is a fairly fundamental chicken-and-egg problem, and there is no portable way around it.

It might be possible to cheat and use function static scope to simulate it, something like:

class MySingleton {
public:
    MySingle(int argc, char **argv);

    // ...
}

const MySingleton &global_singleton(int argc=0, char **argv=nullptr)
{
    static MySingleton instance{argc, argv};

    return instance;
}

int main(int argc, char **argv)
{
    global_singleton(argc, argv);
}

This might work as long as nothing else in global scope calls global_singleton in its constructor. main() does it ASAP, passing in the real argc and argv, and everyone else just calls it with the useless parameters defaulted.

Upvotes: 3

Related Questions