Pregunta

I have a very simple class definition as follows:

#include "../bshttp/controllers.h"
#include <iostream>
#include <string>

class DerivedController : public BS_Controllers
{
  public:
    DerivedController():BS_Controllers(this)
    {
        m_urlRules["print"] = REG_NAME &DerivedController::print;
        //regController(REG_NAME &DerivedController::print,"print");
        regController(REG_NAME &DerivedController::printView,"printView");
    }
    void * print()
    {
        return NULL;
    }
    void * printView()
    {
        cout<<"Print view!"<<endl;
        return NULL;
    }
};

where either

 m_urlRules["print"] = REG_NAME &DerivedController::print;

or

 regController(REG_NAME &DerivedController::printView,"printView");

has to be called for all of the member functions. What it does it that it takes the member function pointer of the class and maps with a string, so later on the function can be identified with a string.

Everything is all well and working, but when the class structure gets bigger, the programmer will have to repetitively call this function for every single member function. Is there anyway to use the preprocessor, or any preprocessing library such as the boost-wave, so that the programmer doesn't have to do these repetitive calling?

EDIT: Sorry for the confusion, I clearly did not describe the problem well enough here. I am mapping strings to member function pointer;

m_urlRules is a std::map with string as the key, and member function pointer as value regController is basically a setter function for m_urlRules, so both statements effectively does the same thing, which maps a string to a member function.

REG_NAME is a macro to replace a very ugly typecast.

what I am trying to do is that, if the class where to have the following structure,

class DerivedController : public BS_Controllers
{
  public:
    DerivedController():BS_Controllers(this);
    void * print();
    void * print2();
    void * print3();
    void * print4();
};

I dont have to do the following in the constructor:

 m_urlRules["print"] = REG_NAME &DerivedController::print;
 m_urlRules["print1"] = REG_NAME &DerivedController::print1;
 m_urlRules["print2"] = REG_NAME &DerivedController::print2;
 m_urlRules["print3"] = REG_NAME &DerivedController::print3;
 m_urlRules["print4"] = REG_NAME &DerivedController::print4;
¿Fue útil?

Solución

Well, you're trying to build the runtime type information (RTTI) on your own, so no there is no preprocessor macro for this. Mainly because preprocessor macros expand to a single place, and the place where you declare, and the place, where you register your functions are different.

Qt and qmake, does something like this, it finds the functions marked signals/slots, and builds a moc object for RTTI. That's about the best you can get with c++. Other languages like java, and delphi, has more RTTI, than c++, and makes it possible to query functions at runtime.

Otros consejos

I am not exactly sure I understood completely your problem, but why don't use the built-in data structure, such as map, in which you can map it to a key (your string).

Here some examples

I would first work on removing the ugly typecast (even in macro form). This can be done by moving the m_urlRules out of BS_Controllers and into an intermediate (or proxy) template class. The template is used to resolve the map to the right derived type. (I didn't know how you defined BS_Controllers, so I made one up.)

class BS_Controllers {
protected:
    virtual ~BS_Controllers () {}
public:
    virtual void * invokeRule (const std::string &) = 0;
};

template <typename D>
class BS_Proxy : public BS_Controllers {
    typedef std::map<std::string, void *(D::*)()> UrlRuleMap;

    static UrlRuleMap & urlRules () {
        static UrlRuleMap urlRules_;
        return urlRules_;
    }

    void * invokeRule (const std::string &s) {
        typename UrlRuleMap::iterator i = urlRules().find(s);
        if (i == urlRules().end()) return 0;
        return (dynamic_cast<D *>(this)->*(i->second))();
    }

protected:
    static void regController (void *(D::*m)(), const std::string &s) {
        urlRules()[s] = m;
    }
};

Now, the DerivedController can be initialized fairly easily, by invoking the regController method of the proxy class.

#define REG_RULE(D, x) BS_Proxy<D>::regController(&D::x, #x)

class DerivedController : public BS_Proxy<DerivedController> {
    struct Populate {
        Populate () {
            REG_RULE(DerivedController, print);
            REG_RULE(DerivedController, printView);
        }
    };
public:
    DerivedController() {
        static Populate populate_;
    }
    void * print() { return NULL; }
    void * printView() {
        std::cout<<"Print view!"<<std::endl;
        return NULL;
    }
};

You can view a demo of the above code.

If you want to make the population semi-automatic, you still have to define the list of methods somewhere. You could list them out in a file.

// DerivedController rules
DERIVED_RULE_INC(print)
DERIVED_RULE_INC(printView)
//...

And then change your DerivedController class to use this file:

class DerivedController : public BS_Proxy<DerivedController> {
    struct Populate {
        Populate () {
            #define DERIVED_RULE_INC(x) REG_RULE(DerivedController, x);
            #include "derived_controller_rules.inc"
            #undef DERIVED_RULE_INC
        }
    };
public:
    DerivedController() {
        static Populate populate_;
    }
    #define DERIVED_RULE_INC(x) void * x ();
    #include "derived_controller_rules.inc"
    #undef DERIVED_RULE_INC
};

void * DerivedController::print() { return NULL; }

void * DerivedController::printView() {
    std::cout<<"Print view!"<<std::endl;
    return NULL;
}

Now, if you add another rule to the file, the registration code and the method declaration is automatic. But the definition of the method needs to be implemented, or a linker error will be generated about the missing method definition.

I believe you want to use this feature for logging reasons, to see where problems appear.

I think you're searching for something like:

urlRules     ("<function name>");
regController("<function name>");

Instead of

m_urlRules["<function name>"] = REG_NAME &DerivedController::print;
regController(REG_NAME &DerivedController::printView,"<function name>");

You can define such makros like so:

#define      urlRules(x) { m_urlRules[(x)] = REG_NAME &DerivedController::print; }
#define regController(x) { regController(REG_NAME &DerivedController::printView,(x)); }

Attention: I have not tested it, it might not work but in my understanding it should.

EDIT:

Ah now I understand, you want calls for every function within the constructor. Actually, the constructor is the wrong place, because it gets called for every object you create, but you only have to assign this pointers once. (on startup for example)

See, the functions of a class only exist once in memory, and the thing that is connected to the pointer is the yield data, so all member variables.

There is no simple way to get all class members by name and then run over them, sorry. At least not as I know of.

But you should keep in mind that the function pointers won't change for any given object. An external function which does the work would be more intelligent. Called on startup.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top