Boost logo

Boost :

From: mfdylan (dylan_at_[hidden])
Date: 2002-01-21 00:27:45


Hi,

After being burnt several times by the (IMHO, crazy) POSIX
definitions of how putenv and getenv work, I finally cracked and
realised a C++ wrapper was needed to prevent stupid mistakes like
accidentally clobbering existing environment variables or previously
extracted values (the POSIX definition of putenv says the string
passed in becomes part of the environment and modifications to the
string after this affect the environment - furthermore calling putenv
can invalidate a string previously returned by getenv).

Anyway this is my first crack at it - I also added in the ability to
easily set and retrieve non-strings, which I use quite a lot (mainly
integers and bools):

namespace env
{

typedef std::string string;

bool get(const char* name, string& value)
{
        assert(name);
        const char* v = getenv(name);
        return v ? (value = v, true) : false;
}

template <class T>
bool get(const char* name, T& value)
{
        assert(name);
        const char* v = getenv(name);
        if (!v) return false;
        std::stringstream s(v);
        return (s >> value);
}

template <class T>
bool get(const string& name, T& value)
{
        return get(name.c_str(), value);
}

bool set(const string& name, const string& value)
{
// NB not thread-safe, need lock here
        static std::map<string, string> m_strings;

        m_strings[name] = name + '=' + value;
        putenv(m_strings[name].c_str());
        return true;
}

template <class T>
bool set(const string& name, const T& value)
{
        std::stringstream s;
        s << value;
        return set(name, s.str());
}

bool set(const char* name, const string& value)
{
        assert(name);
        return set(string(name), value);
}
        
template <class T>
bool set(const char* name, const T& value)
{
        return set(string(name), value);
}

} // namespace env

///////////////////////

Sample usage might be:

std::string path;
if (env::get("PATH", path))
   path += ':';
env::set("PATH", path + my_directory);
int level = 0;
env::get("USER_LEVEL", level);
env::set("USER_LEVEL", level + 1);

The other alternative would be to treat it like an associative array:

env["PATH"] = path + "my_directory";
int level = env["USER_LEVEL"];

but then you'd need another call (and function) to determine whether
an environment variable was set at all, and to define some sensible
behaviour when trying to access a non-existent variable, or when the
conversion from string fails (throwing an exception I think is
overkill, so I suppose setting an error state is the only other
possibility).

I really do need to make the set function thread-safe, which would
probably require using locking classes from the boosts thread library
(I would try to make this an optional dependency, for applications
that don't need thread-safety here).

Comments, suggestions?

Dylan


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk