You are here:

C++/Dynamic Variable Names

Advertisement


Question
Is it possible to declare a variable with a name from a string.

IE - I call a function with a string and a value like so

MakeVariable("VariableA", 25.5);

and the function does something to this effect.

MakeVariable(string var, double val)
{
  // Where var is actually "VariableA" from
  // function call above.
  double var = val;

}

So now I would be able to use the double variable named "VariableA".

I don't think this is possible in C++, I know you can do it in PHP and I'm pretty sure you can do it in Python.

Let me know what you think.

Thanks.

Answer
---------------------------
FOLLOWUP ANSWER:
---------------------------

Overnight (yes it was around midnight  - BST - when I posted my answer) I realised that I forgot to correct one of those errors I mentioned might creep in.

The insert operation of std::map<> classes in the form we want takes a single parameter of a std::pair type in which the first member is the key and the second the value. So my example MakeVariable function should look like:

       #include <string>
       #include <map>
       #include <utility>

       void MakeVariable( std::string const & var, double val)
       {
         double * pDbl( new double(val) );
         gDblVarMap.insert( std::make_pair(var, pDbl) );
       }

I also missed adding void as the return type which is C and C++ speak for no returned value i.e. the function is what might in other languages be called a procedure. This oddity came about as C originally assumed that any function that did not specify a return type returned an int value.

Note the use of the C++ library function template std::make_pair<>() to turn the two separate parameter values into a single std::pair<> value in fact the std::pair<> type is std::pair<std::string, double*> - the key and value types of the map, std::pair is <> a template (like a lot of the C++ standard library, including std::map<> and std::basic_string).

If you do not have decent C++ tutorials and references the I suggest you obtain some here are some suggestions:
Accelerated C++ by Barbara Moo and Andrew Koenig a tutorial often cited as one of the best for learning C++ if you have done some programming before.

The C++ Programming Language 3rd edition by Bjarne Stroustrup the standard work by the creator of C++. You might also consider the special edition version.

The C++ Standard Library a Tutorial and Reference by Nicolai M. Josuttis a very useful reference for the likes of std::map, std::pair and all the rst of the C++ library.

C++ Templates the Complete Guide by David Vandevoorde and Nicolai M. Josuttis a similarly useful reference this time for the various and sometimes convoluted features and uses of C++ templates.
       
Effective C++, More Effective C++ and Effective STL by Scott Myers essential reading for C++ programmers.

The C++ Standard and the C Standard the text of the language standards available in book form from Wiley and as pdf download for $18 from the ANSO website.

Sorry for the error and I hope this has not confused you too much.

------------------------
ORIGINAL ANSWER:
------------------------
You are correct that you cannot do this directly in C++ - just as you cannot create types at runtime. This is because C++ uses a static compilation and linkage translation model and has very limited runtime support - unlike more script like languages such as PHP - or those that have their own runtime system such as Java with the Java Virtual Machine (JVM) or .NET languages with the Common Language Runtime (CLR).

This allows C++ (like C) to be used in situations where you would not have such a runtime system available - operating systems or a JVM or CLR implementation for example. On the other hand this does mean you have these sorts of limitations.

In your case the way forward is to do it yourself. You can create an object (or variable if you prefer) dynamically at runtime using new (and destroy it using delete of course):

       double * pointer_to_double( new double(value) );

If the new operation fails - e.g. due to lack of memory during program execution - then new throws a std::bad_alloc which you can catch and handle either directly or in some outer calling function:

       double * pointer_to_double(0);
       try
       {
         pointer_to_double = new double(value);
       }
       catch ( std::bad_alloc & )
       {
         // Handle allocation failure
       }

I shall ignore error handling concerns for the purposes of demonstration from here on in, however this does not mean that your production code should not handle such conditions.

Now to the do it yourself part. You can create the objects you wish on the fly at runtime, but you also need to associate them with a name string. You can do this using some sort of key,value mapping in which the key is the name string and the value is the (pointer to the) object. You can use std::map<std::string, double*> to achieve this:

       #include <string>
       #include <map>

       namespace
       {
         std::map<std::string, double*>  gDblVarMap;
       }

       MakeVariable( std::string const & var, double val)
       {
         double * pDbl( new double(val) );
         gDblVarMap.insert( var, pDbl );
       }

I have left out checking for such things as ensuring the variable does not exist already and that the name is valid (e.g. not empty, only contains alphanumeric characters or underscore, first character not numeric).

I have defined a global map of strings keys and double pointer values in an anonymous namespace - which means that other source modules cannot access gDblVarMap even though it is global and has external linkage.

I also tidied up your function signature - passing the string by reference to a constant string rather than passing a copy of the string.

You will find that you will have to add other services - to access the variables for example:

       double GetDouble( std::string const & varName );

Which might throw if the name did not match an existing variable, or you could use something like:

       bool GetDouble( std::string const & varName, double & value );

Which returns a true value if the variable value is found or false if not.

You will also require some means to tidy up when you wish to destroy a variable - which will remove the variable from the map and explicitly delete the double. You will also require an operation to do this for all existing variables in the map.

Doing these sort of things suggests using a class to associate the data (e.g. the variable map) with the operations (e.g. MakeVariable):

       class Doubles
       {
       public:
         ~Doubles();
         void   Add( std::string const & varName, double value );
         bool   Get(std::string const & varName, double & value );
         bool   Delete( std::string const & varName );

       private:
         typedef std::map<std::string, double*>  DblVarMapType;

         DblVarMapType        iMap;
       };


~Doubles() is a destructor and should iterate through the map keys calling Delete to both remove each variable from the map and to delete it.

You could use the Doubles class like this:

       // GlobalDefs.cpp
       Doubles   gDblVars;

       // Module1.cpp

       // ...

       gDblVars.Add( "double1", 1.1);
      
       // ...

       // Module2.cpp

       // ...

       double value(0.0);
       if ( gDblsVars.Get( "double1, value ) )
       {
         // use value
       }
       else
       {
         // handle non-existent variable
       }

       // ...


Although the use of global variables should not be encouraged of course. So making Doubles a singleton class - if you only wish there to _ever_ be one set of double variables in you program may be preferable - or use it in the creation of a singleton object (either as a sub-type of Doubles or containing a Doubles instance) or even just a function that returns an instance of a Doubles:

       Doubles & GlobalDoublesInstance()
       {
         static Doubles variables;
         return variables;
       }

One way to implement Doubles as a singleton is to add a static instance member function and declare (and define) a private constructor:

       class Doubles
       {
       public:
         static Doubles & Instance();

         ~Doubles();
         void   Add( std::string const & varName, double value );
         bool   Get(std::string const & varName, double & value );
         bool   Delete( std::string const & varName );

       private:
         typedef std::map<std::string, double*>  DblVarMapType;

         DblVarMapType        iMap;

       // private constructor:
         Doubles() {}
       };

Making the constructor private prevents anything other than Doubles members from creating Doubles instances. In this case the member that does so is the static Instance member - which is implemented just like the previous GlobalDoublesInstance example. You then access the double variables like so:

       Doubles::Instance().Add( "double1", 1.1);

Finally, all this has come straight out of my head so there is no guarantee that the examples a typo free - or do not contain other errors (although I did read it back before posting to you!).

Hope this has given you some food for thought.

PS: Try the ACCU web site for developer resource links and book reviews: http://www.accu.org/.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.