如何允许模板函子在成员和非成员函数上工作 [英] How to allow templated functor work on both member and non-member functions

查看:115
本文介绍了如何允许模板函子在成员和非成员函数上工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个日志模板函数

template<typename RetType, typename Arg1Type, typename Class>

class Logger
{  public:

RetType operator()(Arg1Type s, ...)
{
    if(func != 0 && parser != 0)
        return (parser->*func)(s);
    else if(nfunc != 0)
        return nfunc(s);
    return RetType();
}

Logger& operator=(RetType(*fun)(Arg1Type s, ...))
{
    func = fun;
    return *this;
}

void Bind(Class* pars, RetType(Class::*fun)(Arg1Type s,...))
{
    parser = pars;
    func = fun;
    nfunc = 0;
}

void Bind(RetType(*fun)(Arg1Type s,...))
{
    nfunc = fun;
    func = 0;
    parser = 0;
}

private:
    RetType (Class::*func)(Arg1Type s, ...); //member class method
    RetType(*nfunc)(Arg1Type s, ...);        //non-member class method
    Class* parser;
};

现在我可以使用这样的类来调用这个类:

Now I can call this class using something like this :

Logger<int, const char*, WinLogger > p1;
WinLogger w1;
p1.Bind(&w1, &WinParser::Log);
p1("log");

但是当我想使用以下命令将它绑定到任何非成员函数:

But when I want to bind it to any non-member function using:

Logger<int, const char*, void> 

编译器提示:'Class':必须是类或命名空间, '。因为他不能将void类型适合第一个绑定方法。
但是如果创建与任何DummyClass的记录器它的确定。

the compiler complains that: 'Class': must be a class or namespace when followed by '::'. Because he cannot fit the void type to the first Bind method. But if create the logger with any DummyClass its ok.

Logger<int, const char*, DummyClass> p2;
p2.Bind(printf);
p2("printf called");

这是非常丑陋的。是否有解决方法?

Which is very ugly. Is there a workaround this?

我知道我应该使用boost :: function等,但是我想知道functon指针和函数是如何工作的,所以我决定不使用它。

I know I should probably be using boost::function etc. but I wanted to exactly learn how functon pointers and functors works so I decided to not use it.

推荐答案

