The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

How to manage program data

I have a whole bunch of variables in my app, each with an associated tag. I want to write all this as config data to a file in the easiest way possible. I think the way I did it is incorrect, so please let me know...

I great a global array of CStrings to hold the variables. I have another array of CStrings to hold the tags. Then I have a bunch of #defines that are simple integer indexes into the CString array and set/get the relevent data. When I wish to store the information, I have a loop that runs across the CString arrays and writes an associative XML file.

Thursday, December 23, 2004
 
 
I would put the values and the tags in the same structure or class and then make the array out of that instead of having two separate arrays of strings floating around.

And then I would scrap that system and set up a hash container in which each value's index is by the actual tag string. At this point, you no longer need to maintain those pesky defines and everything suddenly starts working properly.
Dennis Atkins
Thursday, December 23, 2004
 
 
...and if you do not have a hash container at your fingertips, the STL map class will also do the work. ;-)
Gerd Riesselmann
Thursday, December 23, 2004
 
 
> you no longer need to maintain those pesky defines and everything suddenly starts working properly

Actually if you throw away thr defines you lose any chance of the compiler catching your typing mistakes. Compare

getSetting("MySeting"); //spelt incorrectly

with

#define  MySetting "MySetting"
...
getSetting(MySeting); //also spelt incorrectly


I agree with the hash/map though, it is probably going to be generally quicker than an array.

Thursday, December 23, 2004
 
 
Thanks for all your inputs... I have actually now refined what I originally had to the following:

int varUsername, varUserId, varType...;

typedef struct
{
  int  &pos;
  const TCHAR *tagName;
  const TCHAR *initValue;
} myStringVariable;

static myStringVariable StringVariables[NUM_VARS] =
{
    { varUsername, "Username", "Default" },
    { varUserId, "Id", "0" },
    { varType, "Type", "Normal" }
    ...
}

CString UserVariables[NUM_VARS];

** In my initialization I have something like this:

for (int i=0; i<NUM_VARS; i++) {
    StringVariables[i].pos = i;
    UserVariables[i] = StringVariables[i].initValue;
}

** And to manage the variables:

void SetVariable(int n, char *val) {
    UserVariables[n] = val;
}

CString GetVariable(int n) {
    return UserVariables[n];
}


So can you see what's happening? My initialization gives an index to each of my variables (varUserName=0, varUserId=1...) and I can then use this index to do stuff such as SetVariable(varUserName, "David").

But most importantly, and the main purpose of this code, I can easily do the following:
* Add new variables easily. Just create a new 'int' variable and add it to StringVariables.
* Enumerate variables to XML for storage. This is a simple 'for' loop that dumped each variable and its associated tag.


Whew. Sorry for the long mess. However, I am still wondering if there's a beter way to do this stuff. You see, all the above code is actually global to the program, because these are parameters that run throughout. OO guys hate this, but then again these are actually global variables used in all my code, so what's the issue? Moreso, I am curious again if there's a better way to do this, noting that my method allows me to easily add new variables and to enumerate them to XML.

Friday, December 24, 2004
 
 
It's good to see that you're trying to improve your coding practices but I still think you have a bit of a way to go. I think one of the problems for inexperienced programmers, particularly bright ones, is to  get the feel for the right level of abstraction and the right abstractions to use. It's easy to think that you need to build a framework for everything when often a simpler approach will do.

I can't quite see why in this case you want things to be so complicated. You have some paramaters that you want to manage and you want to be able to store them. This is a pretty common problem and one that object oriented languages are designed to solve. Instead of defining new parameters by making a new int and a reference into a parameters table, why not store the variables directly in a class and make that class responsible for reading and writing the data.

So you could make your code much simpler by just having a global configuration class with all of your variables as public properties. Add a static function to read and write to XML and you're done. And with something this simple, the XML serialisation should be a piece of cake.

This approachs seems better to me than what you had. It's ugly, but straightforward. For a small app it might be OK. But in a larger, longer lived app it would start to be a problem.

