Design problem relating to manipulating unknown types
I am stuck with a design problem that I hope you guys can help with. I have a few different classes that have various parameters (more than 20 in each case, and they are mostly different, although some are exactly the same in which case they inherit from a base class). I want to control these parameters through the user of a class called ObjectProperties. Each class will create ObjectProperties and populate it with it's own parameter list when the class is initialized. This is done with a map which is set up like so:
std::map<std::string, [data_type]> // where [data_type] can be an int, float, bool, string etc.
the 'string' is the name for the parameter and the data type is the type it's associated with. This is done for easy scripting later on (instead of having 20+ getters/setters). I have already made the ObjectProperties class, but it is less than ideal. I have a structure with all the possible data types called DataTypeStruct and a variable for the map in the ObjectProperties class called pDataTypeMap;
typedef struct
{
int* IntValue;
float* FloatValue;
bool* BoolValue;
std::string* StringValue;
}DataTypeStruct;
std::map<std::string, DataTypeStruct> pDataTypeMap;
When a class wants to add a parameter for manipulation, it calls one of the following functions, which then creates a new structure appropriately and pairs it with the name to be put in the map. The function returns true or false depending on whether it was able to insert it or not (if false, then the name already exists)
bool AddParam(std::string aName, int* aParam);
bool AddParam(std::string aName, float* aParam);
bool AddParam(std::string aName, bool* aParam);
bool AddParam(std::string aName, std::string* aString);
The reason aParam is/are pointers is because it is required that the parameters must be passed by reference so that they can be manipulated later on.
To change the value for the parameter I have similar functions. As you can see, this is less than ideal, as I have wasted some space with the structure (where each new structure only stores an int OR a bool OR a string OR a float), and calling overloaded functions is unsafe (开发者_如何学运维I 'can' make the functions have unique names, but again, that is less than ideal).
I hope I have explained the design issue that I have come across (it is a little difficult to explain) and would really like to hear suggestions on how to go about solving this problem.
I would suggest first taking a look at boost::any, boost:variant, boost::tuple or boost::fusion::vector as the 'vehicles' for your [data_type].
Looks like you're trying to reinvent boost::any.
It seems like it's be pretty easy to just have 4 maps instead of 1 with 4 sets of data. Then, your AddParam() methods would choose the right map based on overloading and the same for your Gets.
You have to worry about them storing multiple items with the same name but different types. You could either iterate through all 4 maps or keep a separate Set with the names of the items in the whole object.
To reduce your memory usage, use a union instead of a struct for DataTypeStruct
. Nothing else in your code would have to change (accessing members is the same as a struct). Now, your structure will only require as much memory as the largest element in it (possibly give or take a few bytes for padding/alignment).
You can also use a generic pointer in your function and cast it to the appropriate type as needed. For example:
enum data_type {INT, FLOAT, BOOL, STRING};
bool AddParam(std::string aName, void* pData, enum data_type type);
Now you have one function to handle all types. Use the value of type
to determine how to re-cast the pointer inside the function.
A suggestion here is to change DataTypeStruct
to
typedef struct
{
void* value;
int type;
//e.g.
//0 for empty,1 for int*,2 for float*,
//3 for bool*,4 for string* etc.
}DataTypeStruct;
You can then have a single AddParam
function
bool AddParam(std::string aName, void* aParam, int type);
You call the function as
int a;
float b;
bool c;
std::string d;
AddParam("test1", (void*)a, 1);
AddParam("test2", (void*)b, 2);
AddParam("test3", (void*)c, 3);
AddParam("test4", (void*)d, 4);
To get the value you can have a function look like the following
void* GetParam(std::string aName);
And used it by calling
int a_return = *((int*)GetParam("test1"));
float b_return = *((float*)GetParam("test2"));
bool c_return = *((bool*)GetParam("test3"));
std::string d_return = *((std::string*)GetParam("test3"));
Now everything look clean, extensible for new types and you save your wasted space :)
Happy programming!
精彩评论