可以定义一个 Logger< int,const char * 类型,它可以调用任何类的成员或非成员函数。为此,您需要从 Logger 中删除​​ Class 参数,而存储一个不透明对象指针[ void * ]和接受不透明对象的函数指针[ R(* func)(void * object,A a)

It's possible to define a single Logger<int, const char*> type, which may call either a member or a non-member function of any class. To do this, you'll need to remove the Class parameter from Logger and instead store an opaque object pointer [void*] and a function pointer that accepts an opaque object [R (*func)(void* object, A a)].

通过使 Logger 不知道它包含什么样的函数来解决问题中的问题;是否为非成员, X 的成员或 Y 的成员。

This solves the problem in your question by making Logger unaware of what kind of function it contains; whether it be a a non-member, a member of class X, or a member of class Y.

您可以使用我为C ++ 03开发的技术来实现这一点,这涉及生成包装器函数(又称thunks),通过函数指针调用成员函数和非成员函数在编译时已知。你可以把它想象成C ++ 11或 std :: function 的特殊版本/ wiki / Delegate_%28CLI%29rel =nofollow> C#中的委派

You can implement this using a technique I developed for C++03 which involves generating wrapper functions (aka 'thunks') to call the member and non-member functions through a function pointer known at compile-time. You can think of this as a cut-down specialised version of std::function in C++11 or Delegate in C#.

template<typename F>
struct FunctionTraits;

template<typename R, typename C, typename A>
struct FunctionTraits<R (C::*)(A)> // matches a pointer to member function
{
    typedef R RetType;
    typedef C Class;
    typedef A Arg1Type;
};

template<typename R, typename A>
struct FunctionTraits<R (*)(A)> // matches a pointer to function
{
    typedef R RetType;
    typedef A Arg1Type;
};

template<typename RetType, typename Arg1Type>
class Logger
{
    typedef RetType(*Func)(void*, Arg1Type);
public:

    Logger(void* pars, Func func) : pars(pars), func(func)
    {
    }

    RetType operator()(Arg1Type a) const
    {
        // call the function with the opaque object
        return func(pars, a);
    }

private:

    Func func; // a pointer to a function accepting an opaque object
    void* pars; // a pointer to an opaque object
};

template<typename F, F p>
typename FunctionTraits<F>::RetType callMember(void* c, typename FunctionTraits<F>::Arg1Type a)
{
    // restore the type of the object
    return (static_cast<typename FunctionTraits<F>::Class*>(c)->*p)(a);
}

template<typename F, F p>
Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
    makeLogger(typename FunctionTraits<F>::Class* pars)
{
    typedef typename FunctionTraits<F>::RetType RetType;
    typedef typename FunctionTraits<F>::Arg1Type Arg1Type;
    // generates a 'thunk' function which calls the member 'p'
    return Logger<RetType, Arg1Type>(pars, &callMember<F, p>);
}

template<typename F, F p>
typename FunctionTraits<F>::RetType callNonMember(void*, typename FunctionTraits<F>::Arg1Type a)
{
    // the first parameter is not used
    return (p)(a);
}

template<typename F, F p>
Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
    makeLogger()
{
    typedef typename FunctionTraits<F>::RetType RetType;
    typedef typename FunctionTraits<F>::Arg1Type Arg1Type;
    // generates a 'thunk' function which calls the non-member 'p'
    return Logger<RetType, Arg1Type>(0, &callNonMember<F, p>);
}

int log(const char*);

struct Parser
{
    int log(const char*);
};

struct OtherParser
{
    int log(const char*);
};

int main()
{
    Logger<int, const char*> nonmember = makeLogger<decltype(&log), &log>();
    int result1 = nonmember("nonmember"); // calls log("nonmember");

    Parser pars;
    Logger<int, const char*> member = makeLogger<decltype(&Parser::log), &Parser::log>(&pars);
    int result2 = member("member"); // calls pars.log("member");

    OtherParser other;
    Logger<int, const char*> member2 = makeLogger<decltype(&OtherParser::log), &OtherParser::log>(&other);
    int result3 = member2("member2"); // calls other.log("member2");
}

尽管使用 void *

std :: function 相反,生成的函数能够通过成员/非成员指针内联调用,因为指针在编译时是已知的。

In contrast with std::function, the generated functions are able to inline the calls through the member/non-member pointer because the pointer is known at compile time.

EDIT:
上面的例子使用C ++ 11的decltype来自动确定函数指针的类型,但这不是必须的 - 我可以提供一个C ++ 98兼容的技术来实现同样的功能:

The above example uses C++11's decltype to automatically determine the type of a function pointer, but this is not essential - I can offer a C++98 compatible technique that achieves the same thing:

template<typename F>
struct NonMemberHelper
{
    template<F p>
    static Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
        apply()
    {
        return makeLogger<F, p>();
    }
};

template<typename F>
NonMemberHelper<F> makeNonMemberHelper(F)
{
    return NonMemberHelper<F>();
}

template<typename F>
struct MemberHelper
{
    template<F p>
    static Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
        apply(typename FunctionTraits<F>::Class* pars)
    {
        return makeLogger<F, p>(pars);
    }
};

template<typename F>
MemberHelper<F> makeMemberHelper(F)
{
    return MemberHelper<F>();
}

#define MAKE_LOGGER_NONMEMBER(func) makeNonMemberHelper(func).apply<(func)>()
#define MAKE_LOGGER(func, pars) makeMemberHelper(func).apply<(func)>(pars)

int main()
{
    Logger<int, const char*> callNonMember = MAKE_LOGGER_NONMEMBER(&log);
    int result1 = callNonMember("nonmember"); // calls log("nonmember");

    Parser pars;
    Logger<int, const char*> callMember = MAKE_LOGGER(&Parser::log, &pars);
    int result2 = callMember("member"); // calls pars.log("member");
}

这篇关于如何允许模板函子在成员和非成员函数上工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