1

The cplusplus.com documentation on getenv() states...

The pointer returned points to an internal memory block, whose content or validity may be altered by further calls to getenv

...which I take to mean, "If you want to keep the content, copy it." So, since I need to retrieve several variables, I wrote a couple of little wrapper functions:

#include <iostream>
#include <string.h>

using namespace std;

void getEnv (char *val, const char *var) {
    val = nullptr;
    char *enVar = getenv(var);
    if (enVar != nullptr) {
        val = new char[strlen(enVar) + 1];
        strcpy(val, enVar);
    }
}

void getEnv (int &val,  const char *var) {
    val = -1;
    char *enVar = getenv(var);
    if (enVar != nullptr) {
        val = atoi(enVar);
    }
}

int main() {
    char *textMode = nullptr;
    int  cLen = 0;

    getEnv(cLen, "CONTENT_LENGTH");
    cout << cLen << endl << endl;

    getEnv(textMode, "TEXT_MODE");
    if (textMode == nullptr)
        cout << "Not set.";
    else 
        cout << "[" << textMode << "]<br>\n";

    return 0;
}

The int version works as expected, but I get nothing back from the char version, and I mean nothing: if I don't initialize *textMode at declaration it remains an uninitialized pointer.

It's pointers, right? Right? I know it is. Gotta be pointers. I'll figure them out one of these days, but hey -- at least I got my linked list to work! Yay!

alanlittle
  • 460
  • 2
  • 12
  • Your program must not be compiling. – Shravan40 Feb 10 '17 at 21:20
  • It compiles fine. I've been fiddling with it for the past hour, trying various things, and it compiles every time. – alanlittle Feb 10 '17 at 21:21
  • Notice how you take the `int` as a reference but take the `char*` by value. – Kevin Feb 10 '17 at 21:22
  • Yes, because I don't need to allocate memory for the `int`. An `int` is an `int`, so I can just pass it by reference, but the `char` has to be a pointer so I can allocate memory for it. – alanlittle Feb 10 '17 at 21:24
  • Allocating memory has nothing to do with it. You're modifying `val` in both cases but this only has an outside affect when it's a reference. See my answer. – Kevin Feb 10 '17 at 21:26
  • @alanlittle Why do you continue to use pointers? If you want to have a decent wrapper, don't. – LogicStuff Feb 10 '17 at 21:27
  • Surely you should be using `std::string` in some shape or form. – Jonathan Leffler Feb 10 '17 at 21:29
  • FWIW, your program leaks memory. If you are intending to use this in a large program, you may want to use a different strategy. Use a class called `EnvironmentVariable` whose destructor can take care of deallocating memory. – R Sahu Feb 10 '17 at 21:29
  • @LogicStuff I don't know how else to deal with char strings, apart from `std::string`, but I'm trying to do as much as possible with c-strings, as 1) I need to learn, and 2) it's more efficient. – alanlittle Feb 10 '17 at 21:32
  • @RSahu Where is it leaking memory? Where is the `EnvironmentVariable` class? – alanlittle Feb 10 '17 at 21:33
  • @alanlittle, `getEnv` allocates memory to make a copy of the value returned by `getenv`. I don't see any code that deallocates that memory. You don't have a class called `EnvironmentVariable`. I am suggesting that you should create one and let it take care of deallocating memory. – R Sahu Feb 10 '17 at 21:35
  • @RSahu Ah, OK, I see. I guess I thought it deallocated when it went out of scope. I should have known better! :) I'll have to check if I have that problem anywhere else. It amazes me how many little details that are taken care of in higher languages, have to be given direct attention in C++. But that's a good thing -- I'm totally geeking. Thanks for the...pointer ::groan:: – alanlittle Feb 10 '17 at 21:40
  • It's not taken care of for you because you didn't ask for it. `std::string` frees the memory it uses in its destructor. You used `char*` which has no destructor. – Kevin Feb 10 '17 at 21:52
  • @RSahu Well, I added `delete enVar;` to the functions, and the program crashes at that point. I don't know about the inner workings of `getenv()` I assumed that it allocates memory, and simply returns a pointer to that, but apparently not. Does the function somehow manage the deallocation itself? I can't imagine how that would work. I know that if I write a function which allocates memory and returns a pointer to it, the caller can then `delete` it, so I'm not sure what's going on. – alanlittle Feb 13 '17 at 14:12

2 Answers2

1

Your second function takes val (an int) by reference: void getEnv (int &val, const char *var) and so can modify the variable passed to it as you expect.

Your first function takes val (a char*) by value: void getEnv (char *val, const char *var) so modifying val has no affect on the variable passed to it. A simple solution is to simply take it as a reference as well: void getEnv (char *&val, const char *var)

Kevin
  • 6,993
  • 1
  • 15
  • 24
  • Ahh! I see. I didn't know you could do that -- I just figured that, since it's a pointer, it's already passing an address, so it wouldn't be a problem. I think I see, though -- it's passing the *value* of `textMod`, which is `nullptr`, and the function can't do anything with that. OK, thanks. I'll get it one of these days! – alanlittle Feb 10 '17 at 21:29
0

Follow up to my comments and the OP's response to them.

Here's what I was thinking:

#include <iostream>
#include <string.h>

using namespace std;

// Use a class to encapsulate the data need to be captured
// in an environment variable.    
class EnvironmentVariable
{
   public:

      EnvironmentVariable(char const* name) : name_(name), isSet_(false)
      {
         char *val = getenv(name);
         if ( val != nullptr )
         {
            isSet_ = true;
            this->value_ = val;
         }
      }

      bool isSet() const
      {
         return isSet_;
      }

      void getValue(char const*& val) const
      {
         if ( isSet_ )
         {
            val = this->value_.c_str();
         }
         else
         {
            val = nullptr;
         }
      }

      void getValue(int& val) const
      {
         if ( isSet_ )
         {
            val = stoi(this->value_);
         }
         else
         {
            val = 0; // Find a suitable default value
         }
      }

   private:
      std::string name_;
      std::string value_;
      bool isSet_;
};

int main() {
   char const* textMode = nullptr;
   int  cLen = 0;

   EnvironmentVariable env1("CONTENT_LENGTH");

   env1.getValue(cLen);
   cout << cLen << endl << endl;

   EnvironmentVariable env2("TEXT_MODE");
   env2.getValue(textMode);

   if (textMode == nullptr)
      cout << "Not set.\n";
   else 
      cout << "[" << textMode << "]<br>\n";

   return 0;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I like it. A couple of questions: 1) What is this `: name_(name), isSet_(false)` I'm guessing it assigns default values? Why not just do that in the constructor? And `name_` is not used anywhere. 2) You don't `delete val` (see my last comment above). Is it not necessary? – alanlittle Feb 13 '17 at 18:01
  • @alanlittle, `name_` is available for use. You can add a member function `std::string getName() const;` to provide access to the name of the environment variable. – R Sahu Feb 13 '17 at 18:02
  • @alanlittle, the `: name_(name), isSet_(false)` part is C++ syntax for initializing those member variables. – R Sahu Feb 13 '17 at 18:03