How to "create" a variable in "runtime"?

8

I'm doing a project that implements a Python style console. It has several functions, it has many things. But I need to implement a command called set . set would declare a string and set its value as the user defined it.

class Variavel
{
    private:
    char* nome;
    char* valor;
    public:
    Variavel() : nome(NULL), valor(NULL)
    {
    }
    ~Variavel()
    {
        delete[] nome;
        delete[] valor;
    }
    void DefVar(const char* valor, const char* nome)
    {
        this->valor = new char[strlen(valor)];
        this->valor = const_cast<char*>(valor);
        this->nome = new char[strlen(nome)];
        this->nome = const_cast<char*>(nome);
    }
};

I could create a large array or a pointer, and with each instance that the client defined another variable, the array number would increase. When it asks for a variable, it will map each array to the end. It is a method that works, but it is very slow, it spends a lot of memory and it is a bit inefficient. How to create a runtime variable in an efficient way?

    
asked by anonymous 03.02.2014 / 19:47

3 answers

8

You can use a std::map to save your variable table. Example:

#include <map>
#include <string>
#include <iostream>

typedef std::string Variable;

std::map<std::string, Variable> table;
table["x"] = "1+1";
table["y"] = "1-1";
std::cout << table["x"] << std::endl; // 1+1

There are many classes in the default library to do this kind of management. Notice the container library .

You have several other problems in your Variavel class. First, you keep pointers to data that you have allocated (that is, you are the owner of those resources) and as such, it is your responsibility to release them. It is necessary to define a destructor delete as the pointers.

Also, there is no default constructor, so the compiler will create a blank for you that does not initialize the pointers. So in that case they will point to any invalid memory. You must define a default constructor (or some other). The DefVar function for example should be a constructor.

In order for the object to have value semantics (it can be treated as the primitive types), it must also have a copy constructor, which duplicates the allocation and a operator= that has operation similar to the copy constructor. >

This is called: Three's Rule . (Or Rule of Five in C ++ 11 because of being able to move objects).

Or easier than this: use std::string as I did in my example that already implements everything you need and has value semantics. Much simpler and more reliable than using mere pointers directly.

    
03.02.2014 / 19:58
2

The most efficient container for saving many objects is std::vector . Internally it is just an array. If you are going to need to do research on it, you can make it efficient by using the std::lower_bound algorithm to insert the elements in an ordered way into the vector, and then also to do the search.

#include <string>
#include <vector>
#include <algorithm>

struct Variavel {
    std::string nome, valor;

    Variavel(const std::string &nome, const std::string &valor="")
      : nome(nome), valor(valor) {
    }
};

//necessário para ordenar as variáveis no vector
bool operator<(const Variavel &lhs, const Variavel &rhs) {
    return lhs.nome < rhs.nome;
}

std::vector<Variavel> variaveis;

//adiciona uma variável
void setVar(const std::string &nome, const std::string &valor) {
    Variavel v(nome, valor);
    //Posição onde se adicionar para que a lista fique em ordem
    std::vector<Variavel>::iterator pos = std::lower_bound(variaveis.begin(), variaveis.end(), v);
    variaveis.insert(pos, v);
}

//Obtem uma variável. Retorna ponteiro nulo caso nao exista
const Variavel *getVar(const std::string &nome) {
    std::vector<Variavel>::iterator pos = std::lower_bound(variaveis.begin(), variaveis.end(), Variavel(nome));
    if (pos != variaveis.end() && pos->nome == nome) {
        return &(*pos);
    }
    else {
        return 0;
    }
} 

Probably in the future you will add more content to the variable class as its type. If it gets too large it may be worth turning it into a wrapper for a dynamically allocated class, thus minimizing the effect of object copies. But if you're already working with C ++ 11 this problem is minimized.

    
03.02.2014 / 20:31
1

What you want to create is a symbol table for your interpreter. At the lowest level, this is a map of strings (variable names) for memory addresses:

std::map<std::string, void*> tabelaSimbolos;

In this way, each declared variable would be allocated to an area of some kind. To elaborate a bit more (and that will probably be necessary), you will need information about this symbol. So you're going to need a data structure, telling you the type of your variable and where the data is actually. A structure similar to:

class Variavel {
  std::string tipo;
  void* dados;
};

And your map would change to:

std::map<std::string, Variavel*> tabelaSimbolos;

So, for each variable, you would access the corresponding structure, and you would know which type should interpret the data. Your map would look like this:

tabelaSimbolos["minhaString"] = {"string", 0x100000} 
tabelaSimbolos["meuInt"] = {"int", 0x2000000}

This is just a beginning, and quite primitive. Remembering that you will almost certainly have to deal with dynamic allocations, research on RAII to avoid headaches with news and deletes.

    
03.02.2014 / 20:36