Tuesday, December 11, 2012

Dynamic Variable Names in C++

I had this post created some 5 years back, but never published it. Not sure if this is exact repost of content of other site or data collected by me from various sites and composed. Posting it as it is very interesting. If anyone has information about original website/blog, do let me know. We use a custom language based on Lua in our product development. One of the important features of Lua is no variable distinction for the scripting language user. Meaning is a variable is currently pointing to a integer value, in the next step it can be made to point to a char * (string) value.

And more amazing feature is, suppose you got a variable say abc which points to integer value 10. Now what one can do is call a function which will give value pointed to by the variable whose name is same as the string value being passed to the function, say

process_string(“abc”).

The process_string function will return the value 10.

It always made me wonder how achieved this because one cannot create dynamic variable names in C++, which scripting languages allow.

When a C++/C code is compiled, it loses its symbol base identification. This is because C++ uses a static compilation and linkage translation model and has very limited runtime support. So one cannot create Dynamic Variable Names in C/C++ directly which is possible in 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 create an object (or variable if you prefer) dynamically at runtime using new (and destroy it using delete of course)

To create Dynamic Variable Names you need to create dynamic memory and store in a map in format of stringName, value pair/pointer.

That is what lua does. That’s why u can do process_string in lua. When you create a variable say local a = 10,
a (string “a”, new int() = 10) gets stored in it’s internal map.


Example
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 to achieve this:
       #include “string”
       #include “map”
// I cant add the brackets symbols in typesetting this html page. 
// That’s why I have replaced brackets in include with “
       namespace
       {
               std::map  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  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  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!).

Overnight 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 - 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).


Programming Ring - Old Post

No comments: