James Thompson
James Thompson

Reputation: 48172

Accessing Environment Variables In C++

I'd like to have access to the $HOME environment variable in a C++ program that I'm writing. If I were writing code in C, I'd just use the getenv() function, but I was wondering if there was a better way to do it. Here's the code that I have so far:

std::string get_env_var( std::string const & key ) {                                 
    char * val;                                                                        
    val = getenv( key.c_str() );                                                       
    std::string retval = "";                                                           
    if (val != NULL) {                                                                 
        retval = val;                                                                    
    }                                                                                  
    return retval;                                                                        
}           

Should I use getenv() to access environment variables in C++? Are there any problems that I'm likely to run into that I can avoid with a little bit of knowledge?

Upvotes: 113

Views: 161415

Answers (7)

einpoklum
einpoklum

Reputation: 131547

Another alternative to @Vlad's getenv() wrapper would use optionals, available since C++17; and with a little implicit fallback regarding empty-string sematics, we get the :

inline std::optional<std::string_view> get_env(const char* key) {
    if ((key == nullptr) or (*key == '\0')) { return {}; }
    auto raw_value = getenv(key);
    if (raw_value == nullptr) { return {}; }
    return std::string_view { raw_value };
}

Note you could, instead of an optional, throw exceptiosn on failure (see my other answer)..

Upvotes: 0

einpoklum
einpoklum

Reputation: 131547

A version of @Vlad's answer with some error checking and which distinguishes empty from missing values:

inline std::string_view get_env(const char* key) {
    if (key == nullptr) {
        throw std::invalid_argument("Null pointer passed as environment variable name");
    }
    if (*key == '\0') {
        throw std::invalid_argument("Value requested for the empty-name environment variable");
    }
    const char* ev_val = getenv(key);
    if (ev_val == nullptr) {
        throw std::runtime_error("Environment variable not defined");
    }
    return std::string_view { ev_val };
}

Notes:

  • You could also replace the use of exceptions in the above with an std::optional<std::string_view> or, in the future, with an std::expected.
  • I've chosen safety over informativity here, by not concatenating the key into the what-string of the exception. If you make the alternative choice, try and limit copying from key to within reason (e.g. 100 characters? 200 characters?), and I'd also check these characters are printable, and sanitize those characters.

Upvotes: 4

OriginalHacker
OriginalHacker

Reputation: 795

Yes, I know this is an old thread!
Still, common mistakes are, by definition, not new. :-)

The only reasons I see for not just using std::getenv(), would be to add a known default or to adopt common pattern/API in a framework. I would also avoid exceptions in this case (not generally though) simply because a non-value return is often enough a valid response for an environment variable. Adding the complexity of handling exceptions is counter-intuitive.

This is basically what I use:

const char* GetEnv( const char* tag, const char* def=nullptr ) noexcept {
  const char* ret = std::getenv(tag);
  return ret ? ret : def; 
}
    
int main() {
  int ret=0;
  if( GetEnv("DEBUG_LOG") )  {
    // Setup debug-logging
  } else {
     ...
  }
  return (-1==ret?errno:0);
}

The difference between this and the other answers may seem small, but I find such small details are very rewarding when you form habits in how you code.

Just like the fact that getenv() returns a non-const pointer, which could easily lead to bad habits!

Upvotes: 4

Brian R. Bondy
Brian R. Bondy

Reputation: 347216

Why use GetEnvironmentVariable in Windows, from MSDN getenv:

getenv operates only on the data structures accessible to the run-time library and not on the environment "segment" created for the process by the operating system. Therefore, programs that use the envp argument to main or wmain may retrieve invalid information.

And from MSDN GetEnvironment:

This function can retrieve either a system environment variable or a user environment variable.

Upvotes: 24

Matt Davis
Matt Davis

Reputation: 46034

There is nothing wrong with using getenv() in C++. It is defined by stdlib.h, or if you prefer the standard library implementation, you can include cstdlib and access the function via the std:: namespace (i.e., std::getenv()). Absolutely nothing wrong with this. In fact, if you are concerned about portability, either of these two versions is preferred.

If you are not concerned about portability and you are using managed C++, you can use the .NET equivalent - System::Environment::GetEnvironmentVariable(). If you want the non-.NET equivalent for Windows, you can simply use the GetEnvironmentVariable() Win32 function.

Upvotes: 102

Mykola Golubyev
Mykola Golubyev

Reputation: 59834

In c++ you have to use std::getenv and #include <cstdlib>

Upvotes: 11

Vlad
Vlad

Reputation: 559

I would just refactor the code a little bit:

std::string getEnvVar( std::string const & key ) const
{
    char * val = getenv( key.c_str() );
    return val == NULL ? std::string("") : std::string(val);
}

Upvotes: 53

Related Questions