Sorry for the confusing title of this question, but I can’t think of an exact way to word it. I have a very long class with hundreds of similar methods and I am trying to find a way to construct it without so many lines of code and so many DRY violations. Each time I add a new property to my class I have to write two methods and update the copy function. There are already over 80 properties.
Currently this class is written in Java but I was wondering if it were written in C++ if it could be done more efficiently. I think it can be done using preprocessor x-macros like described in wikipedia here but I am not a fan of macros and would prefer to do it using the language itself if possible.
Here is a simplified snippet of my Java class:
class InMemoryPerson implements Person {
private HashMap<String,Object> values = new HashMap<String,Object>();
public InMemoryPerson copyFrom(Person p) {
setName(p.getName());
// more code to copy all the properties...
}
public void setName(String name) {
values.put("name",name);
}
public String getName() {
return (String)map.get("name");
}
// many more methods for all the different properties...
// each method may have a different type
}
16
In C++ this is possible to do, using old style C macros.
// method is get/set method suffix
// name is the key in hash table
#define POINTER_PROPERTY(type,method,name)
type get##method(){return (type)_pointer_map[name];}
void set##method(type value){_pointer_map[name]=value;}
class InMemoryPerson : Person {
public:
POINTER_PROPERTY(char *,Name,"name")
private:
std::map<const char *,void *> _pointer_map;
};
Value types would have to be handled by different macro and (easiest way) by different hash for each type. Not sure though this code is more readable and manageable than 80 different get/set functions.
1
In c++ it should be quite simple. Throw out setters and getters, and member variables, and use default copy constructors to copy objects of this class. Something like this :
struct Person
{
Person() : name("default name")
{}
std::string name;
// etc
};
Person a;
// set fields of person a
Person b;
// set fields of person b
b = a;
7
This isn’t really an answer because of the following constraint from a comment…
Because I am implementing an interface this is not possible, there are several other implementations and my class must be compatible with the interface.
However, it might be an answer for someone else, so…
One option is to provide one getter and setter for each type and to use a parameter to identify which property. I can think of two ways to handle this…
-
Use an enumerate to provide all your property names, and store those properties in an array.
-
Use a separate struct to define all the member data, possibly as a public member of your class. Store the properties using in a private instance of that struct inside your class. Pass member pointers to your getters and setters to identify the property.
Either way allows the getters and setters to handle particular properties specially if needed. The member pointer method is only really mentioned for completeness, though – I’d prefer the enumerate method.
Once you can identify which property using a parameter, you still need different getters and setters for each property datatype. This can be avoided by specifying your getter and setter as member template functions. You still need some checks, though, to ensure that the property for the current call has the correct type. This is actually easier if you used the member pointer approach (the template can require that the member type for the member pointer matches the get/set type) though you’ll probably get other problems later.
The most obvious case where something like this is justified is if you’re developing a RAD-style tool.
The need for updating the copy function can be avoided by using default copy ctors, as @BЈовић has pointed out. For getters and setters, however, I would bite the bullet and use X-macros (if you don’t want to write an external code generator for this).
Whatever you do, you need a place where you associate the hash key "name"
with the associated data type String
and the corresponding getter/setter names (the names of these methods may follow a strict convention, but AFAIK there is no easy uppercase/lowercase conversion within the preprocessor). So defining that list using X-macros will at least give you a very compact and easy-to-extend solution (EDIT: see @petrch’s post for an example).
There was a three-part series on Embedded.com about X-macros, that gives a cleaner result than what the Wikipedia article describes.
#define EXPAND_AS_ENUMERATION(a,b) a,
#define EXPAND_AS_JUMPTABLE(a,b) b,
#define STATE_TABLE(ENTRY)
ENTRY(STATE_0, func_0)
ENTRY(STATE_1, func_1)
ENTRY(STATE_2, func_2)
...
ENTRY(STATE_X, func_X)
/* declare an enumeration of state codes */
enum{
STATE_TABLE(EXPAND_AS_ENUMERATION)
NUM_STATES
}
/* declare a table of function pointers */
p_func_t jumptable[NUM_STATES] = {
STATE_TABLE(EXPAND_AS_JUMPTABLE)
}:
Here’s Part 1.
Here’s Part 2.
They blew the in-article link to Part 3. Here it is.
They require you to create a free account and profile, so they can spam you, if you want to read more than 2 articles per day. (BOO! HISS!) The 2-article limit is a fairly recent change to what used to be a really good site.