As I said, this is a pretty common problems and OO languages like C++ can deal with it pretty well. And most apps have somthing like this. Without knowing the details of your application, a typical approach might be something like this. (Warning: While I have over decade of C/C++ experience, for the last 2 years I have been coding in C#.  Add in extra C++ guff as required).

class User {
public:
  User(string Name, int Id, string Type) {...}

  string getName() {..}
  void setName(string Name) {..}
  int getId() {..}
  void setId(int Id) {..}
  etc...

  void SerializeToXml(stream XmlStream) {...}
  static User& CreateFromXml(stream XmlStream) {...}
  static User& GetDefault() {...}
};


class Configuration {
public:
  User& GetCurrentUser() {...}
  void SetCurrentUser(User& U) {...}
  int GetSomeOtherConfigSetting() {...}
  void SetSomeOtherConfigSetting(int S) {...}
  etc.. for all other configuration objects and variables

  void SerializeToXml(stream XmlStream) {...}
  InitializeFromXml(stream XmlStream) {...}

  static Configuration Instance() {...}

private:
  Configuration() {...}
};


Now I've left some code to be written in the {...} but there's not much to it. You can have a little framework for Serializing to Xml. You could also get rid of the User class if there is no other need for it (but I think that typically there would be).

Also note that your OO thought are right - there really shouldn't be global variables. Sure there may be some stuff that is referenced throughout the app but it can usually be grouped into classes. A common approach is to use static variables within classes. These are like globals but have local scope. In C++ this is a bit of a pain and can cause problems with initialisation order. A better approach is to use the Singleton pattern, as I have shown above. Google for more info. Note the private constructor.

The Configuration class groups together all configuration settings, but still delegates some of the work to other classes (like User).

You can use this stuff pretty simply.
n = Configuration.Instance().GetCurrentUser().GetName();
User u = new User(
Configuration.Instance().SerializeToXml(stream);

The point is, unless there is some  big reason (like the ability to add new parameters at run time) you should just store data in classes. And DON'T use global variables, group data in classes that make sense (that store the data and functionality).

Merry Christmas!
Peter Davidson Send private email
Saturday, December 25, 2004
 
 
Sorry - I left off some of the example code.

User peter = new User("Peter",1,"admin");
Configuration.Instance().setUser(peter);
Configuration.Instance().setSomeOtherConfigSetting(42);
Configuration.Instance().SerializeToXml(xmlStream);

Cos you have proper classes, you can now add functions where they belong. So if you wanted to confirm that the logged in user was allowed to do a certain function then you could add this function to User and go

if(Configuration.Instance().getUser().CanDeleteDatabase()) {}

or whatever.
Peter Davidson Send private email
Saturday, December 25, 2004
 
 
Thanks for even more excellent replies, guys!!

Let me clarify a point about why I did this the way I did, and why creating a bunch of classes to encapsulate and manage the data is not too appropriate for me.
1) I know the XML serialisation is a side issue, but I still want it to to be easy. With the code I pasted, a single 'for' loop will grab all the variables and dump them with little effort. This occurs because all the data is essentialy just an array of CStrings.
2) This is simple data, text only, no funny business. In other words, making classes for all this is overkill. Keep in mind that all these little variables actually total in excess of 100, and putting them into classes would make it a huge mess for little return. In a similar thought, the code I pasted allows me to easily add and delete variables by simply creating a new 'int' and adding it's init parameters to the structures.

While the above is amateurish, I think it may simply appear so because it poorly conforms to the C++ standards we expect, which I fully agree with. But in this instance I think doing stuff the 'good' C++ way may simply be more effort for little purpose.

Saturday, December 25, 2004
 
 
I guess we have different perspectives on "mess". For me, a few well structured classes is a lot less of a mess than 100 variables all lumped in together. I can't think of many sets of 100 things that can't be categorised in some way that makes more sense that leaving them as a big list. But you're the one who has to maintain the system, so whatever works for you...

I think that the approach I suggested would be almost as easy to add to as your "add an int and initialisation". All you have to do to add a new variable is add a new get/set function (or just a new public member if that's what you like), and then add one more line to the XML serialisation stuff.

Incidentally, one of the things I really like about C# over C++ is the reflection capability - you can easily iterate over all of the properties of a class and discover their types at run-time. This makes it really easy to write general functions that can serialise any class to XML or a database, and even control this process using attributes.

Anyway, good luck with the program. I suspect that at some point you might find that doing stuff the "good C++ way" is actually a fair bit easier over the medium to long-run. And well designed classes are rarely overkill. At least, that's been my experience.
Peter Davidson Send private email
Saturday, December 25, 2004
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz