简体   繁体   中英

partial template specialization for template pointer to function

Consider the following abstract Subscription class:

template <typename TMessage>
class Subscription {
public:
    virtual ~Subscription() {}
    virtual bool handle(const TMessage &) = 0;
};

In some cases, it could be convenient if one class alone could implement this abstract class several times - even for the same TMessage - and not force inheritance.

To achieve this I am using a template pointer to function in the following way:

template <typename TMessage, typename TCaller, bool(TCaller::*TMethod)(const TMessage &)>
class Invoker : public Subscription<TMessage> {
public:
    Invoker(TCaller *caller) :
        m_caller(*caller) {
    }

    virtual bool handle(const TMessage &message) {
        return (m_caller.*TMethod)(message);
    }

protected:
    TCaller &m_caller;
};

This allows creating template-generated implementers of the abstract class like this:
(instead of implementing in several different classes and sharing objects between them):

struct MyMessage {};

class Logic {
public:
    Logic() :
        m_subscription1(new Invoker<MyMessage, Logic, &Logic::handleSubscription1>(this)),
        m_subscription2(new Invoker<MyMessage, Logic, &Logic::handleSubscription2>(this)) {
    }

    ~Logic() {
        delete m_subscription1;
        delete m_subscription2;
    }

    bool handleSubscription1(const MyMessage &message) {
        // handle message... uses class members
        return true;
    }

    bool handleSubscription2(const MyMessage &message) {
        // handle message... uses class members
        return false;
    }

private:
    Subscription<MyMessage> *m_subscription1;
    Subscription<MyMessage> *m_subscription2;
};

The thing is, that I would like to allow the user modules (ie Logic class) an implementation of a void handle function as well as a bool option.

Now, of course I could create 2 different Invoker classes - one implemented just like in the above code, and another taking a 3rd template argument of a void-returning function: void(TCaller::*TMethod)(const TMessage &) . In the handle function of the void returning function I would call the pointered function, and return true.

But I was wondering it there's a way to use the same Invoker class name so that the user won't have to match the right Invoker to the return value of his handle function. Kinda like function overloading - but for template classes.

Of course if I just created two classes with the same name (each taking different return type function template argument), the compiler would scream for redeclaration of template parameter.
So I tried to achieve this with template partial specialization but couldn't figure out a way to define the 3rd template argument without getting a compilation error - I'm not even sure it's the right way to go at it.

When I tried this:

template <typename TMessage, typename TCaller, typename TMethod>
class Invoker : public Subscription<TMessage> {
};

template <typename TMessage, typename TCaller, bool(TCaller::*TMethod)(const TMessage &)>
class Invoker<TMessage, TCaller, TMethod> {
public:
    Invoker(TCaller *caller) :
        m_caller(*caller) {
    }

    virtual bool handle(const TMessage &message) {
        return (m_caller.*TMethod)(message);
    }

protected:
    TCaller &m_caller;
};

template <typename TMessage, typename TCaller, void(TCaller::*TMethod)(const TMessage &)>
class Invoker<TMessage, TCaller, TMethod> {
public:
    Invoker(TCaller *caller) :
        m_caller(*caller) {
    }

    virtual bool handle(const TMessage &message) {
        (m_caller.*TMethod)(message);
        return true;
    }

protected:
    TCaller &m_caller;
};

I got the following compilation errors on both Invoker implementations:

error: type/value mismatch at argument 3 in template parameter list for template class Invoker
error: expected a type, got TMethod

And the following compilation errors on the Logic class (for each Invoker):

In constructor Logic::Logic():
error: type/value mismatch at argument 3 in template parameter list for template class Invoker
error: expected a type, got &Logic::handleSubscription1
error: invalid conversion from Logic* const to int
error: cannot convert int* to Subscription* in initialization

What am I doing wrong and is this possible with template partial specialization? If there's any possible solution I would like to know if it's feasible in c++11 as well as c++98.

Thanks.

You may do

template <typename TMethod, TMethod method> class Invoker;

// partial specialization for TMethod = bool(TCaller::*)(const TMessage &)
template <typename TMessage, typename TCaller, bool(TCaller::*method)(const TMessage &)>
class Invoker<bool(TCaller::*)(const TMessage &), method> :
    public Subscription<TMessage>
{
public:
    explicit Invoker(TCaller& caller) : m_caller(caller) {}

    virtual bool handle(const TMessage &message) {
        return (m_caller.*method)(message);
    }
protected:
    TCaller &m_caller;
};

// partial specialization for TMethod = void(TCaller::*)(const TMessage &)
template <typename TMessage, typename TCaller, void(TCaller::*method)(const TMessage &)>
class Invoker<void(TCaller::*)(const TMessage &), method> :
    public Subscription<TMessage>
{
public:
    explicit Invoker(TCaller& caller) : m_caller(*caller) {}

    virtual bool handle(const TMessage &message) {
        (m_caller.*method)(message);
        return true;
    }
protected:
    TCaller &m_caller;
};

And usage is something like:

class Logic {
public:
    Logic() :
        m_subscription1(std::make_unique<Invoker<decltype(&Logic::handleSubscription1), &Logic::handleSubscription1>>(*this)),
        m_subscription2(std::make_unique<Invoker<decltype(&Logic::handleSubscription2), &Logic::handleSubscription2>>(*this)) {
    }

    bool handleSubscription1(const MyMessage &message) {
        // handle message... uses class members
        return true;
    }

    void handleSubscription2(const MyMessage &message) {}

private:
    std::unique_ptr<Subscription<MyMessage>> m_subscription1;
    std::unique_ptr<Subscription<MyMessage>> m_subscription2;
};

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