Minimizing repetitive code in C++, a not so straightforward case - c++

I am working on a piece of code that copies a 'Person' object from one data representation to another. The names in each class (name, address, title) match, and all types are strings. For each field I want to apply the same transformation, based on some condition that also depends on the field name. The tricky part is that the repeating code uses function suffixes that are based on the field name. It looks something like this:
LibraryA::Person person1;
LibraryB::Person person2;
if (person1.name_valid() && [...somestuff...]) {
string *v = SomeOtherFunction(person.name())
person2.set_name(v);
}
if (person1.address_valid() && [...somestuff...]) {
string *v = SomeOtherFunction(person.address())
person2.set_address(v);
}
if (person1.title_valid() && [...somestuff...]) {
string *v = SomeOtherFunction(person.title())
person2.set_title(v);
}
Is there a trick (or technique :) ) to factor out the repetitive part to a template? I'd prefer a solution that does not involve defining a macro (that would be too easy :) )

This fits your requirements, but whether I would use it or not is a different question. Only if there is a huge amount of repetition I would go through this path, and then I would combine it with a macro to simplify the calling code:
void test_and_set( Person const & person1, Person & person2,
bool (Person::*test)() const,
std::string (Person::*get)() const,
void (Person::*set)( std::string const &) )
{
if ( (person1.*test)() ) {
(person2.*set)( (person1.*get)() );
}
}
Used as:
test_and_set( person1, person2, &Person::valid_name, &Person::get_name, &Person::set_name );
And combined with a local macro:
#define TEST_AND_SET( p1, p2, field ) \
test_and_set( (p1), (p2), &Person::valid_##field, &Person::get_##field, &Person::set_##field )
TEST_AND_SET( person1, person2, name );
TEST_AND_SET( person1, person2, title );
#undef TEST_AND_SET

You can use pointer to member function. For example (I didn't check if this code compiles):
typedef bool (LibraryA::Person::Validator)();
typedef string (LibraryA::Person::FieldGetter)();
typedef void (LibraryB::Person::FieldSetter)(string*);
void ApplyField(LibraryA::Person& person1, LibraryB::Person& person2, Validator vl, FieldGetter get, FieldSetter set)
{
if (person1.vl() && [...somestuff...])
{
string* v = SomeOtherFunction(person1.get());
person2.set(v);
}
}
ApplyField(person1, person2, &LibraryA::Person::name_valid, &LibraryA::Person::name, &LibraryB::Person::set_name);
ApplyField(person1, person2, &LibraryA::Person::address_valid, &LibraryA::Person::address, &LibraryB::Person::set_address);
ApplyField(person1, person2, &LibraryA::Person::title_valid, &LibraryA::Person::title, &LibraryB::Person::set_title);
I don't think templates fit here because all fields are of the same type.
And I don't really know what you have against macros in this case. You can use a macrot to generate the call to ApplyField() if you want.

You can use an array of pointer-to-member objects, fill it with the source and destination of the transformation and then apply the transformation to each entry in that array. This could look something like this:
struct trans_info {
trans_info(bool (S::*valid)() const,
std::string* (S::*get)()const,
void (T::*set)(std::string*)):
valid_(valid),
get_(get),
set_(set)
{
}
bool (S::*valid_)() const;
std::string* (S::*get_)() const;
void (S::*set_)(std::string*);
};
trans_info const info[] = {
trans_info(&S::name_valid, &S::name, &T::set_name),
trans_info(&S::address_valid, &S::address, &T::set_address),
trans_info(&S::title_valid, &S::title, &T::set_title),
...
};
template <typename T, int Size> T* begin(T (&array)[Size]) { return array; }
template <typename T, int Size> T* end(T (&array)[Size]) { return array + Size; }
transform(S const& person1, T& person2)
{
for (trans_info const* it(begin(info)), e(end(info)); it != end; ++it)
{
if ((person1.*(it->valid_))() && [...somestuff...]) {
string *v = SomeOtherFunction(person1.*(it->get_))())
(person2.*(it->set))(v);
}
}
}

Did this quick, certainly not valid C++ but I hope you get the idea:
struct MyFunctor
{
Person *person1, *person2;
void operator()(void Person::*validator(), string* Person::*getter(), void Person::*setter(string *))
{
if (person1->*validator() && [...somestuff...])
{
string* v = SomeOtherFunction(person1->*getter());
person2->*setter(v);
}
}
};
// Usage
MyFunctor f = { person1, person2 };
f(&Person::name_valid, &Person::name, &Person::set_name);
f(&Person::address_valid, &Person::address, &Person::set_address);
f(&Person::title_valid, &Person::title, &Person::set_title);

You can use pointer-to-member arguments to a template.
But I question the use of pointers to strings, that looks like a probable memory leak.

If the person objects are immutable to you, you are out of luck.
If they are not, factor the information out of the method name by using tag classes Person::Name, Person::Address and so on, and then re-writing *_valid and *_set to use function overloading:
bool Person::is_valid( Name, std::string ) {...)
bool Person::is_valid( Address, std::string ) {...)

Related

Creating a generic conversion function

I have a ResourceManager which takes in classes of type Resource. Resource is a parent class of other classes such as ShaderProgram, Texture, Mesh and even Camera who are completely unrelated to one another.
Suffice it to say, the ResourceManager works. But there is one thing that is very tedious and annoying, and that's when I retrieve the objects from the ResourceManager. Here is the problem:
In order to get an object from ResourceManager you call either of these functions:
static Resource* get(int id);
static Resource* get(const std::string &name);
The first function checks one std::unordered_map by an integer id; whereas the second function checks another std::unordered_map by the name that is manually given by the client. I have two versions of these functions for flexibility sakes because there are times where we don't care what the object contained within ResourceManager is (like Mesh) and there are times where we do care about what it is (like Camera or ShaderProgram) because we may want to retrieve the said objects by name rather than id.
Either way, both functions return a pointer to a Resource. When you call the function, it's as easy as something like:
rm::get("skyboxShader");
Where rm is just a typedef of ResourceManager since the class is static (all members/functions are static). The problem though is that the rm::get(..) function returns a Resource*, and not the child class that was added to the ResourceManager to begin with. So, in order to solve this problem I have to do a manual type conversion so that I can get ShaderProgram* instead of Resource*. I do it like this:
auto s = static_cast<ShaderProgram*>(rm::get(name));
So, everytime I want to access a Resource I have to insert the type I want to actually get into the static_cast. This is problematic insofar that everytime someone needs to access a Resource they have to type convert it. So, naturally I created a function, and being that ShaderProgram is the subject here, thus:
ShaderProgram* Renderer::program(const std::string &name)
{
auto s = static_cast<ShaderProgram*>(rm::get(name));
return s;
}
This function is static, and ResourceManager is a static class so the two go well hand-in-hand. This is a nice helper function and it works effectively and my program renders the result just fine. The problem is what I have to do when I'm dealing with other Resources; that means for every Resource that exists, there has to be a type-conversion function to accommodate it. Now THAT is annoying. Isn't there a way I can write a generic type-conversion function something like this?
auto Renderer::getResource(classTypeYouWant T, const std::string &name)
{
auto s = static_cast<T*>(rm::get(name));
return s;
}
Here, the auto keyword causes the function to derive which type it's supposed to be dealing with and return the result accordingly. My first guess is that I might have to use templates; but the problem with templates is that I can't limit which types get inserted into the function, and I really REALLY don't want floating-point id numbers, char ids, let alone custom-defined ids. It's either string (might change to const char* tbh) or ints or else.
How can I create a generic conversion function like the one described above?
Have you looked at using dynamic_cast? If the conversion fails with dynamic_cast the the pointer will be set to nullptr. So you could either write overloads for each type or you could write a template function where you pass the the type you want to convert to as well as the string or id and if the conversion succeeds or fails return true or false.
template<typename T>
bool Renderer::getResource(T*& type, const std::string &name)
{
type = dynamic_cast<decltype(std::remove_reference<decltype(T)>::type)>(rm::get(name));
if (type == nullptr)
return false;
return true;
}
OK, I did not like the idea of a typeless storage, but maybe you find that basic program as a start point. There are a lot of things which must be beautified, but some work must remain :-)
Again: It is a design failure to solve something in that way!
In addition to your example code this solution provides a minimum of safety while checking for the stored type while recall the element. But this solution needs rtti an this is not available on all platforms.
#include <map>
#include <iostream>
#include <typeinfo>
class ResourcePointerStorage
{
private:
std::map< const std::string, std::pair<void*, const std::type_info*>> storage;
public:
bool Get(const std::string& id, std::pair<void*, const std::type_info*>& ptr )
{
auto it= storage.find( id );
if ( it==storage.end() ) return false;
ptr= it->second;
return true;
}
bool Put( const std::string& id, void* ptr, const std::type_info* ti)
{
storage[id]=make_pair(ptr, ti);
}
};
template < typename T>
bool Get(ResourcePointerStorage& rm, const std::string& id, T** ptr)
{
std::pair<void*, const std::type_info*> p;
if ( rm.Get( id,p ))
{
if ( *p.second != typeid(T)) { return false; }
*ptr= static_cast<T*>(p.first);
return true;
}
else
{
return 0;
}
}
template < typename T>
void Put( ResourcePointerStorage& rm, const std::string& id, T *ptr)
{
rm.Put( id, ptr, &typeid(T) );
}
class Car
{
private:
int i;
public:
Car(int _i):i(_i){}
void Print() { std::cout << "A car " << i << std::endl; }
};
class Animal
{
private:
double d;
public:
Animal( double _d):d(_d) {}
void Show() { std::cout << "An animal " << d << std::endl; }
};
int main()
{
ResourcePointerStorage store;
Put( store, "A1", new Animal(1.1) );
Put( store, "A2", new Animal(2.2) );
Put( store, "C1", new Car(3) );
Animal *an;
Car *car;
if ( Get(store, "A1", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "A2", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "C1", &car)) { car->Print(); } else { std::cout << "Error" << std::endl; }
// not stored object
if ( Get(store, "XX", &an)) { } else { std::cout << "Expected false condition" << std::endl; }
// false type
if ( Get(store, "A1", &car)) { } else { std::cout << "Expected false condition" << std::endl; }
};
I've found the solution to my question. I created a macro:
#define convert(type, func) dynamic_cast<type>(func)
Extremely generic and code-neutral which allows types to be dynamic_casted from the return type of the function. It also allows for doing checks:
if (!convert(ShaderProgram*, rm::get("skyboxShader")))
cerr << "Conversion unsuccessful!" << endl;
else cout << "Conversion successful!" << endl;
I hope my solution will help people who search for questions similar of this kind. Thanks all!

Return pointer from type and string of name

How would I return a(n existing) pointer to a known type from a string of its name? Say I created some map<string, double> called map1 and some vector<string> called vec1. I'd like to write a function that will return map1 from "map1" and vec1 from "vec1" (and an additional argument specifying the respective type of each).
Is this possible?
My ultimate goal is to get a QWidget by its name in QString form, since I'm working with a large number of them, though an answer should be framework-independent, right?
You need to perform some kind of dynamic dispatch. To do so you can simply start with exactly what you proposed:
enum class types { A = 0, B = 1 };
void (*handlers[])(void*) = { &HandleA, &HandleB };
::std::unordered_map<::std::string, ::std::tuple<types, void*>> registry;
Now all that remains is to perform the lookup:
void lookup(::std::string const& name)
{
auto& t = registry.at(name);
handlers[static_cast<size_t>(::std::get<0>(t))](::std::get<1>(t));
}
Automagic argument casting for handlers
The handlers all take an argument of type void* - this can be dealt with by adding a little template magic:
template<typename T, void(*f)(T*)>
void handle(void* arg)
{
f(static_cast<T*>(arg));
}
void (*handlers[])(void*) = { &handle<A, &HandleA>, &handle<B, &HandleB> };
Now, the prototype is e.g. void HandleA(A*).
Simple adding of objects to registry
With the current code, you can add objects to your registry like so:
A a;
registry.emplace("A #1", ::std::make_tuple(types::A, &a));
While this works perfectly, we would like to do something a bit more elegant. Let us start by changing the enum class types to something which also knows about the type we whish to represent it:
template<typename T> struct types;
template<> struct types<A> { static const size_t id = 0; };
template<> struct types<B> { static const size_t id = 1; };
Of course, now we need to fix the registry type:
::std::unordered_map<::std::string, ::std::tuple<size_t, void*>> registry;
And finally we can provide a simple insert function:
template<typename T>
void insert(::std::string const& name, T* object)
{
registry.emplace(name, ::std::make_tuple(types<T>::id, static_cast<void*>(object)));
}
Final usage example
A a;
insert("A #1", &a);
lookup("A #1");
The meta-object system already handles this, so the answer will be framework-specific because you generally need a code generator to get metadata about the C++ types that's not otherwise available.
QLineEdit * ed = ...;
ed->setObjectName("myObject");
... elsewhere in the code
foreach(QWidget * w, QCoreApplication::allWidgets()) {
// Lookup by name
if (w->objectName() == "myObject") {
...
}
// Lookup by type
if (qobject_cast<QLineEdit*>(w)) {
...
}
}
If you want to speed up the lookup, and the objects have unique names:
class Widgets {
typedef QMap<QString, QPointer<QWidget>> Data;
mutable Data m_map;
public:
Widgets() {
foreach(QWidget * w, QCoreApplication::allWidgets()) {
if (w->objectName().isEmpty()) continue;
m_map.insert(w->objectName(), w);
}
}
QWidget * lookupWidget(const QString & name) const {
Data::iterator it = m_map.find(name);
if (it == m_map.end()) return nullptr;
QWidget * w = it->data();
if (!w) m_map.erase(it); // The widget doesn't exist anymore
return w;
}
template <typename T> T * lookup(const QString & name) const {
return qobject_cast<T*>(lookupWidget(name));
}
void setName(QWidget * w, const QString & name) {
Q_ASSERT(! name.isEmpty());
w->setObjectName(name);
m_map.insert(name, w);
}
};
In your code, use widgets->setName() instead of setObjectName.
If you want to look-up by both name and type, where duplicate names are OK as long as they are all of different types:
class Widgets2 {
typedef QPair<QString, QString> Key;
typedef QMap<Key, QPointer<QWidget>> Data;
mutable Data m_map;
static Key keyFor(QWidget * w) {
return qMakePair(w->objectName(),
QString::fromLatin1(w->metaObject()->className()));
public:
Widgets2() {
foreach(QWidget * w, QCoreApplication::allWidgets()) {
if (w->objectName().isEmpty()) continue;
m_map.insert(keyFor(w), w);
}
}
QWidget * lookupWidget(const QString & name, const QString & type) const {
Data::iterator it = m_map.find(qMakePair(name, type));
if (it == m_map.end()) return nullptr;
QWidget * w = it->data();
if (!w) m_map.erase(it); // The widget doesn't exist anymore
return w;
}
template <typename T> T * lookup(const QString & name) const
{
return qobject_cast<T*>(lookupWidget(name,
QString::fromLatin1(T::staticMetaObject.className())));
}
void setName(QWidget * w, const QString & name) {
Q_ASSERT(! name.isEmpty());
w->setObjectName(name);
m_map.insert(keyFor(w), w);
}
};
The lookup works as follows:
widgets2->lookup<QLineEdit>("myObject")->setText("foo");
I'm leveraging the QObject and QPointer to make the widget registry safe to widget deletions - you won't ever get a dangling pointer back.
It is also possible to track object name changes, if you so wish: QObject emits the objectNameChanged signal.
All of this is of course a horrible hack around a broken design of your code. The fact that you need this means that you're very tightly coupling business logic and the GUI. You should use some kind of model-view architecture.

Can this be solved with a template specialization, if not then how?

I have several "resources" in my code base. All of them are classes and share the same interface except one class, the ShaderProgram is different in just one way, it needs two strings for files names of the vertex and fragment files.
I have a template class called ResourceManager that handles all these resource except the shader one because it needs two files and the others need one, can I solve this with a template specialization? It needs to be that ResourceManager sees GetOrLoadFromFile( string, string ) and not (string) versions, while the others have the opposite, they see (string) and not (string, string). Also AttemptLoad needs the treatment too. How can I make a solution for this please include code, I have never done template specializations before.
template < class ResType > class ResourceManager
{
public:
ResourceManager(void);
~ResourceManager(void);
SmartPointer<ResType> GetOrLoadFromFile( const std::string & fileName );
//weak_ptr<ResType> GetResourceFromID( ResourceID & resID );
void DestroyResources();
void ReleaseResources();
void ReloadResources();
protected:
private:
SmartPointer<ResType> AttemptLoad( const std::string & fileName );
std::unordered_map<string, SmartPointer<ResType> > mResMap;
};
// Relevant methods ( SNIPPED )
template < class ResType> SmartPointer<ResType> ResourceManager<ResType>::GetOrLoadFromFile( const std::string & fileName )
{
if ( !mResMap.empty() )
{
auto index = mResMap.begin();
auto end = mResMap.end();
while ( index != end )
{
if ( index->first == fileName )
{
return index->second;
}
++index;
}
}
return AttemptLoad(fileName);
}
template < class ResType > SmartPointer<ResType> ResourceManager<ResType>::AttemptLoad( const std::string & fileName )
{
SmartPointer<ResType> pRes( new ResType() );
if ( pRes->LoadFromFile( fileName ) )
{
mResMap.insert( std::make_pair( fileName, pRes ) );
return pRes;
}
else
{
LogFailure("Failed to load resource file " + fileName)
return SmartPointer<ResType>(nullptr);
}
}
If both classes are under your control I would suggest a different solution. Why don't you change the AttempLoad method into something like
SmartPointer<ResType> AttemptLoad( const LoadConfiguration &p_loadConfiguration );
Where
class LoadConfiguration
{
public:
std::string FirstFileName;
};
and
class ExtendedLoadConfiguration : public LoadConfiguration
{
public:
std::string SecondFileName;
};
you could then always work with LoadConfiguration and each AttemptLoad would be able to take what he needs. Adding new arguments will be easy, it's less code with the same signature and you wouldn't have to work with template specialization.
The idea behind templates is that you known your types before the execution times, i.e., at compilation time. If this is true, than what you are trying to do is an overloading using templates. So, bellow I just put a generic code, that you can adapt to your code, that do overloading at compilation time.
Note that, to avoid writing code twice, every common methods are put in base class and let to the derived class only the ones that diverges.
#include <memory>
#include <string>
#include <iostream>
using namespace std;
class Base
{
// put common codes here
};
template <typename ResType>
class ResourceManager : public Base
{
public:
unique_ptr<ResType> GetorLoad(const string &f) { cout << f << endl; return 0;}
};
// Specilizing class ResourceManager for string type
template <>
class ResourceManager<string> : public Base
{
public:
unique_ptr<string> GetorLoad(const string &f1, const string &f2) {cout << f1 << f2 << endl; return 0;}
};
int main()
{
ResourceManager<int> i;
ResourceManager<string> s;
i.GetorLoad("int");
s.GetorLoad("string", "string");
}
PS. To compile and test this example, you need to use '--std=c++11' flag from gcc or clang++ compilers
Just implement both 'GetOrLoadFromFile' functions:
#include <string>
struct R1
{
void load (const std::string &name) {}
};
struct R2
{
void load (const std::string &name0, const std::string name1) {}
};
template<typename R>
struct M
{
R *get_or_load (const std::string &name)
{
R *p = new R();
p->load (name);
return p;
}
R *get_or_load (const std::string &name0,
const std::string &name1)
{
R *p = new R();
p->load (name0, name1);
return p;
}
};
M<R1> m1;
M<R2> m2;
int
main ()
{
R1 *p0 = m1.get_or_load ("foo");
// R1 *p1 = m2.get_or_load ("foo"); // error
R2 *q0 = m2.get_or_load ("foo", "bar");
// R2 *q1 = m1.get_or_load ("foo", "bar"); // error
}
The "wrong" member function will not be instantiated, unless actually there's a call to it, in which case the compiler will exit with diagnostics.

How can I create a switch for class members?

Say I have a class with a couple of data members, and I want a class method that returns one, and the next time it is called returns the value of the other. Something like:
class MyClass
{
public:
MyClass():switch(0){};
int get();
private:
int intA, intB;
int sw;
};
int MyClass::get()
{
if ( (++sw)%2 )
return intA;
else
return intB;
}
What would a more elegant way of doing it be? I don't like the if...else statement very much. It's fine for something like return, but if I'm actually using more complex operations, I end up duplicating a ton of code. Or having to create a second method within each method that is called after I resolve what element I'm pointing to.
What I'd prefer to do, ideally, is to use some form of pointer, so I can do
class MyClass
{
public:
MyClass():switch(&intA){};
int get();
void toggleSwitch();
private:
int intA, intB;
int * sw;
};
int MyClass::get()
{
return *sw;
}
void MyClass::toggleSwitch()
{
if ( sw == &intA )
sw = &intB;
else
sw = &intA;
}
Or something to that effect. I could call toggleSwitch(), and have my class operate on either one or the other value easily.
I still don't like it though. I prefer to avoid if's when possible, and I shouldn't need one in this case. This use of a naked pointer should be pretty safe, but I was thinking I could have something like std::unique_ptr holding each element and then std::swap them. But then the pointers would own the elements, and they'd be dynamic memory instead.
So is there a better way to do it?
Well, switch is a keyword, but I'll roll with it. How about an array of pointers?
int *fields[] = {&intA, &intB};
int MyClass::get()
{
return *fields[++switch % 2];
}
This would expand nicely if you could have additional variables later.
Or maybe:
int MyClass::get()
{
return *fields[switch = 1 - switch];
}
If you return a reference then you could use get() internally.
int &MyClass::get()
{
return *fields[switch = 1 - switch];
}
I would encapsulate the concept of a toggling value:
template<typename T>
class Toggleable {
T first;
T second;
T* current;
T* other;
public:
Toggleable(const T& first, const T& second)
: first(first),
second(second),
current(&first),
other(&second) {
}
bool toggle() {
std::swap(current, other);
}
const T& get() const {
return *current;
}
}
Then use as:
class MyClass
{
Toggleable<int> value;
public:
MyClass()
: value(42, 1729)
{
}
const int& get() {
value.toggle();
return value.get();
}
};

what is a good alternative to this ugly construct, in c++?

This is my code (simplification of a real-life problem):
class Foo {
public:
void f(const string& s) {
if (s == "lt") {
return lt();
} else if (s == "lte")
return lte();
} else if (s == "gt")
return gt();
} else if (s == "gte")
return gte();
}
}
void lt() { /* skipped */ }
void lte() { /* skipped */ }
void gt() { /* skipped */ }
void gte() { /* skipped */ }
};
This is how I would do it in PHP/Python/JavaScript/many other languages (example in PHP):
class Foo {
function f($s) {
return $this->$s();
}
function lt() { /* skipped */ }
function lte() { /* skipped */ }
function gt() { /* skipped */ }
function gte() { /* skipped */ }
}
How can I make my C++ code as elegant as this PHP example? Thanks in advance.
There is no reflection in C++. However, something like a std::map<std::string, void (Foo::*)()>should do the trick.
EDIT: Here is some ugly code to do it maintainably. Note the following :
This can probably be improved in various way
Please add code to deal with non-existent tokens. I did no error checking.
#define BEGIN_TOKEN_MAP \
template <int n> \
struct add_to_ \
{ \
static void act() {} \
}; \
std::map<std::string, void (Foo::*)()> map_;
#define DECLARE_TOKEN(str, n) \
template <> struct add_to_<n> \
{ \
static void act() { map_[#str] = &Foo::##str; add_to<n+1>::act();} \
};\
void str()
#define END_TOKEN_MAP \
void init_map() { add_to_<0>::act(); } \
void process_token(std::string s) { (this->*map_[s])(); }
class Foo
{
BEGIN_TOKEN_MAP
DECLARE_TOKEN(lt, 0) { ... }
DECLARE_TOKEN(gt, 1) { ... }
...
END_TOKEN_MAP
Foo() { init_map(); }
void f(const std::string& s) { process_token(s); }
};
You could use a dispatch table like:
typedef struct {
char *name;
void (*handler)();
} handler_t;
handler_t *handlers = {
{"lt", &lt},
{"lte", &lte},
{"gt", &gt},
{"gte", &gte},
(NULL, NULL}
};
void f(const string &s) {
for (int i=0; handlers[i].handler; ++i) {
if (0 == strcmp(s.c_str(), handlers[i].name)) {
handlers[i].handler();
return;
}
}
}
See also this SO question: How do you implement a dispatch table in your language of choice?
C++ is not dynamic, so there is no exact equivalent. A little more elegant would be to use a map and possibly function objects.
Following with the suggestion from Alexandre C., you can combine the std::map<... approach with an operator() to avoid having to call through to the void Foo::f.
For example:
class Foo {
private:
map<string,void (Foo::*)()> funs;
public:
// constructors etc.
void operator () (const string& s) {
if (funs.find (s) != funs.end ())
(this->*funs[s])();
}
// remainder
};
And you can now use foo similar to
Foo f;
f("lt"); // calls Foo::lt ()
f("lte"); // calls Foo::lte ();
// etc...
// Beware, brain-compiled code ahead!
namespace {
typedef std::map<std::string, void (Foo::*)()> operations_map_t;
typedef operations_map_t::value_type operations_entry_t;
const operations_entry_t* operations = { {"lt" , &Foo::lt }
, {"lte", &Foo::lte}
, {"gt" , &Foo::gt }
, {"gte", &Foo::gte} };
const operations_map_t operations_map( operations
, operations + sizeof(operations)
/ sizeof(operations[0]) );
}
void Foo::f(const string& s)
{
operations_map_t::const_iterator it = operations_map.find(s);
if(it == operations_map.end()) throw "Dooh!";
it->second();
}
I've upvoted Alexandre C, but I have reservations about building a data structure at run-time (populating the std::map) when the data is all known at compile-time.
I've upvoted the_void, but a linear search is only appropriate for relatively small data sets.
One option worth considering is a script (written in e.g. Python) to generate a hash-table or perfectly-balanced binary tree or whatever at build-time. You'll only do it if you have a recurring need to support large known-at-compile-time datasets, of course.
There's probably template-trickery ways to do this in C++ - they are Turing complete, and theres at least one compile-time parser state model generator, which is clearly more complex than a hash-table or binary tree. But personally, I wouldn't recommend it. A code-generating script will be simpler and more robust.
I have a script for generating ternary trees, but (1) it's a bit long for here, and (2) its not exactly a shining example of good coding.
You have several possibilities. But the first thing I should say is that C++ is strongly typed. Therefore a method that handles an instance of Foo on the one hand and Foo on the other hand is of a different type from of method that handles Foo and Bar.
Now, let's suppose that you only wish to handle Foo objects. Then you have 2 solutions:
function pointers
function objects
The function object is more general, notably, it would allow you to specify multiple combinations of parameters in one object.
class OperatorBase
{
public:
virtual ~OperatorBase() {}
bool operator()(Foo const& lhs, Foo const& rhs) const;
bool operator()(Foo const& lhs, Bar const& rhs) const;
bool operator()(Bar const& lhs, Foo const& rhs) const;
bool operator()(Bar const& lhs, Bar const& rhs) const;
private:
// virtual methods to actually implement this
};
struct LessThanOperator: OperatorBase
{
// impl
};
class OperatorFactory
{
public:
static OperatorBase& Get(std::string const& name);
template <class T>
static void Register(std::string const& name);
private:
typedef boost::ptr_map<std::string, OperatorBase> ops_t;
static ops_t& Get() { static ops_t O; return O; }
};
And then you can proceed:
// Choose the operator
OperatorBase& op = OperatorFactory::Get("lt");
Foo foo;
Bar bar;
bool const result = op(foo, bar);
It's quite tedious work though.
There are ways to do similar things in C++ with arrays and dynamic dispatch.
What you do is create an abstract class with some standard action(), like so:
class abstract_handler {
public:
virtual void action () = 0;
}
Then you create subclasses with different implementations of action(). For example, for your "ffa" branch you might write:
class ffa_handler : public abstract_handler {
public:
virtual action() {
// Do your custom "ffa" stuff in here
}
// Add your custom "ffa" members for action() to work on here.
// ...and of course a constructor to initialize them.
}
Then you create a map (in your case, indexed by std::string) of pointers to objects of each of your classes. At startup you populate this with the proper objects on the proper string indices. Then at runtime all you have to do is:
handler_map[index_string].action();