• Advertisement
Sign in to follow this  

Elegant Property Map for C++ Subclasses

This topic is 4584 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to make it possible to access certain members of a subclass through a base class function with a string argument type. For example:
BaseClass* foo = new SubClass;
int i = foo->get<int>("bar");
This seems like it would be a common enough problem that it would be easy to find information about it or even a pattern describing it formally. Perhaps it is because I don't know what to call it, but I can't learn a good way to go about doing this from Google or the archives of the board. I have created an inelegant solution to the problem, but it suffers from major flaws. I create a static map<string, int> for each subclass and initialize it with a unique integer for each property name. The base class uses to the subclass's map to get the property index and uses that to request a void* pointing to the property of the subclass instance.
#include <iostream>
#include <string>
#include <map>        // map, pair

using namespace std;

typedef map<string, int> ParamMap;
typedef pair<string, int> ParamPair;

class Resource {

	public:
		template <class T>
		void setParam(string name, T value) {
			int paramNumber = (*(getParameterMap()->find(name))).second;
			T* param = static_cast<T*>(getParameter(paramNumber));
			*param = value;
		}

		template <class T>
		T getParam(string name) {
			int paramNumber = (*(getParameterMap()->find(name))).second;
			T* param = static_cast<T*>(getParameter(paramNumber));
			return *param;
		}

	private:
		virtual ParamMap* getParameterMap() = 0;
		virtual void* getParameter(int param) = 0;

};


class ResInteger : public Resource {

	public:
		ResInteger() {
			if (parameters.empty()) {
				parameters["IntegerOne"] = INTEGER_ONE;
			}

			integerOne = 0;
		}

	protected:
		virtual ParamMap* getParameterMap() {return &parameters;}

		virtual void* getParameter(int param) {
			switch(param) {
				case INTEGER_ONE:
					return static_cast<void*>(&integerOne);
					break;
			}
		}

	private:
		static map<string, int> parameters;

		enum parameterList {
			INTEGER_ONE
		};

		int integerOne;
};

map<string, int> ResInteger::parameters;

int main() {

	Resource* resInt = new ResInteger;

	resInt->setParam<int>(string("IntegerOne"), 42);

	cout << resInt->getParam<int>(string("IntegerOne"));

	delete resInt;
	return 0;
}

The switch statement in each subclass is suboptimal. I'd prefer to have the only per subclass code to be the map initialization. This may possible by using map<string, void*> where void* points directly to an instance member. I don't know if it's possible to do that with a single (static) map per subclass. Another, simpler solution would be to implement void* getProperty(string name) for every subclass. This suffers from heavy code repetition. A major problem with using void* is type safety. In this particular case you could do something like foo->Set<int>("bar") where "bar" may map to something besides an integer. Using something like map<string, boost::any> might be an option. Any help with finding a simple and elegant solution or pointing me in the direction of a lucrative Google search would be greatly appreciated.

Share this post


Link to post
Share on other sites
Advertisement
I don't know much about boost::any, but for something like this, I would investigate further.

Another thing I would consider is why such things are required in the first place. Often when such hackish solutions are required, it's because the design is awkward. Depending on what you want to do, "type traits" might be a good search.

Share this post


Link to post
Share on other sites
What you seem to be trying to do is reflection/introspection, which C++ does not support natively. You many want to consider changing programming languages to a language that does support it such as C# (or really any of the .NET languages), Ruby or Java.

Share this post


Link to post
Share on other sites
"Reflection" seems to be exactly what I'm looking for. This is what I came up with: Metaclasses and Reflection in C++ They seem to follow a simular path, but without as many problems. Many thanks!

I need "metadata" on my resource classes to allow reconfigurable resource trees for generating procedural textures and models. At the moment I configure the trees programatically, creating and linking nodes manually and using regular constructor parameters for initialization. I'd like to be able to load a tree from an external text file or edit one with a GUI. I believe this would be the best way to lay the foundation for this.

As for switching languages: I've considered using an embedded language, but many of my resource classes are computationally intensive. For example, the Perlin and Worley basis functions.

edit: fixed bad link

[Edited by - MidoriKid on July 31, 2005 7:55:27 PM]

Share this post


Link to post
Share on other sites
You can always write the performance critical parts in C or C++ and link it with the other language. Ex: Python via extension, Java via JNI, C# via P/Invoke, etc.

As an aside, generating a class structure from a text file at runtime is very easy with Python.

Share this post


Link to post
Share on other sites
I like how you allow normal access to a member in addition to providing the attribute interface.

Quote:

(Include sample code of the Attribute map in action)


Tell me when you get around to that sample code. It would make it much easier to see how your system is used.

Share this post


Link to post
Share on other sites
Quote:
Original post by MidoriKid
Tell me when you get around to that sample code. It would make it much easier to see how your system is used.


Oh, of course; I forgot I hadn't included the demo program. I'll put one up in the next day or so, along with downloadable source.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement