Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 901
yegor256 Avatar asked Jun 29 '10 12:06

yegor256


3 Answers

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); }
};
like image 107
Alexandre C. Avatar answered Nov 13 '22 12:11

Alexandre C.


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?

like image 23
the_void Avatar answered Nov 13 '22 14:11

the_void


C++ is not dynamic, so there is no exact equivalent. A little more elegant would be to use a map and possibly function objects.

like image 5
Philipp Avatar answered Nov 13 '22 13:11

Philipp