3

我得到了这个日志模板仿函数

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;
};

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

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

但是当我想将它绑定到任何非成员函数时,使用:

Logger<int, const char*, void> 

编译器抱怨: 'Class': 必须是类或命名空间时跟 '::'。因为他无法将 void 类型适合第一个 Bind 方法。但是,如果使用任何 DummyClass 创建记录器就可以了。

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

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

我知道我可能应该使用 boost::function 等,但我想确切地了解函数指针和函子是如何工作的,所以我决定不使用它。

4

4 回答 4

2

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

Logger这通过不知道它包含什么样的功能来解决您的问题;无论是非成员、类X成员还是类成员Y

您可以使用我为 C++03 开发的技术来实现这一点,该技术涉及生成包装函数(又名“thunk”)以通过编译时已知的函数指针调用成员函数和非成员函数。您可以将其视为std::functionC++11 或C# 中的Delegate的精简专用版本。

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,生成的函数能够通过成员/非成员指针内联调用,因为该指针在编译时是已知的。

编辑: 上面的示例使用 C++11 的 decltype 来自动确定函数指针的类型,但这不是必需的——我可以提供一种与 C++98 兼容的技术来实现相同的目的:

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");
}
于 2013-06-21T14:23:41.593 回答
1
#include <iostream>
#include <type_traits>

template <typename T>
struct Logger
{
    Logger(T func) : func(func)
    {
    }

    template <typename... Args>
    auto operator()(const Args&... params) -> decltype(std::declval<T>()(params...))
    {
        return func(params...);
    }

    T func;
};

void simple_logger(const char *mesg)
{
    std::cout << "Simple: " << mesg << std::endl;
}

struct ComplexLogger
{
    std::ostream& operator()(const char *mesg)
    {
        return std::cout << "Complex: " << mesg;
    }
};

int main()
{
    Logger<decltype(&simple_logger)> l1(simple_logger);
    l1("hello!");
    ComplexLogger cl; 
    Logger<ComplexLogger> l2(cl);
    l2("hello!") << "yello!";
}

输出

Simple: hello!
Complex: hello!yello!

<algorithm>在比较函数(BinaryPredicate,UnaryPredicate等)被视为模板化参数的地方看到过,这样仿函数(例如less)或函数指针都可以作为参数传递。我在这里使用了类似的技巧。

我不知道它是否与您的情况完全匹配(因为它希望日志记录类已operator()定义并且这也使用 C++11 功能)尽管我认为它可能会有所帮助。

于 2013-06-20T17:51:43.003 回答
0

您肯定需要某种类的默认实现。但好消息是,这可以通过 logger 类本身来预见。只需更换

template<typename RetType, typename Arg1Type, typename Class>

template<typename RetType, typename Arg1Type, typename Class=DefaultDummyClass>

当然 DefaultDummyClass 必须被预见,但它不再必须由想要使用记录器类的人提供。

于 2013-06-20T16:44:53.547 回答
0

我建议研究 boost::function。前段时间写了一篇文章,希望对你有帮助:

http://crazyeddiecpp.blogspot.com/2010/02/implementing-stdtr1function-pt-1.html

不过,我再也没有回到任务中,也没有讨论成员函数。不过,它可能会帮助您入门。

请注意,boost 使用不同的技术来做同样的事情。他们使用 void 函数指针而不是继承来避免使用 RTTI 创建的所有数据等等。

于 2013-06-20T16:45:29.163 回答