简体   繁体   中英

how to pass pointer to member function of a template class?

I just faced an interesting challenge, let's solve it together:

I had a Broker class similar to this:

//Broker.h
#pragma once
#include <boost/shared_ptr.hpp>
template<class AGENT_MSG_TYPE,class BUFFER_MSG_TYPE>
class Broker
{
public:
    void messageReceiveCallback(boost::shared_ptr<ConnectionHandler>cnnHadler , std::string message){}

};

and a connection handler like this:

//ConnectionHandler.h
#pragma once
#include <boost/enable_shared_from_this.hpp>
#include <iostream>

//connection handler
#define CALL_MEMBER_FN(object, ptrToMember) ((object).*(ptrToMember))

template<class A,class B>
class Broker;

class ConnectionHandler: public boost::enable_shared_from_this<ConnectionHandler>
{
    typedef void (Broker<int,int>::*messageReceiveCallback)(boost::shared_ptr<ConnectionHandler>,std::string);
    messageReceiveCallback receiveCallBack;
    Broker<int,int> &theBroker;
public:

    ConnectionHandler(
            //...
            Broker<int,int>& broker,
            messageReceiveCallback callback
            //,...
            );
    void some_function(std::string incomingMessage);
};

///////////////////ConnectionHandler.cpp
#include "cnn.h"
#include "Broker.h"
ConnectionHandler::ConnectionHandler(
//...
        Broker<int,int>& broker, messageReceiveCallback callback
        //...
        ) :
        receiveCallBack(callback), theBroker(broker) {

}

void ConnectionHandler::some_function(std::string incomingMessage) {
    CALL_MEMBER_FN(theBroker, receiveCallBack)(shared_from_this(),incomingMessage);
}
  • As you can see one of the ConnectionHandler responsibilities is to deliver the incoming messages to the Broker by calling the broker's call back function(look at ConnectionHandler::some_function ).
  • The only way I know to call the callback function is to define a macro CALL_MEMBER_FN and pass the object,member function, and argument(s) like what you saw above.
  • so far seems so good!

BUT

The problem is that, I just have recently templetized the Broker . Consequently, I was forced to provide specific (and useless) template arguments when passing the Broker class and callback information to ConnectionHandler . Do you see the problem? In fact while trying to generalize the Broker , I had to specialize the ConnectionHandler ! ConnectionHandler, by its own, has got no other business with Broker template arguments.

I think If you could help me with a better suggestion for passing the function pointer to the ConnectionHandler without involving the Broker template arguments, it will make my day :)

thank you

I believe the two options are:

  1. Derive the Broker template from a non-template base class that acts as an interface and defines the core functions that are used by the ConnectionHandler as virtual functions. The Broker template would then override those functions. The ConnectionHandler would work with pointers to the new base class only (no more template arguments in the ConnectionHandler implementation). Drawback: Possibly slower performance as the calls into the Broker would have to go through the one extra level of dereferencing.

  2. Make the ConnectionHandler a template as well, taking the same parameters as Broker . Drawback: You will need a separate instance of ConnectionHandler for every combination of template arguments. From the code you showed, this wouldn't be a problem, though.

    Here is a brief code example that shows how the template arguments for ConnectionHandler could be derived at initialization time. It's done by implementing a function template make_connectionhandler , which takes a broker (of any type) as argument and then creates the ConnectionHandler with template arguments that match those of the Broker . This works because function templates (as opposed to class templates) can deduce their template parameters from the arguments they are given:

     /* Using C++11 syntax. */ #include <iostream> template <typename T> struct Broker { using type = T; void act(type token) const { std::cout << token << std::endl; } }; template <typename BrokerType> struct ConnectionHandler { ConnectionHandler(const BrokerType &broker) : broker_(broker) { }; void handle_request(typename BrokerType::type token) { broker_.act(token); } private: const BrokerType &broker_; }; template <typename BrokerType> ConnectionHandler<BrokerType> make_connectionhandler(const BrokerType &broker) { return { broker }; } 

    Here is a main program that shows how the make_connectionhandler function can be used:

     int main() { Broker<int> broker; auto handler = make_connectionhandler(broker); handler.handle_request(42); return 0; } 

    I've used C++11 syntax above. In C++03, you can't use auto , which unfortunately means that in the declaration of handler above, the template arguments will still appear:

     ConnectionHandler<Brokern<int> > handler = make_connectionhandler(broker); 

    Unfortunately, there is not much you can do to avoid this completely in C++03.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM