Difference between revisions of "KeyValues"

From Valve Developer Community
Jump to: navigation, search
m
(added note on saving empty values)
Line 24: Line 24:
 
==<font color="red">Important Notes</font>==
 
==<font color="red">Important Notes</font>==
 
* It is important to note that you must include filesystem.h. Not doing this will result in the inability to reliably call LoadFromFile and SaveToFile. Symptoms are crashes in datastore.dll or a fail-state.
 
* It is important to note that you must include filesystem.h. Not doing this will result in the inability to reliably call LoadFromFile and SaveToFile. Symptoms are crashes in datastore.dll or a fail-state.
 +
* SaveToFile will not save any keyvalues whose value string is empty, although these can be loaded without any problems.
 
* In <code>tier1/KeyValues.cpp</code>, uncomment this line to hit the ~CLeakTrack assert to see what's looking like it's leaking
 
* In <code>tier1/KeyValues.cpp</code>, uncomment this line to hit the ~CLeakTrack assert to see what's looking like it's leaking
 
  // #define LEAKTRACK
 
  // #define LEAKTRACK

Revision as of 12:59, 4 October 2008

Definition

The KeyValues class handles data buffer input, file input, and file output.

A KeyValue is defined recursively as a named key either with a value or children. All keys have names set to them and a search can be performed by the names. If a KeyValue is a parent key, it contains other KeyValues.

Value Types

Note:The KeyValue type gets set automatically when a set value function (i.e. SetFloat) is called.
  • TYPE_NONE</li>
  • TYPE_STRING
  • TYPE_INT
  • TYPE_FLOAT
  • TYPE_PTR
  • TYPE_WSTRING
  • TYPE_COLOR

Example

"ParentKey1"
{
	"ValueKey1"	"1"
	"ParentKey2"
	{
		...
	}
}

Important Notes

  • It is important to note that you must include filesystem.h. Not doing this will result in the inability to reliably call LoadFromFile and SaveToFile. Symptoms are crashes in datastore.dll or a fail-state.
  • SaveToFile will not save any keyvalues whose value string is empty, although these can be loaded without any problems.
  • In tier1/KeyValues.cpp, uncomment this line to hit the ~CLeakTrack assert to see what's looking like it's leaking
// #define LEAKTRACK
  • When you have completed using a KeyValues object, if you are unsure, check if is NULL and if not, call the object's deleteThis function.
  • If you, or any code utilizing the KeyValues object, fail to delete your KeyValues objects, you will end up with memory leaks.
  • Many instances of the usage of the KeyValues in the SDK do not call their deleteThis function, therefore making memory leaks.

CLeakTrack

This class is in tier1/KeyValues.cpp.

Replacing the deconstructor for the CLeakTrack class can help you stomp out KeyValues leaks when the engine shuts down.

	~CLeakTrack()
	{
		AssertMsg ( keys.Count() == 0, VarArgs("keys.Count() is %i",keys.Count()) );

		for(int x=0;x<keys.Count();x++)
		{
			keys[x].kv->deleteThis();
			keys.Remove(x);
		}
	}

When you do this in debug build, you should get told how many leaks you get when you shutdown the game. This number may vary for the amount of activity done within the game.

Although this modification can remove all leaked memory, it should not be relied on to take care of them. Some memory leaks are caused by the engine, so this is a last resort for those kinds of leaks.

Example

ComboBox *pCombo = new ComboBox(...);
pCombo->AddItem("Text", new KeyValues("UserData", "Key", "Value"));

It's important to note that if you were to follow what is called by the AddItem function, you would see that the UserData KeyValues are copied, rather than used directly. Copying KeyValues is a common source of confusion of how your KeyValues objects are used, so make sure to follow where they go. If you are unsure, you could try calling the deleteThis function and see if the same functionality as before still exists.

Fix

ComboBox *pCombo = new ComboBox(...);
KeyValues *kv = new KeyValues("UserData", "Key", "Value");
pCombo->AddItem("Text", kv);
kv->deleteThis();

What not to do

ComboBox *pCombo = new ComboBox(...);
pCombo->AddItem("Text", new KeyValues("UserData", "Key", "Value"));
Button *pButton = new Button(...);
KeyValues *kv = new KeyValues("ButtonCommand");
pButton->SetCommand(kv);
kv->deleteThis();

Useful Traverses

The following loops are contained in the following function: void Traverse(KeyValues *pKV);

All Subkeys

for ( KeyValues *sub = pKV->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
This loop steps through all subkeys contained in pKV.
To handle subkeys with their own subkeys in pKV, you may want use a recursive loop. To check if a subkey has subkeys, do the following:
if(sub->GetFirstSubKey())
{
	Traverse(sub);
}

All Values

for ( KeyValues *sub = pKV->GetFirstValue(); sub; sub = sub->GetNextValue() )
This loop steps through all subkeys contained in a pKV that have values.
It is also valid to use the KeyValues member function GetDataType in the loop to do a type specific traverse.