Trying to make a settings library

I am working on a library that allow settings to be easily stored in a file and easily accesed through functions, values and names.
The format I chose is this:

<name> : <value>

This is what i have so far.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  #include <iostream>
#include <fstream>
#include <stdlib.h>
#include <cstring>

using namespace std;
int lines(char a[255])
{
  int number_of_lines = 0;
string line;
ifstream myfile(a);
while (getline(myfile, line))
    ++number_of_lines;
return number_of_lines;
}
void newSetting(char file[255],char name[255],int value)
{
  ofstream fout(file,ios::app);
  fout<<name<<" : "<<value<<endl;
}
int getName(char a[255],char copyIn[255])
{
  char b[255];
  for(int i=0;a[i]!=' ';i++)
    b[i]=a[i];
  strcpy(copyIn,b);
}
int getNum(char a[255],char cpyIn[255])
{
  char name[255];
  getName(a,name);
  for(int i=(strlen(name)+3);i<strlen(a);i++)
  {
    cpyIn[(i-strlen(name)-3)]=name[i];
    cout<<"cpyIn[("<<i-strlen(name)-3<<")]=name["<<i<<"]";
  }
}
int getValue(char file[255],char name[255])
{
  char a[255][255];
  ifstream fin(file);
for(int i=0;i<lines(file);i++)
{
  fin>>a[i];
  char ff[255];
  getName(a[i],ff);
  if(strcmp(ff,name)==0)
  {
    cout<<"FOUND!"<<endl;
    cout<<a[i]<<endl;
    char num[255];
    getNum(a[i],num);
    return atoi(num);
  }
}
return NULL;
}


The following functions ae tested and are known to work:

-lines
-newSetting
-getName (extracts <name> from the setting and copies it in the second paramater)

And i can't get getValue to work. I tried using atoi() and getNum, but getnum, altough the cout<<"cpyIn[("<<i-strlen(name)-3<<")]=name["<<i<<"]";
shows taht it is working properly, nothing gets copied when i tested it with some basic code.
Any ideas?
Last edited on
Before I comment on the code, may I ask a question about the file format.
The format I chose is this:

<name> : <value>

Is it intended that the <name> should be just a single word, or might it contain several words?

Is it intended that <value> will always be an integer, or might it be a floating-point or string type?

The reason I ask is that I have two comments so far. First, the code is mostly C-style rather than C++. Second it seems overly complicated, there are library functions such as searching or manipulating strings. Doing it all yourself is certainly good practice, but there may be simpler approaches.
@Chevril, thank you very much for your response, the name will always conatin 1 word, but may contain (- and _) and the value is always an integer, as for libraries if you know ANYTHING that could do this, and is not MORE complicated than what I've done, please tell me. I coudln't get atoi() to work with getNum(), so if you have any ideas, please share them with me, as I'd be glad to try them out.
The function getValue() has a design problem. (or maybe several)

At line 44,
 
    fin>>a[i];


Let's say for example, the file contains
orange : 5

the above code will read the string "orange" into the character array a[i]

That's the only use of the file stream fin in that function. There is no attempt to read the corresponding value from the file.

I don't think you need an 2D array of strings in that function, since it is only examining one item at a time. More important, you need to read both the key and the value. How you would do that will depend on the file contents.

If <name> can contain multiple words like this:
oranges and lemons : 5

then you would need a different approach as compared with the case where <name> is always just one word such as "oranges_and_lemons" which contains no whitespace.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int getValue(const char * file, const char * name)
{
    char key [255];
    char separator;
    int value;
  
    ifstream fin(file);
    
    while (fin >> key >>  separator >> value)
    {
        if (strcmp(key, name) == 0)
        {
            cout << "FOUND!" << endl;
            cout << key << endl;
            return value;
        }
    }
    
    return 0;
}

OMG! I forgot about that! And that means i can just read the value! Thanks @Chevril! You are a life saver.
Also in int getValue(const char * file, const char * name), why the stars? I know its something to do with pointers tough i have no idea what they do, could you explain that to me? I don't feel confortable just straight out USING code without knowing how it works, also, while (fin >> key >> separator >> value) how does this work? Does fin return 0 if nothing else is left in the file? Please explain. Thank you alot! Again you are a life saver.
Last edited on
I would rather create a class like this - interface shown only:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <string>
#include <map>

using StringMap = std::map<std::string, std::string>;

class Settings
{
public:
  Settings()
  {
  }
  void load(const std::string filename);
  void save(const std::string filename);

  std::string readString(const std::string& key, std::string defValue);
  int readInt(const std::string& key, int defValue);
  double readDouble(const std::string& key, double defValue);
  bool readBool(const std::string& key, bool defValue);

  void setString(const std::string& key, const std::string& value);
  void setInt(const std::string& key, const int value);
  void setDouble(const std::string& key, const double value);
  void setBool(const std::string& key, const bool value);
private:
  StringMap mSettings;
}


Usage:
1
2
3
4
Settings settings;
settings.load("app.settings");
settings.setString("app.name", "Name of the app");
string appName("app.name", "Some default value);  
@Thomas1965 I like your approach. I already considered something very similar where the filename was passed in the constructor, and stored. Then when the object is destroyed, it can automatically save the map contents back to the file - if it has been modified.

@masecla33
Also in int getValue(const char * file, const char * name), why the stars? I know its something to do with pointers tough i have no idea what they do, could you explain that to me? I don't feel confortable just straight out USING code without knowing how it works,

When an array is passed to a function, what is actually passed is a pointer to the start of the array. Hence it works the same as before. char * is how a character pointer is declared.
If you call the function like this:
 
    int n = getValue("settings.txt", "orange");
the size of the string obviously isn't 255. Those literals are also read-only, which is why I put const in the function parameters.
you can read the tutorial about pointers here:
http://www.cplusplus.com/doc/tutorial/pointers/

also, while (fin >> key >> separator >> value) how does this work? Does fin return 0 if nothing else is left in the file? Please explain.

That code first attempts to read from the file fin. Then it tests the status of the file.

You might do this in separate lines like so
1
2
3
4
5
    fin >> key >> separator >> value;
    if (!fin)
    {
        cout << "an error occurred";
    }

When it is done in the loop condition, the body of the loop is executed only after a successful file access.

You already did something very similar in the function lines()
1
2
    while (getline(myfile, line))
        ++number_of_lines;

Thank you alot! Again you are a life saver.

You're welcome.
... but you still didn't answer the questions I asked.

Can <name> contain more than one word?
Is <value> always an integer?


edit: sorry somehow through mistiming I missed the earlier reply. sorry about that.
Last edited on
I already considered something very similar where the filename was passed in the constructor, and stored.

That's a good idea, maybe as a second constructor. I was thinking that maybe the class could also be used without files, just for in memory settings.
Topic archived. No new replies allowed.