2

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?

Patrick vD
  • 684
  • 1
  • 7
  • 24
  • 4
    How is the global constant if you need to update it with runtime values? – spectras Feb 15 '21 at 23:50
  • @specras If you take a look at the question I just referenced, you can see that it is in fact possible to have your constants set once, at runtime. The point would be for that variable to be set right when the program is run (based on the arguments passed) but then be considered constant by the rest of the program. – Patrick vD Feb 15 '21 at 23:54
  • The question you referenced does not set **global** constants. – spectras Feb 16 '21 at 00:01
  • 1
    @PatrickvD I doubt that's possible to do in a portable way, at least not as literally stated. On the other hand, if you are looking for implementation specific solutions, there may be non-standard ways to access `argc` and `argv` outside `main`, which would then allow using any one of the singleton patterns. – dxiv Feb 16 '21 at 00:10
  • @spectras The original question I found was about global constants, and was marked as a duplicate of the one I linked. I just checked, and you are right, the question I linked doesn't specifically mention global, but several of its answers do, so I would still consider it a valid reference. – Patrick vD Feb 16 '21 at 00:21
  • 1
    @PatrickvD> actually only one of the answers does, and it specifically uses `std::cin` which is special-cased by the C++ standard to be usable in global initializers without running into the initialization order fiasco. It's a very peculiar edge case. – spectras Feb 16 '21 at 00:37

2 Answers2

3

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.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
2

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.

LHLaurini
  • 1,737
  • 17
  • 31